Ver Fonte

更新。

scbc há 4 anos atrás
pai
commit
d8a2eaadc4

+ 1246 - 1230
ssat_sdk/MenuTree3/OptionAction.py

@@ -1,1230 +1,1246 @@
-# -*- coding:utf-8 -*-
-import os
-from enum import Enum
-from BaseLog import CBaseLog
-from ExtraData import CExtraData
-from OptionExcel import COptionExcel
-from OptionConfig import COptionConfig
-from OptionFocus import COptionFocus
-from OptionOCR import COptionOCR
-from ssat_sdk.tv_detect import *
-from ssat_sdk.device_manage.capturecard_manager import CCardManager
-from ssat_sdk.utils.string_util import strcmp
-
-g_level = ['First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth',
-           'Seventh', 'Eighth', 'Ninth', 'Tenth', 'Eleventh', 'Twelfth']
-
-
-def strSplit(text):
-    ret = []
-    str_int = ''
-    str_ch = ''
-    ch_last = ' '
-    for ch in text:
-        if 47 < ord(ch) < 58:
-            str_int += ch
-            if str_ch.__len__():
-                ret.append(str_ch)
-                str_ch = ''
-        else:
-            if 47 < ord(ch_last) < 58 and ch == '.':
-                str_int += ch
-                if str_ch.__len__():
-                    ret.append(str_ch)
-                    str_ch = ''
-            else:
-                str_ch += ch
-                if str_int.__len__():
-                    ret.append(str_int)
-                    str_int = ''
-        ch_last = ch
-    if str_ch.__len__():
-        ret.append(str_ch)
-    if str_int.__len__():
-        ret.append(str_int)
-    return ret
-
-
-# 枚举:value类型
-class VType(Enum):
-    SelectType = 0
-    InputType = 1
-    RangeType = 2
-
-
-# 注意:所有不对外暴露的变量和函数需要私有化,以明确哪些接口和参数是对外的。
-# 这样便于后期维护时,根据对外的变量和函数来做处理。
-class COptionAction(CBaseLog):
-    # ==============设备对象============== #
-    # 红老鼠遥控对象;
-    __redRat3 = TvOperator()
-    # 创建视频采集对象
-    __captureCard = CCardManager()
-    # 图片切割对象
-    __imgCMP = ImageCMP()
-
-    def __init__(self, optionName, optionValue, optionConfig, optionExcel):
-        CBaseLog.__init__(self)
-        # 层级位置;
-        self.__pos = 0
-        # 目标option;
-        self.__optionName = optionName
-        # __optionValue可空;
-        self.__optionValue = optionValue
-        self.__optionExcel = optionExcel
-        self.__optionConfig = optionConfig
-        # 焦点定位及文字识别;
-        self.__optionFocus = COptionFocus(optionConfig)
-        self.__optionOCR = COptionOCR(optionConfig, optionExcel)
-        if self.__optionExcel is None:
-            self.error(u"表格对象空")
-        # ==============常用对象数据==============;
-        self.__optionPaths = self.__optionExcel.getOptionPaths(self.__optionName)
-        # 如果__optionValue空则不取value表任何内容;
-        if self.__optionValue != "" or self.__optionValue is not None:
-            self.__optionValueInfo = self.__optionExcel.getOptionValueInfo(self.__optionName, self.__optionValue)
-        self.__optionInfo = self.__optionExcel.getOptionInfo(self.__optionName)
-        # 当前状态下的变量,与__pos对应;
-        self.__curOptionName = ''
-        self.__curOptionInfo = None
-        self.__prevOptionName = ''
-        self.__prevOptionInfo = None
-        # 到达value节点后,记录value值(一般只用于range() 数值记录,其他值无意义);
-        self.__optionValueText = ""
-        # 获取一次当前层级信息;
-        self.getCurOptionInfo()
-        # 相关返回操作;
-        self.__isEnterKeyInValue = False  # 节点在value表时,是否发送enter key.
-        self.__valueType = VType.SelectType  # 节点在value表时,value值的类型(0:选择型, 1:输入型,2:range()数值型。)
-        # 是否在Value层
-        self.__isOnValueSheet = False
-    @property
-    def pos(self):
-        return self.__pos
-
-    @property
-    def optionName(self):
-        return self.__optionName
-
-    @property
-    def optionValue(self):
-        return self.__optionValue
-
-    @property
-    def curOptionName(self):
-        return self.__curOptionName
-
-    @property
-    def curOptionInfo(self):
-        return self.__curOptionInfo
-
-    @property
-    def optionValueText(self):
-        return self.__optionValueText
-
-    @property
-    def optionValueInfo(self):
-        return self.__optionValueInfo
-
-    @property
-    def optionPaths(self):
-        return self.__optionPaths
-
-    '''
-    函数:截图并返回截图路径,如果当前option/optionValue含有layout坐标参数,则返回坐标截图;
-    参数:无
-    返回:截图路径;    
-    '''
-
-    def takePicture(self):
-        img = os.path.join(getSATTmpDIR(), "menutree_runpath.png")
-        COptionAction.__captureCard.takePicture(img)
-        if not os.path.exists(img):
-            self.error(u"截图失败:%s" % img)
-        retImg = img
-        # 判断当前处于option层还是value层
-        if self.__pos >= self.__optionPaths.__len__():
-            curInfo = self.__optionValueInfo
-        else:
-            curInfo = self.__curOptionInfo
-
-        # 判断当前层是否存在layout参数
-        if "layout" in curInfo and 'bounds' in curInfo['layout']:
-            if curInfo['layout']['bounds'] is ['']:
-                self.info("当前option未配置layout参数")
-            elif curInfo['layout']['bounds'].__len__() != 4:
-                self.info("当前option的layout坐标配置异常:%s" % str(curInfo['layout']['bounds']))
-            else:
-                self.info("当前option的layout坐标为:%s" % str(curInfo['layout']['bounds']))
-                retImg = os.path.join(getSATTmpDIR(), "menutree_runpath_layout.png")
-                COptionAction.__imgCMP.saveCropPic(img, retImg, curInfo['layout']['bounds'])
-        return retImg
-
-    '''
-    函数:调用根节点快捷键(中间节点不需要快捷键;);
-    参数:无
-    返回:无
-    '''
-
-    def callFirstOptionShortCutKey(self):
-        # 获取父节点等待时间(如果没找到,getParentWaitTime默认返回1)。
-        waitTime = self.__optionConfig.getParentWaitTime(self.__optionPaths['First']['parent'])
-        # 是否有另配置快捷按键代替根节点按键;
-        if 'shortcut_key' in self.__optionPaths['First']:
-            self.sendKey(self.__optionPaths['First']['shortcut_key'], 1, waitTime)
-        elif self.__optionPaths['First']['others'] != "":
-            others = json.loads(self.__optionPaths['First']['others'])
-            if 'first_key' in others:
-                self.sendKey(others['first_key'], 1, waitTime)
-        else:
-            self.sendKey(self.__optionPaths['First']['parent'], 1, waitTime)
-            self.warn(u"表格没有shortcut_key字段,执行默认的parent按键:%s" % self.__optionPaths['First']['parent'])
-
-    '''
-    函数:调用当前结点的toparent_key(叫back_key会简单点)
-    参数:
-        curOptionName       当前层级的目标节点.
-    返回:无
-    '''
-
-    def callCurOptionBackKey(self, curOptionName):
-        curOptionInfo = self.__optionExcel.getOptionInfo(curOptionName)
-        if 'toparent_key' in self.__optionPaths[curOptionInfo['level']]:
-            self.sendKey(self.__optionPaths[curOptionInfo['level']]['toparent_key'])
-        else:
-            self.error(u"表格没有toparent_key字段,执行默认按键return")
-            self.sendKey('return')
-
-    '''
-    函数:
-    参数:
-    返回:
-    '''
-
-    def inputUnlock(self, stdText, password=''):
-        # 如果锁住,按ok会弹出输入密码框;
-        self.sendKey("ok")
-        # 获取密码;
-        if password.__len__() == 0:
-            password = self.__optionConfig.getSuperPassword("Password", "super")
-        # 发送按键;
-        for key in list(password):
-            self.sendKey(key, 1, 0.2)
-        time.sleep(1)
-        # 发送ok键;
-        self.sendKey('ok')
-        # 判断是否成功输入密码;
-        textPic = self.takePicture()
-        # 遍历ocr类型;
-        found = False
-        ocrDict = [{"lan": "ChinesePRC+English", "type": 4}, {"lan": "ChinesePRC+English", "type": 253},
-                   {"lan": "ChinesePRC+English", "type": 10001}]
-        for item in ocrDict:
-            # 识别ocr;
-            ocrText = self.__optionOCR.getImageText(textPic, item, {})
-            ocrText = unicode(ocrText).lower()
-            self.info("lan=%s,type=%d,ocr=%s" % (item["lan"], item["type"], ocrText))
-            if ocrText in stdText or stdText == ocrText:
-                found = True
-                break
-        return not found
-
-    '''
-    函数:处理弹出密码框.
-    参数:
-    返回:Boolean, Boolean。
-        如何没有密码框处理,返回False,False。
-        如果有,且成功输入密码后密码框消失,返回True, True
-    
-    注意:对应以前的dealOthers函数。
-    '''
-
-    def dealPasswordBox(self):
-        parentOption = ''
-        if self.isOnValueSheet():
-            if self.__optionValueInfo is None or self.__optionValueInfo.__len__() == 0:
-                self.error(u"当前value[%s]信息空" % str(self.__optionValue))
-                return False, False
-
-            others = self.__optionValueInfo['others']
-            if others is None or others.__len__() == 0:
-                self.info(u"[%s]others字段空" % str(self.__curOptionName))
-                return False, False
-            # 获取父节点
-            parentOption = self.__optionValueInfo['option']
-        else:
-            getStatus, nextOptionInfo = self.getNextOptionInfo()
-            if self.__curOptionInfo is None or self.__curOptionInfo.__len__() == 0:
-                self.error(u"当前option[%s]信息空" % str(self.__curOptionName))
-                return False, False
-
-            others = self.__curOptionInfo['others']
-            if others is None or others.__len__() == 0:
-                self.info(u"[%s]others字段空" % str(self.__curOptionName))
-                return False, False
-            # 获取父节点;
-            parentOption = self.__curOptionInfo['parent']
-
-        # 转换为字典;
-        others = json.loads(others)
-        if "password" not in others:
-            self.info(u"[%s]others没有密码框处理" % str(self.__curOptionName))
-            return False, False
-
-        password = self.__optionConfig.get_value("Password", others["password"])
-        # 发送密码前,停2秒(因为像6586机芯响应很慢,密码框还没弹出就完成了密码输入的操作);
-        time.sleep(2)
-        # 发送按键;
-        for key in list(password):
-            self.sendKey(key, 1, 0.2)
-        time.sleep(1)
-        # 发送ok键;
-        if not strcmp(others["enter_key"], "default"):
-            self.sendKey(others["enter_key"])
-        # 判断是否成功输入密码;
-        current_uiPic = self.takePicture()
-        # 此处findRectByIcon参数3传递的不是first_parent,而是当前option的parent;
-        found, contourRect = self.__optionFocus.findFocusByIcon(current_uiPic, parentOption)
-        return True, not found
-
-    '''
-    函数:是否在父节点菜单上。一般在执行了callFirstOptionShortCutKey后调用;
-    参数:无
-    返回:Boolean, 数组(坐标)。 如:True, [0,0,1920,1080]
-    注意:由于所有父节点上的子项都共用一个图片定位参数,所以只要随意一个父节点的子项option即可获取定位参数;
-    测试:。
-    '''
-
-    def isOnFirstOption(self):
-        pic = self.takePicture()
-        return self.__optionFocus.findFocusByIcon(pic, self.__optionPaths['First']['option'])[0]
-
-    '''
-    函数:是否在当前节点(移动后,判断是否移动到下一目标节点)上.
-    说明:
-        每次移动到下一目标节点(option)上时,self.__pos + 1,表示移动到下一层路径。
-        当self.__pos >= self.__optionPaths.__len__()时,表示到达value表格;
-        所以,该类的重点在self.__pos的移动;
-    参数:
-        staticPic      静态图片路径。
-        主要用于将截图和聚焦、ocr识别分离,然后可以截图和聚焦中间添加其他处理。
-    返回:Boolean, 识别的文本/数字;
-    示例:
-    '''
-
-    def isOnTargetNode(self, staticPic=None):
-        # 是否在value表中;
-        isValueSheet = self.isOnValueSheet()
-        self.info(u"当前层级在:%s" % ("value表" if isValueSheet else "路径表"))
-        # 析出参数;
-        if isValueSheet:
-            curLevel = 'value'
-            curParent = self.__optionPaths[g_level[self.__pos - 1]]['parent']
-            curOption = self.__optionValueInfo['option']
-            curOthers = self.__optionValueInfo['others']
-        else:
-            curLevel = g_level[self.__pos]
-            curParent = self.__optionPaths[curLevel]['parent']
-            curOption = self.__optionPaths[curLevel]['option']
-            curOthers = self.__optionPaths[curLevel]['others']
-        self.info("当前[%s]others=[%s]" % (curOption, curOthers))
-        if curOthers.__len__() == 0:
-            curOthers = {}
-        else:
-            curOthers = json.loads(curOthers)
-        firstParent = self.__optionPaths['First']['parent']
-
-        # 获取文本识别的参数;
-        ocrConfigList = self.__optionConfig.getOptionOCRConfig(curOption)
-        ocrThreshold = self.__optionConfig.getThresholdDict(firstParent)  # 注意,此处使用firstParent;
-        # 获取当前option的ocr值/value name下所有ocr值;
-        if isValueSheet:
-            if curOption.lower() == self.__optionName.lower():
-                optionTextList = self.__optionExcel.getOptionValueText(curOption, self.__optionValue)
-            else:
-                optionTextList = self.__optionExcel.getOptionValueText(curOption)
-        else:
-            optionTextList = self.__optionExcel.getOptionText(curOption)
-        # 获取option下所有兄弟项的ocr:option字典内容;
-        siblingTextDict = self.__optionExcel.getOptionAllSiblingItemDict(curOption, not isValueSheet)
-        # 获取所有option兄弟项(包括自己)的ocr值;
-        siblingTextList = list(siblingTextDict.keys())
-
-        # 是否获取数值文本;
-        isNumberText = False
-        # 如果是value表,且兄弟项文本为range
-        # 注:value表中的option实际并没有兄弟项,取的是所有value项
-        if isValueSheet and siblingTextList.__len__():
-            if siblingTextList[0].startswith('range('):
-                self.info(u"识别的内容是value表数字内容(range(x,y)类型)")
-                isNumberText = True
-
-        # 清除之前的value值;
-        self.__optionValueText = ""
-        # 是否为静态焦点识别(动态则为跑马灯);
-        if curOthers.__len__() and 'marquee' in curOthers:
-            return self.__getDynamicPicText(curOption, optionTextList, siblingTextList, ocrConfigList,
-                                            ocrThreshold, curOthers['marquee'],
-                                            isNumberText, isValueSheet)
-        else:
-            isFocus, isTarget, text = self.__getStaticPicText(self.takePicture() if staticPic is None else staticPic,
-                                                              curOption,
-                                                              optionTextList,
-                                                              siblingTextList,
-                                                              ocrConfigList,
-                                                              ocrThreshold,
-                                                              isNumberText, isValueSheet)
-            return isTarget, text
-        # endif
-
-    '''
-    函数:是否移到目标节点上(在isOnOption后,判断__pos位置是否在__paths最后);
-    参数:无
-    返回:Boolean.
-    
-    '''
-
-    def isOnTargetOption(self):
-        return True if self.__pos == (self.__optionPaths.__len__() - 1) else False
-
-    '''
-    函数:当前节点是否在value sheet层级中;
-    参数:无
-    返回:Boolean
-    '''
-
-    def isOnValueSheet(self):
-        if self.__optionValue == "":  # 该值空,表明不会移动到value sheet中;
-            return False
-        else:
-            return self.__isOnValueSheet
-
-    '''
-    函数:移动到下一兄弟节点;
-    参数:无
-    返回:无
-    
-    注意:
-        sendKey的等待时间太短,会导致画面未响应,截图时还是截上一状态的,比如0.1秒就很经常出现这问题。
-        同时,按键等待时间,应该有所区分。
-        
-        如果不截图,可以不考虑sendKey的等待时间.
-    '''
-
-    def move2NextSiblingNode(self):
-        # 析出move key;
-        if self.isOnValueSheet():
-            valueMoveKey = self.__optionValueInfo['move_key']
-            self.sendKey(valueMoveKey[1], 1, 1)
-        else:
-            optionMoveKey = self.__curOptionInfo['option_move_key']
-            if optionMoveKey.__len__() == 0:
-                self.sendKey(self.__curOptionInfo['move_key'][1], 1, 1)
-            else:
-                self.sendKey(optionMoveKey[1], 1, 1)
-				
-    '''
-    函数:移动到上一兄弟节点
-    参数:无
-    返回:无
-    
-    注意:
-        sendKey的等待时间太短,会导致画面未响应,截图时还是截上一状态的,比如0.1秒就很经常出现这问题。
-        同时,按键等待时间,应该有所区分。
-        
-        如果不截图,可以不考虑sendKey的等待时间.
-    '''
-
-    def move2PrevSiblingNode(self):
-        # 析出move key;
-        if self.isOnValueSheet():
-            valueMoveKey = self.__optionValueInfo['move_key']
-            self.sendKey(valueMoveKey[0], 1, 1)
-        else:
-            optionMoveKey = self.__curOptionInfo['option_move_key']
-            if optionMoveKey.__len__() == 0:
-                self.sendKey(self.__curOptionInfo['move_key'][0], 1, 1)
-            else:
-                self.sendKey(optionMoveKey[0], 1, 1)
-
-    '''
-    函数:返回到父节点
-    参数:
-    返回:无
-
-    注意:
-        sendKey的等待时间太短,会导致画面未响应,截图时还是截上一状态的,比如0.1秒就很经常出现这问题。
-        同时,按键等待时间,应该有所区分。
-
-        如果不截图,可以不考虑sendKey的等待时间.
-    '''
-
-    def back2ParentNode(self):
-        # 获取当前option信息;
-        if self.isOnValueSheet():
-            tKeyDict = self.__optionValueInfo['toparent_key']
-            peKeyDict = self.__prevOptionInfo['enter_key']
-        else:
-            tKeyDict = self.__curOptionInfo['toparent_key']
-            peKeyDict = self.__prevOptionInfo['enter_key']
-
-        '''
-            说明:
-                toparent_key由于旧menutree无此参数,分以下数种情况:
-                当toparent_key没有配时:
-                    是否为value层:
-                        如果为value层,上一父层的enter_key是否为default:
-                            如果是default,则toparent_key也按default处理;
-                            如果不是default,则toparent_key按照return处理;
-                        如果不为value层:
-                            toparent_key统一按return处理。
-                如果配了toparent_key:
-                    toparent_key是否为default:
-                        如果是,按照default处理:
-                        如果不是,按照配置的key来处理。
-
-                tKeyDict中是否包含tolevel字段;
-                    如果不包含,执行完toparent_key后self.__pos自减1
-                    如果包含,执行完toparent_key后self.__pos等于tolevel指定的层级
-        '''
-        # 当toparent_key没有配时;
-        if tKeyDict == "" or tKeyDict['key'][0] == "":
-            # 是否为value层:
-            if self.isOnValueSheet():
-                # 如果为value层,上一父层的enter_key是否为default
-                if strcmp(peKeyDict["key"][0], "default"):
-                    # 如果是default,则toparent_key也按default处理;
-                    self.info("当前处于value层,未配toparent_key,且父层enter_key为default,默认toparent_key为default")
-                    tKeyDict = {'key': ["default", ]}
-                else:
-                    self.info("当前处于value层,未配toparent_key,toparent_key为return")
-                    # 如果不是default,则toparent_key按照return处理;
-                    tKeyDict = {'key': ["return", ]}
-            else:
-                self.info("当前层未配toparent_key,toparent_key为return")
-                # 如果不为value层,toparent_key统一按return处理。
-                tKeyDict = {'key': ["return", ]}
-        #  如果配了toparent_key
-        else:
-            # toparent_key是否为default
-            if strcmp(tKeyDict['key'][0], "default"):
-                # 如果是default,则toparent_key也按default处理;
-                self.info("当前层toparent_key为default")
-            else:
-                self.info("当前层toparent_key为%s" % str(tKeyDict['key']))
-        self.__executeToParentKey(tKeyDict)
-        # 从value层执行完返回后,则不在valuesheet里了
-        self.__isOnValueSheet = False
-
-    '''
-        在忽略大小写的情况下,获取到该level对应的g_level的下标值
-    '''
-
-    def __getLevelIndex(self, level):
-        s_level = []
-        for lvStr in g_level:
-            s_level.append(lvStr.lower())
-        return s_level.index(level)
-
-    '''
-    函数:进入当前节点,只对路径节点有效,value节点不处理;
-    参数:无
-    返回:无
-    '''
-
-    def enterNode(self):
-        # 析出enter key;
-        if self.isOnValueSheet():
-            if not self.__isEnterKeyInValue:
-                self.info(u"节点已在value上,且没有触发过enter key")
-                enterKey = self.__optionValueInfo['enter_key']
-                self.__executeEnterKey(enterKey)
-                self.__isEnterKeyInValue = True
-            else:
-                self.info(u"节点已在value上,已触发过enter key")
-        else:
-            # 优先使用option本身的enter_key
-            optionEnterKey = self.__curOptionInfo['option_enter_key']
-            # 如果option本身的enter_key为空,则使用该层parent的enter_key
-            if optionEnterKey['key'] == "":
-                optionEnterKey = self.__curOptionInfo['enter_key']
-            self.__executeEnterKey(optionEnterKey)
-
-    '''
-    函数:设置当前节点位置;
-    参数:
-        pos:    外部节点位置值。
-    返回:无
-    
-    注意:此函数用于外部创建两个【路径具体子集关系】的COptionAction对象,才可以使用此函数。
-        假设, a对象路径{p,p1,p2,p3,p4, v1},b = {p, p1, p2, p3, p4, p5, p6, v2}, c = {p, p2, p5, p6, v3} 
-        其中, v表示value值,不属于路径。那么,a和b具有子集关系, a与c或b与c都不具有子集关系。
-        
-        a移动到p4上,完成了v1设置后,a.back2ParentNode()后,此时如果要操作b并设置v2,就要b.SetCurPos(a.pos).
-    '''
-
-    def setCurPos(self, pos):
-        if pos < 0 or pos > self.__optionPaths.__len__():
-            self.error(u"pos值[%d]超出路径范围:[0-%d]" % (pos, self.__optionPaths.__len__()))
-            return
-        self.__pos = pos
-        # 变更后要重新读一次当前option
-        self.getCurOptionInfo()
-
-    '''
-    函数:设置目标option的值, 只设置数值型value和输入型value(选择型value不需要此步骤).
-    参数:无
-    返回:无
-    
-    注意:此函数必须是已聚焦在目标value节点上,否则无效。
-    重要:
-        在此函数enter后,UI是否返回到上一层父节点上,还是停留在本层节点不返回。
-        建议在excel中配置这个关键信息,以便此函数可以正确更改self.__pos的值。
-        
-    '''
-
-    def setOptionValue(self):
-        self.info(u"【在此函数enter后,UI是否返回到上一层父节点上,还是停留在本层节点不返回。\
-        建议在excel中配置这个关键信息,以便此函数可以正确更改self.__pos的值。】")
-        if type(self.__optionValue) == str and self.__optionValue.__len__() == 0:
-            self.error(u"[%s]的值为空,没有设置的值" % self.__optionName)
-            return
-
-        enterKey = self.__optionValueInfo['enter_key']
-        moveKey = self.__optionValueInfo['move_key']
-        valueText = self.__optionValueInfo['value_for_ocr']
-        others = self.__optionValueInfo['others']
-        if others.__len__():
-            others = json.loads(others)
-        else:
-            others = {}
-        # 是否有按键延时值;<注意:有些地方duration=0.1时,会变成长按的效果。>
-        duration = float(others['duration']) if "duration" in others else 0.2
-
-        # 值类型:
-        # 0 表示默认选择类型.
-        # 1 表示输入类型(有些输入完了,正确值会自动进入).
-        # 2 表示进度条数据型.
-        self.__valueType = VType.SelectType
-        # 是否为数字文本(特指:range(0, 100));
-        isNumberText = self.isNumberText(valueText)
-        # 数值型value;
-        if isNumberText:
-            if moveKey[0] == 'input':
-                # 标记值类型;
-                self.__valueType = VType.InputType
-                # 将数值转成字符;
-                optionValue = self.__optionValue
-                if type(optionValue) == int or type(optionValue) == float:
-                    optionValue = str(self.__optionValue)
-                # 再将字符转成list;
-                chList = list(optionValue)
-                self.sendKey(chList, 1, duration)
-            else:
-                # 标记值类型;
-                self.__valueType = VType.RangeType
-                # 相差值;
-                num = int(self.__optionValue) - int(self.__optionValueText)
-                # 正->往右或下,负->往左或上;
-                self.sendKey(moveKey[1] if num > 0 else moveKey[0], abs(num), duration)
-        elif moveKey[0] == 'input':
-            # 标记值类型;
-            self.__valueType = VType.InputType
-            # 将字符转成list;
-            chList = list(self.__optionValue)
-            self.sendKey(chList, 1, duration)
-
-        # 最后,如果有进入键执行;
-        if not strcmp(enterKey['key'][0], 'default') and enterKey['key'][0] != "":
-            self.info(u"value节点具有enter key")
-            self.__executeEnterKey(enterKey)
-
-    '''
-    函数:获取当前层级的目标option详细信息.
-    参数:无
-    返回:Boolean, 获取成功返回True
-    '''
-
-    def getCurOptionInfo(self):
-        if self.__optionPaths is None or self.__optionPaths.__len__() == 0:
-            self.error(u"paths路径空")
-            return False
-
-        if 'First' not in self.__optionPaths:
-            self.error(u"构建的paths不连续,路径存在断点")
-            self.error(u"当前paths内容:%s"%self.__optionPaths)
-            return False
-
-        if self.__pos >= self.__optionPaths.__len__():
-            # 有可能存在使用openOption打开某个option,但是下一层未到value层的情况。
-            # 此时pos会等于整个路径,但是我们要取的数据又不在value层。需要返回该option下任意一个子option的参数
-            outResult, parentData = self.__optionExcel.getParentInfo(self.__optionName)
-            if outResult:
-                for optionName in parentData['option']:
-                    outResult, outData = self.__optionExcel.getOptionInfo(optionName)
-                    if outResult:
-                        self.__curOptionInfo = outData
-                        self.info(u"当前使用openOption方式打开目标option:%s,下一层未到value层,将以其子option:%s的参数信息作为返回值"%(self.__optionName, optionName))
-                        break
-                else:
-                    self.warn(u"获取到了目标option:%s在下一层存在parent配置,但无法获取到任何子option信息!"%self.__optionName)
-                    return False
-            else:
-                # self.__curOptionInfo = None  # 是否需要在到达value层级后置空,可能保留值会有比较多用处!
-                self.warn(u"已到达value节点,无法获取路径信息")
-                self.__prevOptionName = self.__curOptionName
-                self.__prevOptionInfo = self.__curOptionInfo
-                self.__isOnValueSheet = True
-                return False
-
-        # 只有第一次或层级移动了才需要更新;
-        if self.__curOptionInfo is None or self.__pos != self.__curOptionInfo['layers']:
-            self.__curOptionName = self.__optionPaths[g_level[self.__pos]]['option']
-            outResult, outData = self.__optionExcel.getOptionInfo(self.__curOptionName, self.__optionPaths)
-            if outResult is False:
-                return False
-            self.__curOptionInfo = outData
-
-        # 当处于第二层或以后的层级时,读取前一层的数据
-        if self.__pos > 0:
-            # 是否在value层
-            if not self.isOnValueSheet():
-                self.__prevOptionName = self.__optionPaths[g_level[self.__pos - 1]]['option']
-                outResult, outData = self.__optionExcel.getOptionInfo(self.__prevOptionName, self.__optionPaths)
-                if outResult is False:
-                    return False
-                self.__prevOptionInfo = outData
-
-            # 特殊情况:如果只一层且该层与value相同(信源source,要在value表中配置一份相同配置)。
-            # if self.__curOptionInfo['first_parent'] == self.__curOptionName:
-            #     # 自动进入value表;
-            #     self.__pos += 1
-
-        return True
-
-    '''
-    函数:返回下一个option的详细信息;
-    参数:无
-    返回:Boolean、option info。
-    '''
-
-    def getNextOptionInfo(self):
-        if self.__optionPaths is None or self.__optionPaths.__len__() == 0:
-            self.error(u"paths路径空")
-            return False, None
-
-        if self.__pos + 1 >= self.__optionPaths.__len__():
-            self.warn(u"已到达value节点,无法获取路径信息")
-            return False, None
-
-        nextOptionName = self.__optionPaths[g_level[self.__pos + 1]]['option']
-        return self.__optionExcel.getOptionInfo(nextOptionName, self.__optionPaths)
-
-    '''
-    函数:获取指定位置的option信息;
-    参数:
-        pos     指定的层级位置;
-    返回:
-        成功返回option信息,失败返回None
-    '''
-
-    def getOptionInfo(self, pos):
-        if pos > self.__optionPaths.__len__() and pos < 0:
-            return None
-        outResult, outData = self.__optionExcel.getOptionInfo(self.__optionPaths[g_level[pos]]['option'],
-                                                              self.__optionPaths)
-        if outResult is False:
-            return None
-
-        return outData
-
-    '''
-    函数:指定option是否存在该路径中;
-    参数:
-        optionName  指定的option名称;
-    返回:
-        存在返回True+位置, 不存在返回False+位置;
-    '''
-
-    def isOptionInPaths(self, pos, optionNameOrValueName):
-        bExist = False
-        # 在路径表中;
-        if 0 < pos < self.__optionPaths.__len__():
-            posOptionName = self.__optionPaths[g_level[pos]]['option'].lower()
-            siblingText = self.__optionExcel.getOptionAllSiblingItemName(posOptionName)
-            for name in siblingText:
-                if name.lower() == optionNameOrValueName.lower():
-                    bExist = True
-                    break
-
-        # 在value表中;
-        if self.isOnValueSheet():
-            valueOptionName = self.__optionPaths[g_level[self.__optionPaths.__len__() - 1]]['option']
-            valueNames = self.__optionExcel.getOptionAllValueName(valueOptionName)
-            for name in valueNames:
-                if name.lower() == optionNameOrValueName.lower():
-                    bExist = True
-                    break
-
-        return bExist
-
-    '''
-    函数:检测路径是否有效;
-    参数:无
-    返回:Boolean, 检测正常返回True
-    '''
-
-    def checkRunOptionPath(self):
-        outData = self.__optionExcel.checkOptionPaths(self.__optionPaths)
-        if str(outData[1]) == 'NoExit':
-            self.error(u"表格中不存在到达Option:[%s]的路径,在表格中排查到达该Option路径" % self.__optionName)
-            return False
-
-        if str(outData[1]) == 'Fail':
-            self.error(u"表格中到达Option:[%s]的路径出现数据断层找不到First层级,在表格中排查到达该Option路径" % self.__optionName)
-            return False
-
-        return True
-
-    '''
-    函数:指定的value_for_ocr或option_for_ocr数组是否为range(xx,xx)类型。
-    参数:
-        textList        value_for_ocr或option_for_ocr数组,一般只可能会是value_for_ocr
-    返回:Boolean, 如果是range(xx,xx)类型返回True.
-    '''
-
-    def isNumberText(self, textList):
-        # 是否获取数值文本;
-        isNumberText = False
-        # 如果是value表,且兄弟项文本为range
-        # 注:value表中的option实际并没有兄弟项,取的是所有value项
-        if self.isOnValueSheet() and textList.__len__():
-            if textList[0].startswith('range('):
-                self.info(u"识别的内容是value表数字内容(range(0,xx)类型)")
-                isNumberText = True
-
-        return isNumberText
-
-    '''
-    函数:获取静态图片文本内容
-    参数:(略,请看调用函数)
-    注意:
-    返回:Boolean、Boolean、文本识别内容。
-        是否成功聚焦、是否聚焦在目标节点上、聚焦框识别的文本内容。
-    '''
-
-    def __getStaticPicText(self, pic, optionName, optionTextList, siblingTextList, ocrConfigList, ocrThreshold,
-                           isNumberText, isValueSheet, aliveKey=None):
-        # 获取图片焦点框;
-        found, focusBox = self.__optionFocus.findFocusByIcon(pic, optionName, isValueSheet)
-        if found is False:
-            self.debug(u"未找到[%s]聚集框" % optionName)
-            return False, False, None
-
-        # 如果有鲜活键;
-        self.sendAliveKey(aliveKey)
-
-        # 获取文本区域框;
-        textBox = self.__optionFocus.getFocusTextBox(optionName, focusBox, isValueSheet)
-        # 配置文本图片路径,保存文本区域截图;
-        text_pic = os.path.join(getSATTmpDIR(), "menutree_area_text.png")
-        self.__imgCMP.saveCropPic(pic, text_pic, textBox)
-        if not os.path.exists(text_pic):
-            self.error(u"%s截取文本图片失败:%s" % (optionName, text_pic))
-            return False, False, None
-
-        # 是否在某个兄弟项中;
-        isOnSibling = False
-        # 遍历所有ocr识别选项;
-        for ocrConfig in ocrConfigList:
-            # 如果有鲜活键;
-            self.sendAliveKey(aliveKey)
-            # 识别出当前聚焦文本;
-            curFocusText = self.__optionOCR.getImageText(text_pic, ocrConfig, ocrThreshold)
-            # 判断识别文本是来正常;
-            if curFocusText == "ERR<Exp>" or curFocusText.__len__() == 0:
-                continue
-
-            # 转成小写;
-            curFocusText = curFocusText.lower()
-            self.info("[%s]当前识别出的文本=%s" % (optionName, curFocusText))
-
-            # 是否取数字文本(肯定在value节点上);
-            if isNumberText is True:
-                # 特殊情况处理:某些情况下,会将包含数字以外的区域一起识别;
-                curFocusText = curFocusText.strip('>')
-                # 将数字分组
-                numberTextList = strSplit(curFocusText)
-                # 只判断最后一位是否为数字;
-                if numberTextList.__len__() < 1:
-                    self.error(u"当前识别的文本不是数字文本:%s" % curFocusText)
-                    continue
-
-                try:
-                    numberText = float(numberTextList[numberTextList.__len__() - 1])
-                    # 记录value值;
-                    self.__optionValueText = numberText
-                    return True, True, numberText
-                except Exception:
-                    continue
-            else:
-                # 当前option识别的文本与所有兄弟项文本比较;
-                for siblingText in siblingTextList:
-                    # 转为小写,保证所有比较都是小写;
-                    siblingText = siblingText.lower()
-                    # 兄弟项文本是否被包含在curFocusText中或相同;
-                    if siblingText.lower() in curFocusText.lower() or strcmp(siblingText, curFocusText):
-                        isOnSibling = True
-                        self.info(u"当前焦点在[%s], 目标焦点为[%s]" % (siblingText, optionName))
-                        self.info(u"optionTextList:%s" % optionTextList)
-                        # 再判断,该兄弟项是否为目标节点(curOption);
-                        for optionText in optionTextList:
-                            optionText = optionText.lower()
-                            # 若当前兄弟项为目标option返回True、文本;
-                            if strcmp(optionText, siblingText):
-                                return True, True, curFocusText
-                            # endif
-                        # endfor
-                        # 在兄弟项中,退出循环;
-                        break
-                    # endif
-                # endfor
-
-                if isOnSibling is False:
-                    self.error(u"未聚集到任何[%s]的兄弟项中" % optionName)
-                else:
-                    self.info("未聚集到目标节点[%s],当前文本=%s" % (optionName, curFocusText))
-                    return found, False, curFocusText
-            # endif
-        # endfor
-
-        # 默认返回;
-        return found, False, 0 if isNumberText else curFocusText
-
-    '''
-    函数:获取动态图片文本内容
-    参数:(略,请看调用函数)
-    注意:
-    返回:Boolean、文本识别内容。成功识别出文本,返回True及文本内容。
-    '''
-
-    def __getDynamicPicText(self, optionName, optionTextList, siblingTextList, ocrConfigList, ocrThreshold, marqueeDict,
-                            isNumberText, isValueSheet):
-        # 判断图片是否动态:截图两次,判断两次文本内容是否相同;
-        firstFocus, firstTarget, firstText = self.__getStaticPicText(self.takePicture(), optionName, optionTextList,
-                                                                     siblingTextList,
-                                                                     [{"lan": "ChinesePRC+English", "type": 10001}],
-                                                                     ocrThreshold, isNumberText, isValueSheet)
-        if firstFocus is False:
-            self.error(u"[%s]第一次截图未识别出聚焦框" % optionName)
-            return False, None
-        # 目标option不是跑马灯,且已经聚焦到了目标option
-        elif firstTarget is True:
-            self.info(u"已聚焦到了目标option:%s" % optionName)
-            return firstTarget, firstText
-
-        # 发送鲜活键, 保证界面鲜活;
-        self.sendAliveKey(marqueeDict['alive_key'])
-        # 第二次截图;
-        secondFocus, secondTarget, secondText = self.__getStaticPicText(self.takePicture(), optionName, optionTextList,
-                                                                        siblingTextList,
-                                                                        [{"lan": "ChinesePRC+English", "type": 10001}],
-                                                                        ocrThreshold, isNumberText, isValueSheet)
-        if secondFocus is False:
-            self.error(u"[%s]第二次截图未识别出聚焦框" % optionName)
-            return False, None
-
-        # 发送鲜活键, 保证界面鲜活;
-        self.sendAliveKey(marqueeDict['alive_key'])
-
-        # 比较两文本是否相同;
-        if firstText.__len__() and firstText == secondText:
-            self.info(u"截图两次,文本(%s)识别相同,聚焦的不是跑马灯Option" % firstText)
-            return False, firstText
-        elif firstText.__len__() == 0:
-            self.warn(u"未能识别出当前option")
-            return False, firstText
-
-        # 文本不相同,为动态图片;
-        menuList = marqueeDict['menu']
-        # 如果只有一项跑马灯,且目标option亦在跑马灯列表中,则直接返回成功结果
-        if menuList.__len__() == 1 and (optionName in menuList):
-            self.info(u"该层菜单只有一项跑马灯, 找到即成功返回")
-            return True, firstText
-
-        picList = []
-        # 如果有多项同级option都是跑马灯, 要成功识别文本需要间隔截图5次(大概会成功截图到最全的文本);
-        for i in range(0, 5):
-            picList.append(self.takePicture())
-            # 间隔多久截图;
-            time.sleep(marqueeDict['sleep_time'])
-            # 发送鲜活键;
-            self.sendAliveKey(marqueeDict['alive_key'])
-
-        ocrTextList = []
-        # 对截图进行文本识别分析;
-        for pic in picList:
-            isFocus, isTarget, text = self.__getStaticPicText(pic, optionName, optionTextList, siblingTextList,
-                                                              ocrConfigList,
-                                                              ocrThreshold, isNumberText, isValueSheet,
-                                                              marqueeDict['alive_key'])
-            if isTarget is True:
-                ocrTextList.append(text)
-            # 发送鲜活键;
-            self.sendAliveKey(marqueeDict['alive_key'])
-
-        # 过滤重复的字符;
-        ocrTextList = self.__removeDuplicateString(ocrTextList)
-        self.info(u"识别到的跑马灯ocr文字列表:%s" % ocrTextList)
-        # 获取动态文本的option字典;
-        dynamicOptionOcrDict = self.__getOptionInfoDict(menuList)
-        self.info(u"获取到的跑马灯Option对应的ocr字典:%s" % dynamicOptionOcrDict)
-        # 遍历:识别结果与xls结果进行比较;
-        for dynamicOption in dynamicOptionOcrDict:
-            dynamicOptionOcrList = dynamicOptionOcrDict[dynamicOption]
-            for dynamicOptionOcr in dynamicOptionOcrList:
-                # 只要有3张满足,判断找到option;
-                count = 0
-                for ocrText in ocrTextList:
-                    if ocrText.lower() in dynamicOptionOcr:
-                        count += 1
-
-                if count >= 3 and optionName == dynamicOption:
-                    return True, ocrText
-                else:
-                    self.info(u"当前聚焦的跑马灯实际为:%s" % dynamicOption)
-                    return False, ocrText
-            # endfor
-        # endfor
-
-        self.info("未能识别到当前聚焦的跑马灯Option")
-
-        return False, 0 if isNumberText else None
-
-    '''
-    函数:获取option名称数组内所有option的详细信息.
-    参数:
-        optionNameList      option名称数组。
-    返回:字典。
-        {
-            "option1": {self.__optionExcel.getOptionInfo(option1)[1]},
-            "option2": {self.__optionExcel.getOptionInfo(option2)[1]},
-            "option3": {self.__optionExcel.getOptionInfo(option3)[1]},
-        }
-    '''
-
-    def __getOptionInfoDict(self, optionNameList):
-        OptionInfoDict = {}
-        for optionName in optionNameList:
-            found, optionDict = self.__optionExcel.getOptionInfo(optionName)
-            if found:
-                OptionInfoDict[optionName] = optionDict
-            # endif
-        # endfor
-        return OptionInfoDict
-
-    '''
-    函数:找到两个字符串左边或者右边相同的部分
-    参数:
-        str1:
-        str2:
-        direction       方向,默认为right
-    返回:
-    '''
-
-    def __findDuplicateString(self, str1, str2, direction="right"):
-        index = 0
-        if direction == "right":
-            while True:
-                index -= 1
-                if abs(index) > str1.__len__() or abs(index) > str2.__len__():
-                    break
-                if not str1[index] == str2[index]:
-                    break
-            if index == -1:
-                self.info(u"没有找到重复文本")
-                return ""
-            return str1[index + 1:]
-        elif direction == "left":
-            while True:
-                if abs(index) >= str1.__len__() or abs(index) >= str2.__len__():
-                    break
-                if not str1[index] == str2[index]:
-                    break
-                index += 1
-            return str1[:index]
-
-    '''
-    函数:获取路径长度。
-    参数:
-    返回:
-    '''
-
-    def getPathLength(self):
-        if self.__optionPaths is None:
-            self.error(u"路径空,返回0")
-            return 0
-
-        self.info(u"路径长度:%d" % self.__optionPaths.__len__())
-        return self.__optionPaths.__len__()
-
-    '''
-    函数:去掉字符串数组中每个字符串 左边或右边相同的部分
-    参数:
-        strList
-    返回:
-    '''
-
-    def __removeDuplicateString(self, strList):
-        finishedList = strList
-        directionList = ["left", "right"]
-        for direction in directionList:
-            same_str = self.__findDuplicateString(strList[0], strList[1], direction)
-            if same_str == "":
-                continue
-            else:
-                for i in range(2, strList.__len__()):
-                    same_str = self.__findDuplicateString(same_str, strList[i], direction)
-                    if same_str == "":
-                        break
-                if same_str != "":
-                    finishedList = []
-                    for text in strList:
-                        if direction == "left":
-                            text = str[same_str.__len__():]
-                        else:
-                            text = str[:-same_str.__len__()]
-
-                        finishedList.append(text)
-                    # endfor
-                # endif
-            # endif
-        # endfor
-        return finishedList
-
-    '''
-    函数:发送红老鼠按键;
-    参数:
-        key     1、如果是字符串时,当作单个按键; 2、如果是list时,当作多个按键;
-        count   执行多少次key;
-        wait    1、执行单个key后,等待时长(因为电视机响应遥控需要时间);
-                2、执行list多个key后,每个key的等待时间;
-    返回:无
-    '''
-
-    def sendKey(self, key, count=1, duration=1.0):
-        if key is not None and key.__len__() > 0:
-            if type(key) == list:
-                for k in key:
-                    # 清除前后空格;
-                    k = k.lstrip()
-                    k = k.rstrip()
-                    COptionAction.__redRat3.sendKey(k, 1, duration)
-            else:
-                key = str(key)
-                # 清除前后空格;
-                key = key.lstrip()
-                key = key.rstrip()
-                COptionAction.__redRat3.sendKey(key, count, duration)
-        else:
-            self.error(u"error:按键内容空")
-
-    '''
-    函数:发送鲜活键;
-    参数:
-        aliveKey     鲜活按键;
-    返回:无
-
-    注意:鲜活键等待时间是0.1秒,因为不考虑截图。
-    '''
-
-    def sendAliveKey(self, aliveKey):
-        self.sendKey(aliveKey, 1, 0.1)
-
-    '''
-    函数:按照新格式执行enter_key。
-    参数:
-        eKeyDict 即enterKey的字典对象,包含的wait和duration等参数。
-
-        wait,duration是为了兼容旧menutree按照others配置参数的处理方法。
-        如果others中有配waitTime和duration,优先采用others里的配置,否则则采用enterKey里的配置。
-    '''
-    def __executeEnterKey(self, eKeyDict):
-        # 先从config读取
-        wait = self.__optionConfig.getParentWaitTime(self.__curOptionInfo['parent'])
-        # config中读取不到则尝试从others中读取
-        if wait == 1.0:
-            try:
-                others = json.loads(self.__curOptionInfo['others'])
-                if "waitTime" in others:
-                    if others['waitTime'] != "":
-                        wait = float(others['waitTime'])
-            except Exception,e:
-                pass
-        # 读出来的enter_key,不为空或者default时,按格式执行enter_key
-        if not strcmp(eKeyDict['key'][0], 'default') and eKeyDict['key'][0] != "":
-            self.__executeKey(eKeyDict, wait=wait)
-        if eKeyDict['tolevel'] != "":
-            level = eKeyDict['tolevel']
-        else:
-            level = ""
-        # value层的enter_key存在特殊字段,isback。用于判断执行enter_key以后会不会返回父层。
-        # isback为0或为""时:
-        #     则认为不返回父层;
-        # 为1时:
-        #     如果enter_key中没有配tolevel,则默认返回上一层父层;
-        #     如果有,则默认返回到tolevel指定层级的父层。
-        # 非value层,则self.__pos默认进入下一层。
-        if self.isOnValueSheet():
-            if eKeyDict['isback'] == "" or int(eKeyDict['isback']) == 0:
-                pass
-            else:
-                if level == "":
-                    self.__pos -= 1
-                else:
-                    self.__pos = self.__getLevelIndex(level) + 1
-        else:
-            self.__pos += 1
-        # 重新获取次信息;
-        self.getCurOptionInfo()
-
-    def __executeToParentKey(self, tKeyDict):
-        self.__executeKey(tKeyDict)
-        # 层级变化
-        if 'tolevel' in tKeyDict and tKeyDict['tolevel'] != "":
-            level = tKeyDict['tolevel']
-            self.__pos = self.__getLevelIndex(level) + 1
-        else:
-            self.__pos -= 1
-        # 重新获取次信息;
-        self.getCurOptionInfo()
-
-    def __executeKey(self, keyDict, wait=1.0, duration=1.0):
-        if wait != 1.0 and keyDict['wait'] != "":
-            wait = float(keyDict['wait'])
-        if duration != 1.0 and keyDict['duration'] != "":
-            duration = float(keyDict['duration'])
-        for key in keyDict['key']:
-            self.sendKey(key, 1, duration)
-        time.sleep(wait)
-
-    def executeKey(self, keyDict, wait=1.0, duration=1.0):
-        self.__executeKey(keyDict, wait, duration)
-
-    def setValue(self, optionValue):
-        self.__optionValue = optionValue
-        if self.__optionValue != "" or self.__optionValue is not None:
-            self.__optionValueInfo = self.__optionExcel.getOptionValueInfo(self.__optionName, self.__optionValue)
-
-        return True if self.__optionValueInfo and self.__optionValueInfo.__len__() else False
-
-
-if __name__ == "__main__":
-    exData = CExtraData()
-    optionExcel = COptionExcel(exData)
-    optionConfig = COptionConfig(exData, optionExcel)
-    optionAction = COptionAction('source', None, optionConfig, optionExcel)
-    # print optionAction.optionValueInfo
-    print optionAction.curOptionInfo
-    # ====================================== #
-    # optionAction.callFirstOptionShortCutKey()
+# -*- coding:utf-8 -*-
+import os
+from enum import Enum
+from BaseLog import CBaseLog
+from ExtraData import CExtraData
+from OptionExcel import COptionExcel
+from OptionConfig import COptionConfig
+from OptionFocus import COptionFocus
+from OptionOCR import COptionOCR
+from ssat_sdk.tv_detect import *
+from ssat_sdk.device_manage.capturecard_manager import CCardManager
+from ssat_sdk.utils.string_util import strcmp
+
+g_level = ['First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth',
+           'Seventh', 'Eighth', 'Ninth', 'Tenth', 'Eleventh', 'Twelfth']
+
+
+def strSplit(text):
+    ret = []
+    str_int = ''
+    str_ch = ''
+    ch_last = ' '
+    for ch in text:
+        if 47 < ord(ch) < 58:
+            str_int += ch
+            if str_ch.__len__():
+                ret.append(str_ch)
+                str_ch = ''
+        else:
+            if 47 < ord(ch_last) < 58 and ch == '.':
+                str_int += ch
+                if str_ch.__len__():
+                    ret.append(str_ch)
+                    str_ch = ''
+            else:
+                str_ch += ch
+                if str_int.__len__():
+                    ret.append(str_int)
+                    str_int = ''
+        ch_last = ch
+    if str_ch.__len__():
+        ret.append(str_ch)
+    if str_int.__len__():
+        ret.append(str_int)
+    return ret
+
+
+# 枚举:value类型
+class VType(Enum):
+    SelectType = 0
+    InputType = 1
+    RangeType = 2
+
+
+# 注意:所有不对外暴露的变量和函数需要私有化,以明确哪些接口和参数是对外的。
+# 这样便于后期维护时,根据对外的变量和函数来做处理。
+class COptionAction(CBaseLog):
+    # ==============设备对象============== #
+    # 红老鼠遥控对象;
+    __redRat3 = TvOperator()
+    # 创建视频采集对象
+    __captureCard = CCardManager()
+    # 图片切割对象
+    __imgCMP = ImageCMP()
+
+    def __init__(self, optionName, optionValue, optionConfig, optionExcel):
+        CBaseLog.__init__(self)
+        # 层级位置;
+        self.__pos = 0
+        # 目标option;
+        self.__optionName = optionName
+        # __optionValue可空;
+        self.__optionValue = optionValue
+        self.__optionExcel = optionExcel
+        self.__optionConfig = optionConfig
+        # 焦点定位及文字识别;
+        self.__optionFocus = COptionFocus(optionConfig)
+        self.__optionOCR = COptionOCR(optionConfig, optionExcel)
+        if self.__optionExcel is None:
+            self.error(u"表格对象空")
+        # ==============常用对象数据==============;
+        self.__optionPaths = self.__optionExcel.getOptionPaths(self.__optionName)
+        # 如果__optionValue空则不取value表任何内容;
+        if self.__optionValue != "" or self.__optionValue is not None:
+            self.__optionValueInfo = self.__optionExcel.getOptionValueInfo(self.__optionName, self.__optionValue)
+        self.__optionInfo = self.__optionExcel.getOptionInfo(self.__optionName)
+        # 当前状态下的变量,与__pos对应;
+        self.__curOptionName = ''
+        self.__curOptionInfo = None
+        self.__prevOptionName = ''
+        self.__prevOptionInfo = None
+        # 到达value节点后,记录value值(一般只用于range() 数值记录,其他值无意义);
+        self.__optionValueText = ""
+        # 获取一次当前层级信息;
+        self.getCurOptionInfo()
+        # 相关返回操作;
+        self.__isEnterKeyInValue = False  # 节点在value表时,是否发送enter key.
+        self.__valueType = VType.SelectType  # 节点在value表时,value值的类型(0:选择型, 1:输入型,2:range()数值型。)
+        # 是否在Value层
+        self.__isOnValueSheet = False
+    @property
+    def pos(self):
+        return self.__pos
+
+    @property
+    def optionName(self):
+        return self.__optionName
+
+    @property
+    def optionValue(self):
+        return self.__optionValue
+
+    @property
+    def curOptionName(self):
+        return self.__curOptionName
+
+    @property
+    def curOptionInfo(self):
+        return self.__curOptionInfo
+
+    @property
+    def optionValueText(self):
+        return self.__optionValueText
+
+    @property
+    def optionValueInfo(self):
+        return self.__optionValueInfo
+
+    @property
+    def optionPaths(self):
+        return self.__optionPaths
+
+    '''
+    函数:截图并返回截图路径,如果当前option/optionValue含有layout坐标参数,则返回坐标截图;
+    参数:无
+    返回:截图路径;    
+    '''
+
+    def takePicture(self):
+        img = os.path.join(getSATTmpDIR(), "menutree_runpath.png")
+        COptionAction.__captureCard.takePicture(img)
+        if not os.path.exists(img):
+            self.error(u"截图失败:%s" % img)
+        retImg = img
+        # 判断当前处于option层还是value层
+        if self.__pos >= self.__optionPaths.__len__():
+            curInfo = self.__optionValueInfo
+        else:
+            curInfo = self.__curOptionInfo
+
+        # 判断当前层是否存在layout参数
+        if "layout" in curInfo and 'bounds' in curInfo['layout']:
+            if curInfo['layout']['bounds'] is ['']:
+                self.info("当前option未配置layout参数")
+            elif curInfo['layout']['bounds'].__len__() != 4:
+                self.info("当前option的layout坐标配置异常:%s" % str(curInfo['layout']['bounds']))
+            else:
+                self.info("当前option的layout坐标为:%s" % str(curInfo['layout']['bounds']))
+                retImg = os.path.join(getSATTmpDIR(), "menutree_runpath_layout.png")
+                COptionAction.__imgCMP.saveCropPic(img, retImg, curInfo['layout']['bounds'])
+        return retImg
+
+    '''
+    函数:调用根节点快捷键(中间节点不需要快捷键;);
+    参数:无
+    返回:无
+    '''
+
+    def callFirstOptionShortCutKey(self):
+        # 获取父节点等待时间(如果没找到,getParentWaitTime默认返回1)。
+        waitTime = self.__optionConfig.getParentWaitTime(self.__optionPaths['First']['parent'])
+        # 是否有另配置快捷按键代替根节点按键;
+
+        '''
+        由于NT72561的source区分了tv信源与非tv信源,为了统一脚本,故others(first_key)新增逻辑:
+        1、优先读取option的others(first_key)信息;
+        2、options的others信息没有的情况下,读取parent的others(first_key)信息
+        '''
+        if 'shortcut_key' in self.__optionPaths['First']:
+            self.sendKey(self.__optionPaths['First']['shortcut_key'], 1, waitTime)
+        elif self.__optionPaths['First']['option_others'] != "":
+            others = json.loads(self.__optionPaths['First']['option_others'])
+            if 'first_key' in others:
+                self.info(u"option:%s包含first_key信息,将使用first_key进入。\noption first_key:%s"%(self.__optionPaths['First']['option'], others['first_key']))
+                keyList = others['first_key']
+                for key in keyList:
+                    self.sendKey(key, 1, waitTime)
+        elif self.__optionPaths['First']['others'] != "":
+            others = json.loads(self.__optionPaths['First']['others'])
+            if 'first_key' in others:
+                self.info(u"parent:%s包含first_key信息,将使用first_key进入。\nparent first_key:%s"%(self.__optionPaths['First']['parent'], others['first_key']))
+                keyList = others['first_key']
+                for key in keyList:
+                    self.sendKey(key, 1, waitTime)
+        else:
+            self.sendKey(self.__optionPaths['First']['parent'], 1, waitTime)
+            self.warn(u"表格没有shortcut_key字段,执行默认的parent按键:%s" % self.__optionPaths['First']['parent'])
+
+    '''
+    函数:调用当前结点的toparent_key(叫back_key会简单点)
+    参数:
+        curOptionName       当前层级的目标节点.
+    返回:无
+    '''
+
+    def callCurOptionBackKey(self, curOptionName):
+        curOptionInfo = self.__optionExcel.getOptionInfo(curOptionName)
+        if 'toparent_key' in self.__optionPaths[curOptionInfo['level']]:
+            self.sendKey(self.__optionPaths[curOptionInfo['level']]['toparent_key'])
+        else:
+            self.error(u"表格没有toparent_key字段,执行默认按键return")
+            self.sendKey('return')
+
+    '''
+    函数:
+    参数:
+    返回:
+    '''
+
+    def inputUnlock(self, stdText, password=''):
+        # 如果锁住,按ok会弹出输入密码框;
+        self.sendKey("ok")
+        # 获取密码;
+        if password.__len__() == 0:
+            password = self.__optionConfig.getSuperPassword("Password", "super")
+        # 发送按键;
+        for key in list(password):
+            self.sendKey(key, 1, 0.2)
+        time.sleep(1)
+        # 发送ok键;
+        self.sendKey('ok')
+        # 判断是否成功输入密码;
+        textPic = self.takePicture()
+        # 遍历ocr类型;
+        found = False
+        ocrDict = [{"lan": "ChinesePRC+English", "type": 4}, {"lan": "ChinesePRC+English", "type": 253},
+                   {"lan": "ChinesePRC+English", "type": 10001}]
+        for item in ocrDict:
+            # 识别ocr;
+            ocrText = self.__optionOCR.getImageText(textPic, item, {})
+            ocrText = unicode(ocrText).lower()
+            self.info("lan=%s,type=%d,ocr=%s" % (item["lan"], item["type"], ocrText))
+            if ocrText in stdText or stdText == ocrText:
+                found = True
+                break
+        return not found
+
+    '''
+    函数:处理弹出密码框.
+    参数:
+    返回:Boolean, Boolean。
+        如何没有密码框处理,返回False,False。
+        如果有,且成功输入密码后密码框消失,返回True, True
+    
+    注意:对应以前的dealOthers函数。
+    '''
+
+    def dealPasswordBox(self):
+        parentOption = ''
+        if self.isOnValueSheet():
+            if self.__optionValueInfo is None or self.__optionValueInfo.__len__() == 0:
+                self.error(u"当前value[%s]信息空" % str(self.__optionValue))
+                return False, False
+
+            others = self.__optionValueInfo['others']
+            if others is None or others.__len__() == 0:
+                self.info(u"[%s]others字段空" % str(self.__curOptionName))
+                return False, False
+            # 获取父节点
+            parentOption = self.__optionValueInfo['option']
+        else:
+            getStatus, nextOptionInfo = self.getNextOptionInfo()
+            if self.__curOptionInfo is None or self.__curOptionInfo.__len__() == 0:
+                self.error(u"当前option[%s]信息空" % str(self.__curOptionName))
+                return False, False
+
+            others = self.__curOptionInfo['others']
+            if others is None or others.__len__() == 0:
+                self.info(u"[%s]others字段空" % str(self.__curOptionName))
+                return False, False
+            # 获取父节点;
+            parentOption = self.__curOptionInfo['parent']
+
+        # 转换为字典;
+        others = json.loads(others)
+        if "password" not in others:
+            self.info(u"[%s]others没有密码框处理" % str(self.__curOptionName))
+            return False, False
+
+        password = self.__optionConfig.get_value("Password", others["password"])
+        # 发送密码前,停2秒(因为像6586机芯响应很慢,密码框还没弹出就完成了密码输入的操作);
+        time.sleep(2)
+        # 发送按键;
+        for key in list(password):
+            self.sendKey(key, 1, 0.2)
+        time.sleep(1)
+        # 发送ok键;
+        if not strcmp(others["enter_key"], "default"):
+            self.sendKey(others["enter_key"])
+        # 判断是否成功输入密码;
+        current_uiPic = self.takePicture()
+        # 此处findRectByIcon参数3传递的不是first_parent,而是当前option的parent;
+        found, contourRect = self.__optionFocus.findFocusByIcon(current_uiPic, parentOption)
+        return True, not found
+
+    '''
+    函数:是否在父节点菜单上。一般在执行了callFirstOptionShortCutKey后调用;
+    参数:无
+    返回:Boolean, 数组(坐标)。 如:True, [0,0,1920,1080]
+    注意:由于所有父节点上的子项都共用一个图片定位参数,所以只要随意一个父节点的子项option即可获取定位参数;
+    测试:。
+    '''
+
+    def isOnFirstOption(self):
+        pic = self.takePicture()
+        return self.__optionFocus.findFocusByIcon(pic, self.__optionPaths['First']['option'])[0]
+
+    '''
+    函数:是否在当前节点(移动后,判断是否移动到下一目标节点)上.
+    说明:
+        每次移动到下一目标节点(option)上时,self.__pos + 1,表示移动到下一层路径。
+        当self.__pos >= self.__optionPaths.__len__()时,表示到达value表格;
+        所以,该类的重点在self.__pos的移动;
+    参数:
+        staticPic      静态图片路径。
+        主要用于将截图和聚焦、ocr识别分离,然后可以截图和聚焦中间添加其他处理。
+    返回:Boolean, 识别的文本/数字;
+    示例:
+    '''
+
+    def isOnTargetNode(self, staticPic=None):
+        # 是否在value表中;
+        isValueSheet = self.isOnValueSheet()
+        self.info(u"当前层级在:%s" % ("value表" if isValueSheet else "路径表"))
+        # 析出参数;
+        if isValueSheet:
+            curLevel = 'value'
+            curParent = self.__optionPaths[g_level[self.__pos - 1]]['parent']
+            curOption = self.__optionValueInfo['option']
+            curOthers = self.__optionValueInfo['others']
+        else:
+            curLevel = g_level[self.__pos]
+            curParent = self.__optionPaths[curLevel]['parent']
+            curOption = self.__optionPaths[curLevel]['option']
+            curOthers = self.__optionPaths[curLevel]['others']
+        self.info("当前[%s]others=[%s]" % (curOption, curOthers))
+        if curOthers.__len__() == 0:
+            curOthers = {}
+        else:
+            curOthers = json.loads(curOthers)
+        firstParent = self.__optionPaths['First']['parent']
+
+        # 获取文本识别的参数;
+        ocrConfigList = self.__optionConfig.getOptionOCRConfig(curOption)
+        ocrThreshold = self.__optionConfig.getThresholdDict(firstParent)  # 注意,此处使用firstParent;
+        # 获取当前option的ocr值/value name下所有ocr值;
+        if isValueSheet:
+            if curOption.lower() == self.__optionName.lower():
+                optionTextList = self.__optionExcel.getOptionValueText(curOption, self.__optionValue)
+            else:
+                optionTextList = self.__optionExcel.getOptionValueText(curOption)
+        else:
+            optionTextList = self.__optionExcel.getOptionText(curOption)
+        # 获取option下所有兄弟项的ocr:option字典内容;
+        siblingTextDict = self.__optionExcel.getOptionAllSiblingItemDict(curOption, not isValueSheet)
+        # 获取所有option兄弟项(包括自己)的ocr值;
+        siblingTextList = list(siblingTextDict.keys())
+
+        # 是否获取数值文本;
+        isNumberText = False
+        # 如果是value表,且兄弟项文本为range
+        # 注:value表中的option实际并没有兄弟项,取的是所有value项
+        if isValueSheet and siblingTextList.__len__():
+            if siblingTextList[0].startswith('range('):
+                self.info(u"识别的内容是value表数字内容(range(x,y)类型)")
+                isNumberText = True
+
+        # 清除之前的value值;
+        self.__optionValueText = ""
+        # 是否为静态焦点识别(动态则为跑马灯);
+        if curOthers.__len__() and 'marquee' in curOthers:
+            return self.__getDynamicPicText(curOption, optionTextList, siblingTextList, ocrConfigList,
+                                            ocrThreshold, curOthers['marquee'],
+                                            isNumberText, isValueSheet)
+        else:
+            isFocus, isTarget, text = self.__getStaticPicText(self.takePicture() if staticPic is None else staticPic,
+                                                              curOption,
+                                                              optionTextList,
+                                                              siblingTextList,
+                                                              ocrConfigList,
+                                                              ocrThreshold,
+                                                              isNumberText, isValueSheet)
+            return isTarget, text
+        # endif
+
+    '''
+    函数:是否移到目标节点上(在isOnOption后,判断__pos位置是否在__paths最后);
+    参数:无
+    返回:Boolean.
+    
+    '''
+
+    def isOnTargetOption(self):
+        return True if self.__pos == (self.__optionPaths.__len__() - 1) else False
+
+    '''
+    函数:当前节点是否在value sheet层级中;
+    参数:无
+    返回:Boolean
+    '''
+
+    def isOnValueSheet(self):
+        if self.__optionValue == "":  # 该值空,表明不会移动到value sheet中;
+            return False
+        else:
+            return self.__isOnValueSheet
+
+    '''
+    函数:移动到下一兄弟节点;
+    参数:无
+    返回:无
+    
+    注意:
+        sendKey的等待时间太短,会导致画面未响应,截图时还是截上一状态的,比如0.1秒就很经常出现这问题。
+        同时,按键等待时间,应该有所区分。
+        
+        如果不截图,可以不考虑sendKey的等待时间.
+    '''
+
+    def move2NextSiblingNode(self):
+        # 析出move key;
+        if self.isOnValueSheet():
+            valueMoveKey = self.__optionValueInfo['move_key']
+            self.sendKey(valueMoveKey[1], 1, 1)
+        else:
+            optionMoveKey = self.__curOptionInfo['option_move_key']
+            if optionMoveKey.__len__() == 0:
+                self.sendKey(self.__curOptionInfo['move_key'][1], 1, 1)
+            else:
+                self.sendKey(optionMoveKey[1], 1, 1)
+				
+    '''
+    函数:移动到上一兄弟节点
+    参数:无
+    返回:无
+    
+    注意:
+        sendKey的等待时间太短,会导致画面未响应,截图时还是截上一状态的,比如0.1秒就很经常出现这问题。
+        同时,按键等待时间,应该有所区分。
+        
+        如果不截图,可以不考虑sendKey的等待时间.
+    '''
+
+    def move2PrevSiblingNode(self):
+        # 析出move key;
+        if self.isOnValueSheet():
+            valueMoveKey = self.__optionValueInfo['move_key']
+            self.sendKey(valueMoveKey[0], 1, 1)
+        else:
+            optionMoveKey = self.__curOptionInfo['option_move_key']
+            if optionMoveKey.__len__() == 0:
+                self.sendKey(self.__curOptionInfo['move_key'][0], 1, 1)
+            else:
+                self.sendKey(optionMoveKey[0], 1, 1)
+
+    '''
+    函数:返回到父节点
+    参数:
+    返回:无
+
+    注意:
+        sendKey的等待时间太短,会导致画面未响应,截图时还是截上一状态的,比如0.1秒就很经常出现这问题。
+        同时,按键等待时间,应该有所区分。
+
+        如果不截图,可以不考虑sendKey的等待时间.
+    '''
+
+    def back2ParentNode(self):
+        # 获取当前option信息;
+        if self.isOnValueSheet():
+            tKeyDict = self.__optionValueInfo['toparent_key']
+            peKeyDict = self.__prevOptionInfo['enter_key']
+        else:
+            tKeyDict = self.__curOptionInfo['toparent_key']
+            peKeyDict = self.__prevOptionInfo['enter_key']
+
+        '''
+            说明:
+                toparent_key由于旧menutree无此参数,分以下数种情况:
+                当toparent_key没有配时:
+                    是否为value层:
+                        如果为value层,上一父层的enter_key是否为default:
+                            如果是default,则toparent_key也按default处理;
+                            如果不是default,则toparent_key按照return处理;
+                        如果不为value层:
+                            toparent_key统一按return处理。
+                如果配了toparent_key:
+                    toparent_key是否为default:
+                        如果是,按照default处理:
+                        如果不是,按照配置的key来处理。
+
+                tKeyDict中是否包含tolevel字段;
+                    如果不包含,执行完toparent_key后self.__pos自减1
+                    如果包含,执行完toparent_key后self.__pos等于tolevel指定的层级
+        '''
+        # 当toparent_key没有配时;
+        if tKeyDict == "" or tKeyDict['key'][0] == "":
+            # 是否为value层:
+            if self.isOnValueSheet():
+                # 如果为value层,上一父层的enter_key是否为default
+                if strcmp(peKeyDict["key"][0], "default"):
+                    # 如果是default,则toparent_key也按default处理;
+                    self.info("当前处于value层,未配toparent_key,且父层enter_key为default,默认toparent_key为default")
+                    tKeyDict = {'key': ["default", ]}
+                else:
+                    self.info("当前处于value层,未配toparent_key,toparent_key为return")
+                    # 如果不是default,则toparent_key按照return处理;
+                    tKeyDict = {'key': ["return", ]}
+            else:
+                self.info("当前层未配toparent_key,toparent_key为return")
+                # 如果不为value层,toparent_key统一按return处理。
+                tKeyDict = {'key': ["return", ]}
+        #  如果配了toparent_key
+        else:
+            # toparent_key是否为default
+            if strcmp(tKeyDict['key'][0], "default"):
+                # 如果是default,则toparent_key也按default处理;
+                self.info("当前层toparent_key为default")
+            else:
+                self.info("当前层toparent_key为%s" % str(tKeyDict['key']))
+        self.__executeToParentKey(tKeyDict)
+        # 从value层执行完返回后,则不在valuesheet里了
+        self.__isOnValueSheet = False
+
+    '''
+        在忽略大小写的情况下,获取到该level对应的g_level的下标值
+    '''
+
+    def __getLevelIndex(self, level):
+        s_level = []
+        for lvStr in g_level:
+            s_level.append(lvStr.lower())
+        return s_level.index(level)
+
+    '''
+    函数:进入当前节点,只对路径节点有效,value节点不处理;
+    参数:无
+    返回:无
+    '''
+
+    def enterNode(self):
+        # 析出enter key;
+        if self.isOnValueSheet():
+            if not self.__isEnterKeyInValue:
+                self.info(u"节点已在value上,且没有触发过enter key")
+                enterKey = self.__optionValueInfo['enter_key']
+                self.__executeEnterKey(enterKey)
+                self.__isEnterKeyInValue = True
+            else:
+                self.info(u"节点已在value上,已触发过enter key")
+        else:
+            # 优先使用option本身的enter_key
+            optionEnterKey = self.__curOptionInfo['option_enter_key']
+            # 如果option本身的enter_key为空,则使用该层parent的enter_key
+            if optionEnterKey['key'] == "":
+                optionEnterKey = self.__curOptionInfo['enter_key']
+            self.__executeEnterKey(optionEnterKey)
+
+    '''
+    函数:设置当前节点位置;
+    参数:
+        pos:    外部节点位置值。
+    返回:无
+    
+    注意:此函数用于外部创建两个【路径具体子集关系】的COptionAction对象,才可以使用此函数。
+        假设, a对象路径{p,p1,p2,p3,p4, v1},b = {p, p1, p2, p3, p4, p5, p6, v2}, c = {p, p2, p5, p6, v3} 
+        其中, v表示value值,不属于路径。那么,a和b具有子集关系, a与c或b与c都不具有子集关系。
+        
+        a移动到p4上,完成了v1设置后,a.back2ParentNode()后,此时如果要操作b并设置v2,就要b.SetCurPos(a.pos).
+    '''
+
+    def setCurPos(self, pos):
+        if pos < 0 or pos > self.__optionPaths.__len__():
+            self.error(u"pos值[%d]超出路径范围:[0-%d]" % (pos, self.__optionPaths.__len__()))
+            return
+        self.__pos = pos
+        # 变更后要重新读一次当前option
+        self.getCurOptionInfo()
+
+    '''
+    函数:设置目标option的值, 只设置数值型value和输入型value(选择型value不需要此步骤).
+    参数:无
+    返回:无
+    
+    注意:此函数必须是已聚焦在目标value节点上,否则无效。
+    重要:
+        在此函数enter后,UI是否返回到上一层父节点上,还是停留在本层节点不返回。
+        建议在excel中配置这个关键信息,以便此函数可以正确更改self.__pos的值。
+        
+    '''
+
+    def setOptionValue(self):
+        self.info(u"【在此函数enter后,UI是否返回到上一层父节点上,还是停留在本层节点不返回。\
+        建议在excel中配置这个关键信息,以便此函数可以正确更改self.__pos的值。】")
+        if type(self.__optionValue) == str and self.__optionValue.__len__() == 0:
+            self.error(u"[%s]的值为空,没有设置的值" % self.__optionName)
+            return
+
+        enterKey = self.__optionValueInfo['enter_key']
+        moveKey = self.__optionValueInfo['move_key']
+        valueText = self.__optionValueInfo['value_for_ocr']
+        others = self.__optionValueInfo['others']
+        if others.__len__():
+            others = json.loads(others)
+        else:
+            others = {}
+        # 是否有按键延时值;<注意:有些地方duration=0.1时,会变成长按的效果。>
+        duration = float(others['duration']) if "duration" in others else 0.2
+
+        # 值类型:
+        # 0 表示默认选择类型.
+        # 1 表示输入类型(有些输入完了,正确值会自动进入).
+        # 2 表示进度条数据型.
+        self.__valueType = VType.SelectType
+        # 是否为数字文本(特指:range(0, 100));
+        isNumberText = self.isNumberText(valueText)
+        # 数值型value;
+        if isNumberText:
+            if moveKey[0] == 'input':
+                # 标记值类型;
+                self.__valueType = VType.InputType
+                # 将数值转成字符;
+                optionValue = self.__optionValue
+                if type(optionValue) == int or type(optionValue) == float:
+                    optionValue = str(self.__optionValue)
+                # 再将字符转成list;
+                chList = list(optionValue)
+                self.sendKey(chList, 1, duration)
+            else:
+                # 标记值类型;
+                self.__valueType = VType.RangeType
+                # 相差值;
+                num = int(self.__optionValue) - int(self.__optionValueText)
+                # 正->往右或下,负->往左或上;
+                self.sendKey(moveKey[1] if num > 0 else moveKey[0], abs(num), duration)
+        elif moveKey[0] == 'input':
+            # 标记值类型;
+            self.__valueType = VType.InputType
+            # 将字符转成list;
+            chList = list(self.__optionValue)
+            self.sendKey(chList, 1, duration)
+
+        # 最后,如果有进入键执行;
+        if not strcmp(enterKey['key'][0], 'default') and enterKey['key'][0] != "":
+            self.info(u"value节点具有enter key")
+            self.__executeEnterKey(enterKey)
+
+    '''
+    函数:获取当前层级的目标option详细信息.
+    参数:无
+    返回:Boolean, 获取成功返回True
+    '''
+
+    def getCurOptionInfo(self):
+        if self.__optionPaths is None or self.__optionPaths.__len__() == 0:
+            self.error(u"paths路径空")
+            return False
+
+        if 'First' not in self.__optionPaths:
+            self.error(u"构建的paths不连续,路径存在断点")
+            self.error(u"当前paths内容:%s"%self.__optionPaths)
+            return False
+
+        if self.__pos >= self.__optionPaths.__len__():
+            # 有可能存在使用openOption打开某个option,但是下一层未到value层的情况。
+            # 此时pos会等于整个路径,但是我们要取的数据又不在value层。需要返回该option下任意一个子option的参数
+            outResult, parentData = self.__optionExcel.getParentInfo(self.__optionName)
+            if outResult:
+                for optionName in parentData['option']:
+                    outResult, outData = self.__optionExcel.getOptionInfo(optionName)
+                    if outResult:
+                        self.__curOptionInfo = outData
+                        self.info(u"当前使用openOption方式打开目标option:%s,下一层未到value层,将以其子option:%s的参数信息作为返回值"%(self.__optionName, optionName))
+                        break
+                else:
+                    self.warn(u"获取到了目标option:%s在下一层存在parent配置,但无法获取到任何子option信息!"%self.__optionName)
+                    return False
+            else:
+                # self.__curOptionInfo = None  # 是否需要在到达value层级后置空,可能保留值会有比较多用处!
+                self.warn(u"已到达value节点,无法获取路径信息")
+                self.__prevOptionName = self.__curOptionName
+                self.__prevOptionInfo = self.__curOptionInfo
+                self.__isOnValueSheet = True
+                return False
+
+        # 只有第一次或层级移动了才需要更新;
+        if self.__curOptionInfo is None or self.__pos != self.__curOptionInfo['layers']:
+            self.__curOptionName = self.__optionPaths[g_level[self.__pos]]['option']
+            outResult, outData = self.__optionExcel.getOptionInfo(self.__curOptionName, self.__optionPaths)
+            if outResult is False:
+                return False
+            self.__curOptionInfo = outData
+
+        # 当处于第二层或以后的层级时,读取前一层的数据
+        if self.__pos > 0:
+            # 是否在value层
+            if not self.isOnValueSheet():
+                self.__prevOptionName = self.__optionPaths[g_level[self.__pos - 1]]['option']
+                outResult, outData = self.__optionExcel.getOptionInfo(self.__prevOptionName, self.__optionPaths)
+                if outResult is False:
+                    return False
+                self.__prevOptionInfo = outData
+
+            # 特殊情况:如果只一层且该层与value相同(信源source,要在value表中配置一份相同配置)。
+            # if self.__curOptionInfo['first_parent'] == self.__curOptionName:
+            #     # 自动进入value表;
+            #     self.__pos += 1
+
+        return True
+
+    '''
+    函数:返回下一个option的详细信息;
+    参数:无
+    返回:Boolean、option info。
+    '''
+
+    def getNextOptionInfo(self):
+        if self.__optionPaths is None or self.__optionPaths.__len__() == 0:
+            self.error(u"paths路径空")
+            return False, None
+
+        if self.__pos + 1 >= self.__optionPaths.__len__():
+            self.warn(u"已到达value节点,无法获取路径信息")
+            return False, None
+
+        nextOptionName = self.__optionPaths[g_level[self.__pos + 1]]['option']
+        return self.__optionExcel.getOptionInfo(nextOptionName, self.__optionPaths)
+
+    '''
+    函数:获取指定位置的option信息;
+    参数:
+        pos     指定的层级位置;
+    返回:
+        成功返回option信息,失败返回None
+    '''
+
+    def getOptionInfo(self, pos):
+        if pos > self.__optionPaths.__len__() and pos < 0:
+            return None
+        outResult, outData = self.__optionExcel.getOptionInfo(self.__optionPaths[g_level[pos]]['option'],
+                                                              self.__optionPaths)
+        if outResult is False:
+            return None
+
+        return outData
+
+    '''
+    函数:指定option是否存在该路径中;
+    参数:
+        optionName  指定的option名称;
+    返回:
+        存在返回True+位置, 不存在返回False+位置;
+    '''
+
+    def isOptionInPaths(self, pos, optionNameOrValueName):
+        bExist = False
+        # 在路径表中;
+        if 0 < pos < self.__optionPaths.__len__():
+            posOptionName = self.__optionPaths[g_level[pos]]['option'].lower()
+            siblingText = self.__optionExcel.getOptionAllSiblingItemName(posOptionName)
+            for name in siblingText:
+                if name.lower() == optionNameOrValueName.lower():
+                    bExist = True
+                    break
+
+        # 在value表中;
+        if self.isOnValueSheet():
+            valueOptionName = self.__optionPaths[g_level[self.__optionPaths.__len__() - 1]]['option']
+            valueNames = self.__optionExcel.getOptionAllValueName(valueOptionName)
+            for name in valueNames:
+                if name.lower() == optionNameOrValueName.lower():
+                    bExist = True
+                    break
+
+        return bExist
+
+    '''
+    函数:检测路径是否有效;
+    参数:无
+    返回:Boolean, 检测正常返回True
+    '''
+
+    def checkRunOptionPath(self):
+        outData = self.__optionExcel.checkOptionPaths(self.__optionPaths)
+        if str(outData[1]) == 'NoExit':
+            self.error(u"表格中不存在到达Option:[%s]的路径,在表格中排查到达该Option路径" % self.__optionName)
+            return False
+
+        if str(outData[1]) == 'Fail':
+            self.error(u"表格中到达Option:[%s]的路径出现数据断层找不到First层级,在表格中排查到达该Option路径" % self.__optionName)
+            return False
+
+        return True
+
+    '''
+    函数:指定的value_for_ocr或option_for_ocr数组是否为range(xx,xx)类型。
+    参数:
+        textList        value_for_ocr或option_for_ocr数组,一般只可能会是value_for_ocr
+    返回:Boolean, 如果是range(xx,xx)类型返回True.
+    '''
+
+    def isNumberText(self, textList):
+        # 是否获取数值文本;
+        isNumberText = False
+        # 如果是value表,且兄弟项文本为range
+        # 注:value表中的option实际并没有兄弟项,取的是所有value项
+        if self.isOnValueSheet() and textList.__len__():
+            if textList[0].startswith('range('):
+                self.info(u"识别的内容是value表数字内容(range(0,xx)类型)")
+                isNumberText = True
+
+        return isNumberText
+
+    '''
+    函数:获取静态图片文本内容
+    参数:(略,请看调用函数)
+    注意:
+    返回:Boolean、Boolean、文本识别内容。
+        是否成功聚焦、是否聚焦在目标节点上、聚焦框识别的文本内容。
+    '''
+
+    def __getStaticPicText(self, pic, optionName, optionTextList, siblingTextList, ocrConfigList, ocrThreshold,
+                           isNumberText, isValueSheet, aliveKey=None):
+        # 获取图片焦点框;
+        found, focusBox = self.__optionFocus.findFocusByIcon(pic, optionName, isValueSheet)
+        if found is False:
+            self.debug(u"未找到[%s]聚集框" % optionName)
+            return False, False, None
+
+        # 如果有鲜活键;
+        self.sendAliveKey(aliveKey)
+
+        # 获取文本区域框;
+        textBox = self.__optionFocus.getFocusTextBox(optionName, focusBox, isValueSheet)
+        # 配置文本图片路径,保存文本区域截图;
+        text_pic = os.path.join(getSATTmpDIR(), "menutree_area_text.png")
+        self.__imgCMP.saveCropPic(pic, text_pic, textBox)
+        if not os.path.exists(text_pic):
+            self.error(u"%s截取文本图片失败:%s" % (optionName, text_pic))
+            return False, False, None
+
+        # 是否在某个兄弟项中;
+        isOnSibling = False
+        # 遍历所有ocr识别选项;
+        for ocrConfig in ocrConfigList:
+            # 如果有鲜活键;
+            self.sendAliveKey(aliveKey)
+            # 识别出当前聚焦文本;
+            curFocusText = self.__optionOCR.getImageText(text_pic, ocrConfig, ocrThreshold)
+            # 判断识别文本是来正常;
+            if curFocusText == "ERR<Exp>" or curFocusText.__len__() == 0:
+                continue
+
+            # 转成小写;
+            curFocusText = curFocusText.lower()
+            self.info("[%s]当前识别出的文本=%s" % (optionName, curFocusText))
+
+            # 是否取数字文本(肯定在value节点上);
+            if isNumberText is True:
+                # 特殊情况处理:某些情况下,会将包含数字以外的区域一起识别;
+                curFocusText = curFocusText.strip('>')
+                # 将数字分组
+                numberTextList = strSplit(curFocusText)
+                # 只判断最后一位是否为数字;
+                if numberTextList.__len__() < 1:
+                    self.error(u"当前识别的文本不是数字文本:%s" % curFocusText)
+                    continue
+
+                try:
+                    numberText = float(numberTextList[numberTextList.__len__() - 1])
+                    # 记录value值;
+                    self.__optionValueText = numberText
+                    return True, True, numberText
+                except Exception:
+                    continue
+            else:
+                # 当前option识别的文本与所有兄弟项文本比较;
+                for siblingText in siblingTextList:
+                    # 转为小写,保证所有比较都是小写;
+                    siblingText = siblingText.lower()
+                    # 兄弟项文本是否被包含在curFocusText中或相同;
+                    if siblingText.lower() in curFocusText.lower() or strcmp(siblingText, curFocusText):
+                        isOnSibling = True
+                        self.info(u"当前焦点在[%s], 目标焦点为[%s]" % (siblingText, optionName))
+                        self.info(u"optionTextList:%s" % optionTextList)
+                        # 再判断,该兄弟项是否为目标节点(curOption);
+                        for optionText in optionTextList:
+                            optionText = optionText.lower()
+                            # 若当前兄弟项为目标option返回True、文本;
+                            if strcmp(optionText, siblingText):
+                                return True, True, curFocusText
+                            # endif
+                        # endfor
+                        # 在兄弟项中,退出循环;
+                        break
+                    # endif
+                # endfor
+
+                if isOnSibling is False:
+                    self.error(u"未聚集到任何[%s]的兄弟项中" % optionName)
+                else:
+                    self.info("未聚集到目标节点[%s],当前文本=%s" % (optionName, curFocusText))
+                    return found, False, curFocusText
+            # endif
+        # endfor
+
+        # 默认返回;
+        return found, False, 0 if isNumberText else curFocusText
+
+    '''
+    函数:获取动态图片文本内容
+    参数:(略,请看调用函数)
+    注意:
+    返回:Boolean、文本识别内容。成功识别出文本,返回True及文本内容。
+    '''
+
+    def __getDynamicPicText(self, optionName, optionTextList, siblingTextList, ocrConfigList, ocrThreshold, marqueeDict,
+                            isNumberText, isValueSheet):
+        # 判断图片是否动态:截图两次,判断两次文本内容是否相同;
+        firstFocus, firstTarget, firstText = self.__getStaticPicText(self.takePicture(), optionName, optionTextList,
+                                                                     siblingTextList,
+                                                                     [{"lan": "ChinesePRC+English", "type": 10001}],
+                                                                     ocrThreshold, isNumberText, isValueSheet)
+        if firstFocus is False:
+            self.error(u"[%s]第一次截图未识别出聚焦框" % optionName)
+            return False, None
+        # 目标option不是跑马灯,且已经聚焦到了目标option
+        elif firstTarget is True:
+            self.info(u"已聚焦到了目标option:%s" % optionName)
+            return firstTarget, firstText
+
+        # 发送鲜活键, 保证界面鲜活;
+        self.sendAliveKey(marqueeDict['alive_key'])
+        # 第二次截图;
+        secondFocus, secondTarget, secondText = self.__getStaticPicText(self.takePicture(), optionName, optionTextList,
+                                                                        siblingTextList,
+                                                                        [{"lan": "ChinesePRC+English", "type": 10001}],
+                                                                        ocrThreshold, isNumberText, isValueSheet)
+        if secondFocus is False:
+            self.error(u"[%s]第二次截图未识别出聚焦框" % optionName)
+            return False, None
+
+        # 发送鲜活键, 保证界面鲜活;
+        self.sendAliveKey(marqueeDict['alive_key'])
+
+        # 比较两文本是否相同;
+        if firstText.__len__() and firstText == secondText:
+            self.info(u"截图两次,文本(%s)识别相同,聚焦的不是跑马灯Option" % firstText)
+            return False, firstText
+        elif firstText.__len__() == 0:
+            self.warn(u"未能识别出当前option")
+            return False, firstText
+
+        # 文本不相同,为动态图片;
+        menuList = marqueeDict['menu']
+        # 如果只有一项跑马灯,且目标option亦在跑马灯列表中,则直接返回成功结果
+        if menuList.__len__() == 1 and (optionName in menuList):
+            self.info(u"该层菜单只有一项跑马灯, 找到即成功返回")
+            return True, firstText
+
+        picList = []
+        # 如果有多项同级option都是跑马灯, 要成功识别文本需要间隔截图5次(大概会成功截图到最全的文本);
+        for i in range(0, 5):
+            picList.append(self.takePicture())
+            # 间隔多久截图;
+            time.sleep(marqueeDict['sleep_time'])
+            # 发送鲜活键;
+            self.sendAliveKey(marqueeDict['alive_key'])
+
+        ocrTextList = []
+        # 对截图进行文本识别分析;
+        for pic in picList:
+            isFocus, isTarget, text = self.__getStaticPicText(pic, optionName, optionTextList, siblingTextList,
+                                                              ocrConfigList,
+                                                              ocrThreshold, isNumberText, isValueSheet,
+                                                              marqueeDict['alive_key'])
+            if isTarget is True:
+                ocrTextList.append(text)
+            # 发送鲜活键;
+            self.sendAliveKey(marqueeDict['alive_key'])
+
+        # 过滤重复的字符;
+        ocrTextList = self.__removeDuplicateString(ocrTextList)
+        self.info(u"识别到的跑马灯ocr文字列表:%s" % ocrTextList)
+        # 获取动态文本的option字典;
+        dynamicOptionOcrDict = self.__getOptionInfoDict(menuList)
+        self.info(u"获取到的跑马灯Option对应的ocr字典:%s" % dynamicOptionOcrDict)
+        # 遍历:识别结果与xls结果进行比较;
+        for dynamicOption in dynamicOptionOcrDict:
+            dynamicOptionOcrList = dynamicOptionOcrDict[dynamicOption]
+            for dynamicOptionOcr in dynamicOptionOcrList:
+                # 只要有3张满足,判断找到option;
+                count = 0
+                for ocrText in ocrTextList:
+                    if ocrText.lower() in dynamicOptionOcr:
+                        count += 1
+
+                if count >= 3 and optionName == dynamicOption:
+                    return True, ocrText
+                else:
+                    self.info(u"当前聚焦的跑马灯实际为:%s" % dynamicOption)
+                    return False, ocrText
+            # endfor
+        # endfor
+
+        self.info("未能识别到当前聚焦的跑马灯Option")
+
+        return False, 0 if isNumberText else None
+
+    '''
+    函数:获取option名称数组内所有option的详细信息.
+    参数:
+        optionNameList      option名称数组。
+    返回:字典。
+        {
+            "option1": {self.__optionExcel.getOptionInfo(option1)[1]},
+            "option2": {self.__optionExcel.getOptionInfo(option2)[1]},
+            "option3": {self.__optionExcel.getOptionInfo(option3)[1]},
+        }
+    '''
+
+    def __getOptionInfoDict(self, optionNameList):
+        OptionInfoDict = {}
+        for optionName in optionNameList:
+            found, optionDict = self.__optionExcel.getOptionInfo(optionName)
+            if found:
+                OptionInfoDict[optionName] = optionDict
+            # endif
+        # endfor
+        return OptionInfoDict
+
+    '''
+    函数:找到两个字符串左边或者右边相同的部分
+    参数:
+        str1:
+        str2:
+        direction       方向,默认为right
+    返回:
+    '''
+
+    def __findDuplicateString(self, str1, str2, direction="right"):
+        index = 0
+        if direction == "right":
+            while True:
+                index -= 1
+                if abs(index) > str1.__len__() or abs(index) > str2.__len__():
+                    break
+                if not str1[index] == str2[index]:
+                    break
+            if index == -1:
+                self.info(u"没有找到重复文本")
+                return ""
+            return str1[index + 1:]
+        elif direction == "left":
+            while True:
+                if abs(index) >= str1.__len__() or abs(index) >= str2.__len__():
+                    break
+                if not str1[index] == str2[index]:
+                    break
+                index += 1
+            return str1[:index]
+
+    '''
+    函数:获取路径长度。
+    参数:
+    返回:
+    '''
+
+    def getPathLength(self):
+        if self.__optionPaths is None:
+            self.error(u"路径空,返回0")
+            return 0
+
+        self.info(u"路径长度:%d" % self.__optionPaths.__len__())
+        return self.__optionPaths.__len__()
+
+    '''
+    函数:去掉字符串数组中每个字符串 左边或右边相同的部分
+    参数:
+        strList
+    返回:
+    '''
+
+    def __removeDuplicateString(self, strList):
+        finishedList = strList
+        directionList = ["left", "right"]
+        for direction in directionList:
+            same_str = self.__findDuplicateString(strList[0], strList[1], direction)
+            if same_str == "":
+                continue
+            else:
+                for i in range(2, strList.__len__()):
+                    same_str = self.__findDuplicateString(same_str, strList[i], direction)
+                    if same_str == "":
+                        break
+                if same_str != "":
+                    finishedList = []
+                    for text in strList:
+                        if direction == "left":
+                            text = str[same_str.__len__():]
+                        else:
+                            text = str[:-same_str.__len__()]
+
+                        finishedList.append(text)
+                    # endfor
+                # endif
+            # endif
+        # endfor
+        return finishedList
+
+    '''
+    函数:发送红老鼠按键;
+    参数:
+        key     1、如果是字符串时,当作单个按键; 2、如果是list时,当作多个按键;
+        count   执行多少次key;
+        wait    1、执行单个key后,等待时长(因为电视机响应遥控需要时间);
+                2、执行list多个key后,每个key的等待时间;
+    返回:无
+    '''
+
+    def sendKey(self, key, count=1, duration=1.0):
+        if key is not None and key.__len__() > 0:
+            if type(key) == list:
+                for k in key:
+                    # 清除前后空格;
+                    k = k.lstrip()
+                    k = k.rstrip()
+                    COptionAction.__redRat3.sendKey(k, 1, duration)
+            else:
+                key = str(key)
+                # 清除前后空格;
+                key = key.lstrip()
+                key = key.rstrip()
+                COptionAction.__redRat3.sendKey(key, count, duration)
+        else:
+            self.error(u"error:按键内容空")
+
+    '''
+    函数:发送鲜活键;
+    参数:
+        aliveKey     鲜活按键;
+    返回:无
+
+    注意:鲜活键等待时间是0.1秒,因为不考虑截图。
+    '''
+
+    def sendAliveKey(self, aliveKey):
+        self.sendKey(aliveKey, 1, 0.1)
+
+    '''
+    函数:按照新格式执行enter_key。
+    参数:
+        eKeyDict 即enterKey的字典对象,包含的wait和duration等参数。
+
+        wait,duration是为了兼容旧menutree按照others配置参数的处理方法。
+        如果others中有配waitTime和duration,优先采用others里的配置,否则则采用enterKey里的配置。
+    '''
+    def __executeEnterKey(self, eKeyDict):
+        # 先从config读取
+        wait = self.__optionConfig.getParentWaitTime(self.__curOptionInfo['parent'])
+        # config中读取不到则尝试从others中读取
+        if wait == 1.0:
+            try:
+                others = json.loads(self.__curOptionInfo['others'])
+                if "waitTime" in others:
+                    if others['waitTime'] != "":
+                        wait = float(others['waitTime'])
+            except Exception,e:
+                pass
+        # 读出来的enter_key,不为空或者default时,按格式执行enter_key
+        if not strcmp(eKeyDict['key'][0], 'default') and eKeyDict['key'][0] != "":
+            self.__executeKey(eKeyDict, wait=wait)
+        if eKeyDict['tolevel'] != "":
+            level = eKeyDict['tolevel']
+        else:
+            level = ""
+        # value层的enter_key存在特殊字段,isback。用于判断执行enter_key以后会不会返回父层。
+        # isback为0或为""时:
+        #     则认为不返回父层;
+        # 为1时:
+        #     如果enter_key中没有配tolevel,则默认返回上一层父层;
+        #     如果有,则默认返回到tolevel指定层级的父层。
+        # 非value层,则self.__pos默认进入下一层。
+        if self.isOnValueSheet():
+            if eKeyDict['isback'] == "" or int(eKeyDict['isback']) == 0:
+                pass
+            else:
+                if level == "":
+                    self.__pos -= 1
+                else:
+                    self.__pos = self.__getLevelIndex(level) + 1
+        else:
+            self.__pos += 1
+        # 重新获取次信息;
+        self.getCurOptionInfo()
+
+    def __executeToParentKey(self, tKeyDict):
+        self.__executeKey(tKeyDict)
+        # 层级变化
+        if 'tolevel' in tKeyDict and tKeyDict['tolevel'] != "":
+            level = tKeyDict['tolevel']
+            self.__pos = self.__getLevelIndex(level) + 1
+        else:
+            self.__pos -= 1
+        # 重新获取次信息;
+        self.getCurOptionInfo()
+
+    def __executeKey(self, keyDict, wait=1.0, duration=1.0):
+        if wait != 1.0 and keyDict['wait'] != "":
+            wait = float(keyDict['wait'])
+        if duration != 1.0 and keyDict['duration'] != "":
+            duration = float(keyDict['duration'])
+        for key in keyDict['key']:
+            self.sendKey(key, 1, duration)
+        time.sleep(wait)
+
+    def executeKey(self, keyDict, wait=1.0, duration=1.0):
+        self.__executeKey(keyDict, wait, duration)
+
+    def setValue(self, optionValue):
+        self.__optionValue = optionValue
+        if self.__optionValue != "" or self.__optionValue is not None:
+            self.__optionValueInfo = self.__optionExcel.getOptionValueInfo(self.__optionName, self.__optionValue)
+
+        return True if self.__optionValueInfo and self.__optionValueInfo.__len__() else False
+
+
+if __name__ == "__main__":
+    exData = CExtraData()
+    optionExcel = COptionExcel(exData)
+    optionConfig = COptionConfig(exData, optionExcel)
+    optionAction = COptionAction('source', None, optionConfig, optionExcel)
+    # print optionAction.optionValueInfo
+    print optionAction.curOptionInfo
+    # ====================================== #
+    # optionAction.callFirstOptionShortCutKey()

+ 320 - 320
ssat_sdk/MenuTree3/OptionConfig.py

@@ -1,320 +1,320 @@
-# -*- coding:utf-8 -*-
-import os
-from BaseLog import CBaseLog
-from TConfig import TConfig
-from ExtraData import CExtraData
-from OptionExcel import COptionExcel
-
-
-g_level = ['First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth',
-           'Seventh', 'Eighth', 'Ninth', 'Tenth', 'Eleventh', 'Twelfth']
-
-
-# 注意:所有不对外暴露的变量和函数需要私有化,以明确哪些接口和参数是对外的。
-# 这样便于后期维护时,根据对外的变量和函数来做处理。
-class COptionConfig(TConfig, CBaseLog):
-    def __init__(self, exData, optionExcel):
-        CBaseLog.__init__(self)
-        self.__exData = exData
-        self.__optionExcel = optionExcel
-        if self.__optionExcel is None:
-            self.error(u"路径参数错误:None")
-        # 状态:False表示路径值失败;
-        self.status = False
-        # menuTree目录;
-        self.__uiTreeDir = self.__exData.menuTreeDir
-        # 判断是否为目录;
-        if not os.path.isdir(self.__uiTreeDir):
-            self.error(u"%s 不是menuTree目录" % self.__uiTreeDir)
-        # 判断目录是否存在;
-        if not os.path.exists(self.__uiTreeDir):
-            self.error(u"menuTree(%s)目录不存在" % self.__uiTreeDir)
-        # 判断配置文件是否存在;
-        self.__configPath = os.path.join(self.__uiTreeDir, "menutree.ini")
-        if os.path.exists(self.__configPath):
-            self.status = True
-        else:
-            self.error(u"menutree配置文件不存在:%s" % self.__configPath)
-        # 初始化父类;
-        TConfig.__init__(self, self.__configPath)
-
-    # 获取超级密码;
-    def getSuperPassword(self):
-        return self.get_value("Password", "super")
-
-    # 获取普通密码;
-    def getOrdinaryPassword(self):
-        return self.get_value("Password", "ordinary")
-
-    # 获取父节点等待进入子节点时间;
-    def getParentWaitTime(self, parent):
-        optionName = parent + "WaitTime"
-        try:
-            waitTime = float(self.get_value("waitTime", optionName))
-        except Exception:
-            waitTime = 1.0
-            self.error(u"获取%s界面的等待界面时间失败,使用默认值1.0s" % str(parent))
-        return waitTime
-
-    # 获取阀值字典;
-    '''
-    函数:获取指定option的阀值字典
-    参数:
-        optionName      节点名称;
-    返回:字典。
-    '''
-    def getThresholdDict(self, optionName):
-        section = "Threshold"
-        thresholdDict = {}
-        value = self.get_value(section, optionName)
-        self.info("value:%s,%s" % (value, type(value)))
-        if not value:
-            return thresholdDict
-        value_str = str(value)
-        self.info("value_str:%s,%s" % (value_str, type(value_str)))
-        value_dict = eval(value_str)
-        self.info("value_dict:%s,%s" % (value_dict, type(value_dict)))
-        return value_dict
-
-    '''
-    函数:获取Option的图片配置,方式1
-    参数:
-    返回:Boolean, 字典
-    示例:True, {"icon_path": "", "dcfg": {}, "dir_path": ""}
-    
-    说明:
-        查找顺序:
-            [当前父节点][当前层级][当前节点] ->[当前父节点][当前层级] ->[根节点][当前层级][当前节点]
-            [curParent][curLevel][curOption] ->[curParent][curLevel] ->[firstParent][curLevel][curOption]
-    '''
-    def __getICONConfig1(self, optionName, is_value_sheet=False):
-        # 默认值;
-        def_cfg = {"icon_path": "", "dcfg": {}, "dir_path": ""}
-        paths = self.__optionExcel.getOptionPaths(optionName)
-        # 判断路径节点是否空;
-        if paths.__len__() == 0:
-            self.error(u"当前【%s】的路径节点空,使用默认的配置值%s" % (optionName, def_cfg))
-            return False, def_cfg
-
-        # 获取first parent;
-        first_parent = paths[g_level[0]]['parent']
-        # 获取当前option的level;
-        cur_level = g_level[paths.__len__() - 1]
-        # 当前option的父节点名称;
-        cur_parent = paths[cur_level]['parent']
-        if is_value_sheet is True:
-            cur_level = "value"
-
-        # option聚焦的首配图片路径;
-        icon_path = os.path.join(self.__uiTreeDir, "icon\\", cur_parent + "." + cur_level + "_" + optionName + ".png")
-        # option聚焦的dir首配图片路径;
-        icon_dir_path = os.path.join(self.__uiTreeDir, "icon\\" + cur_parent + "." + cur_level + "_" + optionName + ".dir.png")
-        # 图片聚焦时的定位参数;
-        opc_cfg = self.get_value_dict(cur_level, cur_parent + '.' + optionName)
-
-        # 首配图片判断是否存在,不存在取用次配图片路径;
-        if not os.path.exists(icon_path):
-            # 使用次配图片,父级配图;
-            self.warn(u"Option(%s)首配图片不存在:%s" % (optionName, icon_path))
-            icon_path = os.path.join(self.__uiTreeDir, "icon\\" + cur_parent + "." + cur_level + ".png")
-            icon_dir_path = os.path.join(self.__uiTreeDir, "icon\\" + cur_parent + "." + cur_level + ".dir.png")
-
-            if opc_cfg.__len__() == 0:
-                opc_cfg = self.get_value_dict(cur_level, cur_parent)
-            self.warn(u"Option(%s)首配图片不存在,尝试使用次配图(%s):%s,配置%s" % (optionName, cur_parent, icon_path, opc_cfg))
-            # 如果次配都不存在,使用顶层配图;
-            if not os.path.exists(icon_path):
-                self.warn(u"Option(%s)次配图片不存在:%s" % (optionName, icon_path))
-                # 使用顶层配图(first parent)
-                icon_path = os.path.join(self.__uiTreeDir, "icon\\" + first_parent + "." + cur_level + "_" + optionName + ".png")
-                icon_dir_path = os.path.join(self.__uiTreeDir, "icon\\" + first_parent + "." + cur_level + "_" + optionName + ".dir.png")
-                if opc_cfg.__len__() == 0:
-                    opc_cfg = self.get_value_dict(cur_level, first_parent)
-                self.warn(u"Option(%s)次配图片不存在,尝试使用顶层配图(%s):%s,配置%s" % (optionName, first_parent, icon_path, opc_cfg))
-                # 如果顶层配图不存在,退出;
-                if not os.path.exists(icon_path):
-                    self.error(u"%s对应的顶层菜单配图不存在%s,使用默认配置%s" % (optionName, icon_path, def_cfg))
-                    return False, def_cfg
-                # endif
-            # endif
-        # endif
-
-        if opc_cfg.__len__() == 0:
-            opc_cfg = {"offset": 20, "minPeri": 0, "maxPeri": 0, "minArea": 0, "maxArea": 0, "morphology": []}
-
-        return True, {"icon_path": icon_path, "dcfg": opc_cfg, "dir_path": icon_dir_path}
-
-    '''
-    函数:获取Option的图片配置,方式2
-    参数:
-    返回:Boolean, 字典
-    示例:True, {"icon_path": "", "dcfg": {}, "dir_path": ""}
-
-    说明:
-        查找顺序:
-            [根节点][当前层级][当前节点] ->[根节点][当前层级] ->[根节点][根层级]
-            [firstParent][curLevel][curOption] ->[firstParent][curLevel] ->[firstParent][firstLevel]
-    '''
-    def __getICONConfig2(self, optionName, is_value_sheet=False):
-        # 默认值;
-        def_cfg = {"icon_path": "", "dcfg": {}, "dir_path": ""}
-        paths = self.__optionExcel.getOptionPaths(optionName)
-        # 判断路径节点是否空;
-        if paths.__len__() == 0:
-            self.error(u"当前【%s】的路径节点空,使用默认的配置值%s" % (optionName, def_cfg))
-            return False, def_cfg
-
-        # 获取first parent;
-        first_parent = paths[g_level[0]]['parent']
-        # 获取当前option的level;
-        cur_level = g_level[paths.__len__() - 1]
-        # 当前option的父节点名称;
-        cur_parent = paths[cur_level]['parent']
-        if is_value_sheet is True:
-            cur_level = "value"
-
-        # option聚焦的首配图片路径;
-        icon_path = os.path.join(self.__uiTreeDir, "icon\\",
-                                 first_parent + "." + cur_level + "_" + optionName + ".png")
-        # option聚焦的dir首配图片路径;
-        icon_dir_path = os.path.join(self.__uiTreeDir,
-                                     "icon\\" + first_parent + "." + cur_level + "_" + optionName + ".dir.png")
-        # 图片聚焦时的定位参数;
-        opc_cfg = self.get_value_dict(cur_level, first_parent)
-
-        # 首配图片判断是否存在,不存在取用次配图片路径;
-        if not os.path.exists(icon_path):
-            # 使用次配图片,父级配图;
-            self.warn(u"Option(%s)首配图片不存在:%s" % (optionName, icon_path))
-            icon_path = os.path.join(self.__uiTreeDir, "icon\\" + first_parent + "." + cur_level + ".png")
-            icon_dir_path = os.path.join(self.__uiTreeDir, "icon\\" + first_parent + "." + cur_level + ".dir.png")
-            self.warn(u"Option(%s)首配图片不存在,尝试使用次配图(%s):%s,配置%s" % (optionName, cur_parent, icon_path, opc_cfg))
-            # 如果次配都不存在,使用顶层配图;
-            if not os.path.exists(icon_path):
-                self.warn(u"Option(%s)次配图片不存在:%s" % (optionName, icon_path))
-                # 使用顶层配图(first parent)
-                icon_path = os.path.join(self.__uiTreeDir, "icon\\" + first_parent + "." + "First.png")
-                icon_dir_path = os.path.join(self.__uiTreeDir, "icon\\" + first_parent + "." + "First.dir.png")
-                self.warn(
-                    u"Option(%s)次配图片不存在,尝试使用顶层配图(%s):%s,配置%s" % (optionName, first_parent, icon_path, opc_cfg))
-                # 如果顶层配图不存在,退出;
-                if not os.path.exists(icon_path):
-                    self.error(u"%s对应的顶层菜单配图不存在%s,使用默认配置%s" % (optionName, icon_path, def_cfg))
-                    return False, def_cfg
-                # endif
-            # endif
-        # endif
-
-        if opc_cfg.__len__() == 0:
-            opc_cfg = {"offset": 20, "minPeri": 0, "maxPeri": 0, "minArea": 0, "maxArea": 0, "morphology": []}
-
-        return True, {"icon_path": icon_path, "dcfg": opc_cfg, "dir_path": icon_dir_path}
-
-    '''
-    函数:获取Option的图片配置
-    参数:
-        optionName      要获取的option。
-    返回:Boolean, {}
-    '''
-    def getOptionICONConfig(self, optionName):
-        outResutl, outData = self.__getICONConfig1(optionName, False)
-        if outResutl is False:
-            return self.__getICONConfig2(optionName, False)
-
-        return outResutl, outData
-
-    '''
-    函数:获取Value的图片配置
-    参数:
-        valueName      要获取的value名称。
-    返回:Boolean, {}
-    '''
-    def getValueICONConfig(self, valueName):
-        outResutl, outData = self.__getICONConfig1(valueName, True)
-        if outResutl is False:
-            return self.__getICONConfig2(valueName, True)
-
-        return outResutl, outData
-
-    '''
-    函数:获取Option的OCR配置
-    参数:
-        optionName      要获取的option名称
-    返回:字典
-    '''
-    def getOptionOCRConfig(self, optionName):
-        ocr_dict = []
-        # 默认的ocr配置;
-        def_orc = [{"lan": "ChinesePRC+English", "type": 4}, {"lan": "ChinesePRC+English", "type": 253}, {"lan": "ChinesePRC+English", "type": 10001}]
-        paths = self.__optionExcel.getOptionPaths(optionName)
-        # 判断路径节点是否空;
-        if paths.__len__() == 0:
-            self.error(u"当前【%s】的路径节点空,将使用默认的ocr配置%s" % (optionName, def_orc))
-        else:
-            # 如果状态False,退出;
-            if self.status is False:
-                self.warn(u"配置文件(%s)不存在,%s使用默认的ocr配置%s" % (self.__configPath, optionName, def_orc))
-            else:
-                # 获取当前option的level;
-                cur_level = g_level[paths.__len__() - 1]
-                # 获取first parent;
-                first_parent = paths[g_level[0]]['parent']
-                # 当前option的父节点名称;
-                cur_parent = paths[cur_level]['parent']
-
-                # 优先级逻辑解释:
-
-                # 是否有当前option的ocr配置;
-                if self.has_option(cur_level, optionName + '.ocr'):
-                    ocr_dict = self.get_dict(self.get_value(cur_level, optionName + '.ocr'))
-                    self.warn(u"%s使用自身的ocr配置%s" % (optionName, ocr_dict))
-                else:
-                    # 如果option本身没有配置,获取其父节点的配置;
-                    if self.has_option(cur_level, cur_parent + '.ocr'):
-                        ocr_dict = self.get_dict(self.get_value(cur_level, cur_parent + '.ocr'))
-                        self.warn(u"%s使用父节点%s的ocr配置%s" % (optionName, cur_parent, ocr_dict))
-                    else:
-                        # 如果option父级没有配置,则获取顶层配置(first parent);
-                        if self.has_option("First", first_parent + '.ocr'):
-                            ocr_dict = self.get_dict(self.get_value("First", first_parent + '.ocr'))
-                            self.warn(u"%s使用顶层节点%s的ocr配置%s" % (optionName, first_parent, ocr_dict))
-                    #endif
-                #endif
-            # end-if
-        # end-if
-
-        if ocr_dict.__len__() == 0:
-            ocr_dict = def_orc
-            self.warn(u"无有效的ocr配置,将使用默认的ocr配置%s" % def_orc)
-
-        self.info(u"%s使用的ocr配置=%s" % (optionName, ocr_dict))
-
-        return ocr_dict
-
-    '''
-    函数:获取ICON源屏幕的分辨率配置值
-    参数:
-    返回:
-    
-    说明:
-    该值用途:由于有些icon是在某些分辨率下截图的,但是同机芯项目可能使用同一套UI,只是分辨率不一样而已。
-    所以,根据当初的icon分辨率,可以计算出在别的分辨率下的icon区域大小;
-    '''
-    def getICONResolutionConfig(self):
-        return self.get_value_dict('Screen', 'shape') if self.has_option('Screen', 'shape') else [1920, 1080]
-
-
-if __name__ == "__main__":
-    exData = CExtraData()
-    optionExcel = COptionExcel(exData)
-    optionConfig = COptionConfig(exData, optionExcel)
-    # print "getSuperPassword", optionConfig.getSuperPassword()
-    # print "getOrdinaryPassword", optionConfig.getOrdinaryPassword()
-    # print "getParentWaitTime", optionConfig.getParentWaitTime('setting')
-    # print "getThresholdDict", optionConfig.getThresholdDict('setting')
-    # print "getOptionICONConfig", optionConfig.getOptionICONConfig('picture')
-    print "getOptionICONConfig", optionConfig.getOptionICONConfig('picture_preset')
-    # print "getValueICONConfig", optionConfig.getValueICONConfig('picture')
-    # print "getOptionOCRConfig", optionConfig.getOptionOCRConfig('picture')
-    # print "getICONResolutionConfig", optionConfig.getICONResolutionConfig()
-
+# -*- coding:utf-8 -*-
+import os
+from BaseLog import CBaseLog
+from TConfig import TConfig
+from ExtraData import CExtraData
+from OptionExcel import COptionExcel
+
+
+g_level = ['First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth',
+           'Seventh', 'Eighth', 'Ninth', 'Tenth', 'Eleventh', 'Twelfth']
+
+
+# 注意:所有不对外暴露的变量和函数需要私有化,以明确哪些接口和参数是对外的。
+# 这样便于后期维护时,根据对外的变量和函数来做处理。
+class COptionConfig(TConfig, CBaseLog):
+    def __init__(self, exData, optionExcel):
+        CBaseLog.__init__(self)
+        self.__exData = exData
+        self.__optionExcel = optionExcel
+        if self.__optionExcel is None:
+            self.error(u"路径参数错误:None")
+        # 状态:False表示路径值失败;
+        self.status = False
+        # menuTree目录;
+        self.__uiTreeDir = self.__exData.menuTreeDir
+        # 判断是否为目录;
+        if not os.path.isdir(self.__uiTreeDir):
+            self.error(u"%s 不是menuTree目录" % self.__uiTreeDir)
+        # 判断目录是否存在;
+        if not os.path.exists(self.__uiTreeDir):
+            self.error(u"menuTree(%s)目录不存在" % self.__uiTreeDir)
+        # 判断配置文件是否存在;
+        self.__configPath = os.path.join(self.__uiTreeDir, "menutree.ini")
+        if os.path.exists(self.__configPath):
+            self.status = True
+        else:
+            self.error(u"menutree配置文件不存在:%s" % self.__configPath)
+        # 初始化父类;
+        TConfig.__init__(self, self.__configPath)
+
+    # 获取超级密码;
+    def getSuperPassword(self):
+        return self.get_value("Password", "super")
+
+    # 获取普通密码;
+    def getOrdinaryPassword(self):
+        return self.get_value("Password", "ordinary")
+
+    # 获取父节点等待进入子节点时间;
+    def getParentWaitTime(self, parent):
+        optionName = parent + "WaitTime"
+        try:
+            waitTime = float(self.get_value("waitTime", optionName))
+        except Exception:
+            waitTime = 1.0
+            self.error(u"获取%s界面的等待界面时间失败,使用默认值1.0s" % str(parent))
+        return waitTime
+
+    # 获取阀值字典;
+    '''
+    函数:获取指定option的阀值字典
+    参数:
+        optionName      节点名称;
+    返回:字典。
+    '''
+    def getThresholdDict(self, optionName):
+        section = "Threshold"
+        thresholdDict = {}
+        value = self.get_value(section, optionName)
+        self.info("value:%s,%s" % (value, type(value)))
+        if not value:
+            return thresholdDict
+        value_str = str(value)
+        self.info("value_str:%s,%s" % (value_str, type(value_str)))
+        value_dict = eval(value_str)
+        self.info("value_dict:%s,%s" % (value_dict, type(value_dict)))
+        return value_dict
+
+    '''
+    函数:获取Option的图片配置,方式1
+    参数:
+    返回:Boolean, 字典
+    示例:True, {"icon_path": "", "dcfg": {}, "dir_path": ""}
+    
+    说明:
+        查找顺序:
+            [当前父节点][当前层级][当前节点] ->[当前父节点][当前层级] ->[根节点][当前层级][当前节点]
+            [curParent][curLevel][curOption] ->[curParent][curLevel] ->[firstParent][curLevel][curOption]
+    '''
+    def __getICONConfig1(self, optionName, is_value_sheet=False):
+        # 默认值;
+        def_cfg = {"icon_path": "", "dcfg": {}, "dir_path": ""}
+        paths = self.__optionExcel.getOptionPaths(optionName)
+        # 判断路径节点是否空;
+        if paths.__len__() == 0:
+            self.error(u"当前【%s】的路径节点空,使用默认的配置值%s" % (optionName, def_cfg))
+            return False, def_cfg
+
+        # 获取first parent;
+        first_parent = paths[g_level[0]]['parent']
+        # 获取当前option的level;
+        cur_level = g_level[paths.__len__() - 1]
+        # 当前option的父节点名称;
+        cur_parent = paths[cur_level]['parent']
+        if is_value_sheet is True:
+            cur_level = "value"
+
+        # option聚焦的首配图片路径;
+        icon_path = os.path.join(self.__uiTreeDir, "icon\\", cur_parent + "." + cur_level + "_" + optionName + ".png")
+        # option聚焦的dir首配图片路径;
+        icon_dir_path = os.path.join(self.__uiTreeDir, "icon\\" + cur_parent + "." + cur_level + "_" + optionName + ".dir.png")
+        # 图片聚焦时的定位参数;
+        opc_cfg = self.get_value_dict(cur_level, cur_parent + '.' + optionName)
+
+        # 首配图片判断是否存在,不存在取用次配图片路径;
+        if not os.path.exists(icon_path):
+            # 使用次配图片,父级配图;
+            self.warn(u"Option(%s)首配图片不存在:%s" % (optionName, icon_path))
+            icon_path = os.path.join(self.__uiTreeDir, "icon\\" + cur_parent + "." + cur_level + ".png")
+            icon_dir_path = os.path.join(self.__uiTreeDir, "icon\\" + cur_parent + "." + cur_level + ".dir.png")
+
+            if opc_cfg.__len__() == 0:
+                opc_cfg = self.get_value_dict(cur_level, cur_parent)
+            self.warn(u"Option(%s)首配图片不存在,尝试使用次配图(%s):%s,配置%s" % (optionName, cur_parent, icon_path, opc_cfg))
+            # 如果次配都不存在,使用顶层配图;
+            if not os.path.exists(icon_path):
+                self.warn(u"Option(%s)次配图片不存在:%s" % (optionName, icon_path))
+                # 使用顶层配图(first parent)
+                icon_path = os.path.join(self.__uiTreeDir, "icon\\" + first_parent + "." + cur_level + "_" + optionName + ".png")
+                icon_dir_path = os.path.join(self.__uiTreeDir, "icon\\" + first_parent + "." + cur_level + "_" + optionName + ".dir.png")
+                if opc_cfg.__len__() == 0:
+                    opc_cfg = self.get_value_dict(cur_level, first_parent)
+                self.warn(u"Option(%s)次配图片不存在,尝试使用顶层配图(%s):%s,配置%s" % (optionName, first_parent, icon_path, opc_cfg))
+                # 如果顶层配图不存在,退出;
+                if not os.path.exists(icon_path):
+                    self.error(u"%s对应的顶层菜单配图不存在%s,使用默认配置%s" % (optionName, icon_path, def_cfg))
+                    return False, def_cfg
+                # endif
+            # endif
+        # endif
+
+        if opc_cfg.__len__() == 0:
+            opc_cfg = {"offset": 20, "minPeri": 0, "maxPeri": 0, "minArea": 0, "maxArea": 0, "morphology": []}
+
+        return True, {"icon_path": icon_path, "dcfg": opc_cfg, "dir_path": icon_dir_path}
+
+    '''
+    函数:获取Option的图片配置,方式2
+    参数:
+    返回:Boolean, 字典
+    示例:True, {"icon_path": "", "dcfg": {}, "dir_path": ""}
+
+    说明:
+        查找顺序:
+            [根节点][当前层级][当前节点] ->[根节点][当前层级] ->[根节点][根层级]
+            [firstParent][curLevel][curOption] ->[firstParent][curLevel] ->[firstParent][firstLevel]
+    '''
+    def __getICONConfig2(self, optionName, is_value_sheet=False):
+        # 默认值;
+        def_cfg = {"icon_path": "", "dcfg": {}, "dir_path": ""}
+        paths = self.__optionExcel.getOptionPaths(optionName)
+        # 判断路径节点是否空;
+        if paths.__len__() == 0:
+            self.error(u"当前【%s】的路径节点空,使用默认的配置值%s" % (optionName, def_cfg))
+            return False, def_cfg
+
+        # 获取first parent;
+        first_parent = paths[g_level[0]]['parent']
+        # 获取当前option的level;
+        cur_level = g_level[paths.__len__() - 1]
+        # 当前option的父节点名称;
+        cur_parent = paths[cur_level]['parent']
+        if is_value_sheet is True:
+            cur_level = "value"
+
+        # option聚焦的首配图片路径;
+        icon_path = os.path.join(self.__uiTreeDir, "icon\\",
+                                 first_parent + "." + cur_level + "_" + optionName + ".png")
+        # option聚焦的dir首配图片路径;
+        icon_dir_path = os.path.join(self.__uiTreeDir,
+                                     "icon\\" + first_parent + "." + cur_level + "_" + optionName + ".dir.png")
+        # 图片聚焦时的定位参数;
+        opc_cfg = self.get_value_dict(cur_level, first_parent)
+
+        # 首配图片判断是否存在,不存在取用次配图片路径;
+        if not os.path.exists(icon_path):
+            # 使用次配图片,父级配图;
+            self.warn(u"Option(%s)首配图片不存在:%s" % (optionName, icon_path))
+            icon_path = os.path.join(self.__uiTreeDir, "icon\\" + first_parent + "." + cur_level + ".png")
+            icon_dir_path = os.path.join(self.__uiTreeDir, "icon\\" + first_parent + "." + cur_level + ".dir.png")
+            self.warn(u"Option(%s)首配图片不存在,尝试使用次配图(%s):%s,配置%s" % (optionName, cur_parent, icon_path, opc_cfg))
+            # 如果次配都不存在,使用顶层配图;
+            if not os.path.exists(icon_path):
+                self.warn(u"Option(%s)次配图片不存在:%s" % (optionName, icon_path))
+                # 使用顶层配图(first parent)
+                icon_path = os.path.join(self.__uiTreeDir, "icon\\" + first_parent + "." + "First.png")
+                icon_dir_path = os.path.join(self.__uiTreeDir, "icon\\" + first_parent + "." + "First.dir.png")
+                self.warn(
+                    u"Option(%s)次配图片不存在,尝试使用顶层配图(%s):%s,配置%s" % (optionName, first_parent, icon_path, opc_cfg))
+                # 如果顶层配图不存在,退出;
+                if not os.path.exists(icon_path):
+                    self.error(u"%s对应的顶层菜单配图不存在%s,使用默认配置%s" % (optionName, icon_path, def_cfg))
+                    return False, def_cfg
+                # endif
+            # endif
+        # endif
+
+        if opc_cfg.__len__() == 0:
+            opc_cfg = {"offset": 20, "minPeri": 0, "maxPeri": 0, "minArea": 0, "maxArea": 0, "morphology": []}
+
+        return True, {"icon_path": icon_path, "dcfg": opc_cfg, "dir_path": icon_dir_path}
+
+    '''
+    函数:获取Option的图片配置
+    参数:
+        optionName      要获取的option。
+    返回:Boolean, {}
+    '''
+    def getOptionICONConfig(self, optionName):
+        outResutl, outData = self.__getICONConfig1(optionName, False)
+        if outResutl is False:
+            return self.__getICONConfig2(optionName, False)
+
+        return outResutl, outData
+
+    '''
+    函数:获取Value的图片配置
+    参数:
+        valueName      要获取的value名称。
+    返回:Boolean, {}
+    '''
+    def getValueICONConfig(self, valueName):
+        outResutl, outData = self.__getICONConfig1(valueName, True)
+        if outResutl is False:
+            return self.__getICONConfig2(valueName, True)
+
+        return outResutl, outData
+
+    '''
+    函数:获取Option的OCR配置
+    参数:
+        optionName      要获取的option名称
+    返回:字典
+    '''
+    def getOptionOCRConfig(self, optionName):
+        ocr_dict = []
+        # 默认的ocr配置;
+        def_orc = [{"lan": "ChinesePRC+English", "type": 10000},{"lan": "ChinesePRC+English", "type": 10001}]
+        paths = self.__optionExcel.getOptionPaths(optionName)
+        # 判断路径节点是否空;
+        if paths.__len__() == 0:
+            self.error(u"当前【%s】的路径节点空,将使用默认的ocr配置%s" % (optionName, def_orc))
+        else:
+            # 如果状态False,退出;
+            if self.status is False:
+                self.warn(u"配置文件(%s)不存在,%s使用默认的ocr配置%s" % (self.__configPath, optionName, def_orc))
+            else:
+                # 获取当前option的level;
+                cur_level = g_level[paths.__len__() - 1]
+                # 获取first parent;
+                first_parent = paths[g_level[0]]['parent']
+                # 当前option的父节点名称;
+                cur_parent = paths[cur_level]['parent']
+
+                # 优先级逻辑解释:
+
+                # 是否有当前option的ocr配置;
+                if self.has_option(cur_level, optionName + '.ocr'):
+                    ocr_dict = self.get_dict(self.get_value(cur_level, optionName + '.ocr'))
+                    self.warn(u"%s使用自身的ocr配置%s" % (optionName, ocr_dict))
+                else:
+                    # 如果option本身没有配置,获取其父节点的配置;
+                    if self.has_option(cur_level, cur_parent + '.ocr'):
+                        ocr_dict = self.get_dict(self.get_value(cur_level, cur_parent + '.ocr'))
+                        self.warn(u"%s使用父节点%s的ocr配置%s" % (optionName, cur_parent, ocr_dict))
+                    else:
+                        # 如果option父级没有配置,则获取顶层配置(first parent);
+                        if self.has_option("First", first_parent + '.ocr'):
+                            ocr_dict = self.get_dict(self.get_value("First", first_parent + '.ocr'))
+                            self.warn(u"%s使用顶层节点%s的ocr配置%s" % (optionName, first_parent, ocr_dict))
+                    #endif
+                #endif
+            # end-if
+        # end-if
+
+        if ocr_dict.__len__() == 0:
+            ocr_dict = def_orc
+            self.warn(u"无有效的ocr配置,将使用默认的ocr配置%s" % def_orc)
+
+        self.info(u"%s使用的ocr配置=%s" % (optionName, ocr_dict))
+
+        return ocr_dict
+
+    '''
+    函数:获取ICON源屏幕的分辨率配置值
+    参数:
+    返回:
+    
+    说明:
+    该值用途:由于有些icon是在某些分辨率下截图的,但是同机芯项目可能使用同一套UI,只是分辨率不一样而已。
+    所以,根据当初的icon分辨率,可以计算出在别的分辨率下的icon区域大小;
+    '''
+    def getICONResolutionConfig(self):
+        return self.get_value_dict('Screen', 'shape') if self.has_option('Screen', 'shape') else [1920, 1080]
+
+
+if __name__ == "__main__":
+    exData = CExtraData()
+    optionExcel = COptionExcel(exData)
+    optionConfig = COptionConfig(exData, optionExcel)
+    # print "getSuperPassword", optionConfig.getSuperPassword()
+    # print "getOrdinaryPassword", optionConfig.getOrdinaryPassword()
+    # print "getParentWaitTime", optionConfig.getParentWaitTime('setting')
+    # print "getThresholdDict", optionConfig.getThresholdDict('setting')
+    # print "getOptionICONConfig", optionConfig.getOptionICONConfig('picture')
+    print "getOptionICONConfig", optionConfig.getOptionICONConfig('picture_preset')
+    # print "getValueICONConfig", optionConfig.getValueICONConfig('picture')
+    # print "getOptionOCRConfig", optionConfig.getOptionOCRConfig('picture')
+    # print "getICONResolutionConfig", optionConfig.getICONResolutionConfig()
+

+ 134 - 134
ssat_sdk/MenuTree3/TConfig.py

@@ -1,134 +1,134 @@
-# -*- coding:utf-8 -*-
-import string
-import os
-import sys
-import time
-from configparser import ConfigParser
-import json
-
-
-class TConfig:
-    def __init__(self, ini_path):
-        self.path = ini_path
-        self.cfgParser = ConfigParser()
-
-    # 保存修改;
-    def save_config(self):
-        with open(self.path, 'wb') as configfile:
-            self.cfgParser.write(configfile)
-
-    # 字符转字典;
-    def get_dict(self, value):
-        return json.loads(value)
-
-    def get_value_dict(self, section, option):
-        value = self.get_value(section, option)
-        if value is None:
-            return {}
-        return json.loads(value)
-
-    # 获取参数值;默认返回字符串
-    def get_value(self, section, option):
-        # 读取配置文件;
-        self.cfgParser.read(self.path)
-        if self.cfgParser.has_option(section, option):
-            return self.cfgParser.get(section, option)
-        return None
-
-    # 获取参数值;
-    def get_int(self, section, option):
-        # 读取配置文件;
-        self.cfgParser.read(self.path)
-        if self.cfgParser.has_option(section, option):
-            return self.cfgParser.getint(section, option)
-        return None
-
-    # 获取参数值;
-    def get_float(self, section, option):
-        # 读取配置文件;
-        self.cfgParser.read(self.path)
-        if self.cfgParser.has_option(section, option):
-            return self.cfgParser.getfloat(section, option)
-        return None
-
-    # 获取参数值;
-    def get_boolean(self, section, option):
-        # 读取配置文件;
-        self.cfgParser.read(self.path)
-        if self.cfgParser.has_option(section, option):
-            return self.cfgParser.getboolean(section, option)
-        return None
-
-    # 获取键值;
-    def get_options(self, section):
-        # 读取配置文件;
-        self.cfgParser.read(self.path)
-        if self.cfgParser.has_section(section):
-            return self.cfgParser.options(section)
-        return None
-
-    # 获取所有sections;
-    def get_sections(self):
-        # 读取配置文件;
-        self.cfgParser.read(self.path)
-        return self.cfgParser.sections()
-
-    # 获取指定section所有items;
-    def get_items(self, section):
-        # 读取配置文件;
-        self.cfgParser.read(self.path)
-        if self.cfgParser.has_section(section):
-            return self.cfgParser.items(section)
-        return None
-
-    # 添加section;
-    def add_section(self, section):
-        # 读取配置文件;
-        self.cfgParser.read(self.path)
-        if self.cfgParser.has_section(section) is False:
-            self.cfgParser.add_section(section)
-        # 保存修改;
-        self.save_config()
-
-    # 设置值;
-    def set_value(self, section, option, value):
-        # 读取配置文件;
-        self.cfgParser.read(self.path)
-        if self.cfgParser.has_section(section) is False:
-            self.cfgParser.add_section(section)
-        self.cfgParser.set(section, option, value)
-        # 保存修改;
-        self.save_config()
-
-    # 删除指定option;
-    def del_option(self, section, option):
-        # 读取配置文件;
-        self.cfgParser.read(self.path)
-        self.cfgParser.remove_option(section, option)
-        # 保存修改;
-        self.save_config()
-
-    # 删除section;
-    def del_section(self, section):
-        # 读取配置文件;
-        self.cfgParser.read(self.path)
-        self.cfgParser.remove_section(section)
-        # 保存修改;
-        self.save_config()
-
-    def has_section(self, section):
-        # 读取配置文件;
-        self.cfgParser.read(self.path)
-        return self.cfgParser.has_section(section)
-
-    def has_option(self, section, option):
-        # 读取配置文件;
-        self.cfgParser.read(self.path)
-        return self.cfgParser.has_option(section, option)
-
-
-if __name__ == "__main__":
-    tconfig = TConfig(r'E:\SAT\resource\MenuTree\NT72\DVB_nafrican\MenuTree.ini')
-    value = tconfig.get_value('value', 'Setting')
-    print value
-
+# -*- coding:utf-8 -*-
+import string
+import os
+import sys
+import time
+from configparser import ConfigParser
+import json
+import codecs
+
+class TConfig:
+    def __init__(self, ini_path):
+        self.path = ini_path
+        self.cfgParser = ConfigParser()
+
+    # 保存修改;
+    def save_config(self):
+        with open(self.path, 'wb') as configfile:
+            self.cfgParser.write(configfile)
+
+    # 字符转字典;
+    def get_dict(self, value):
+        return json.loads(value)
+
+    def get_value_dict(self, section, option):
+        value = self.get_value(section, option)
+        if value is None:
+            return {}
+        return json.loads(value)
+
+    # 获取参数值;默认返回字符串
+    def get_value(self, section, option):
+        # 读取配置文件;
+        self.cfgParser.read_file(codecs.open(self.path, "r", "utf-8-sig"))
+        if self.cfgParser.has_option(section, option):
+            return self.cfgParser.get(section, option)
+        return None
+
+    # 获取参数值;
+    def get_int(self, section, option):
+        # 读取配置文件;
+        self.cfgParser.read_file(codecs.open(self.path, "r", "utf-8-sig"))
+        if self.cfgParser.has_option(section, option):
+            return self.cfgParser.getint(section, option)
+        return None
+
+    # 获取参数值;
+    def get_float(self, section, option):
+        # 读取配置文件;
+        self.cfgParser.read_file(codecs.open(self.path, "r", "utf-8-sig"))
+        if self.cfgParser.has_option(section, option):
+            return self.cfgParser.getfloat(section, option)
+        return None
+
+    # 获取参数值;
+    def get_boolean(self, section, option):
+        # 读取配置文件;
+        self.cfgParser.read_file(codecs.open(self.path, "r", "utf-8-sig"))
+        if self.cfgParser.has_option(section, option):
+            return self.cfgParser.getboolean(section, option)
+        return None
+
+    # 获取键值;
+    def get_options(self, section):
+        # 读取配置文件;
+        self.cfgParser.read_file(codecs.open(self.path, "r", "utf-8-sig"))
+        if self.cfgParser.has_section(section):
+            return self.cfgParser.options(section)
+        return None
+
+    # 获取所有sections;
+    def get_sections(self):
+        # 读取配置文件;
+        self.cfgParser.read_file(codecs.open(self.path, "r", "utf-8-sig"))
+        return self.cfgParser.sections()
+
+    # 获取指定section所有items;
+    def get_items(self, section):
+        # 读取配置文件;
+        self.cfgParser.read_file(codecs.open(self.path, "r", "utf-8-sig"))
+        if self.cfgParser.has_section(section):
+            return self.cfgParser.items(section)
+        return None
+
+    # 添加section;
+    def add_section(self, section):
+        # 读取配置文件;
+        self.cfgParser.read_file(codecs.open(self.path, "r", "utf-8-sig"))
+        if self.cfgParser.has_section(section) is False:
+            self.cfgParser.add_section(section)
+        # 保存修改;
+        self.save_config()
+
+    # 设置值;
+    def set_value(self, section, option, value):
+        # 读取配置文件;
+        self.cfgParser.read_file(codecs.open(self.path, "r", "utf-8-sig"))
+        if self.cfgParser.has_section(section) is False:
+            self.cfgParser.add_section(section)
+        self.cfgParser.set(section, option, value)
+        # 保存修改;
+        self.save_config()
+
+    # 删除指定option;
+    def del_option(self, section, option):
+        # 读取配置文件;
+        self.cfgParser.read_file(codecs.open(self.path, "r", "utf-8-sig"))
+        self.cfgParser.remove_option(section, option)
+        # 保存修改;
+        self.save_config()
+
+    # 删除section;
+    def del_section(self, section):
+        # 读取配置文件;
+        self.cfgParser.read_file(codecs.open(self.path, "r", "utf-8-sig"))
+        self.cfgParser.remove_section(section)
+        # 保存修改;
+        self.save_config()
+
+    def has_section(self, section):
+        # 读取配置文件;
+        self.cfgParser.read_file(codecs.open(self.path, "r", "utf-8-sig"))
+        return self.cfgParser.has_section(section)
+
+    def has_option(self, section, option):
+        # 读取配置文件;
+        self.cfgParser.read_file(codecs.open(self.path, "r", "utf-8-sig"))
+        return self.cfgParser.has_option(section, option)
+
+
+if __name__ == "__main__":
+    tconfig = TConfig(r'D:\SAT\resource\MenuTree\NT72\NT72\MenuTree.ini')
+    value = tconfig.get_value('value', 'Setting')
+    print value
+

+ 710 - 632
ssat_sdk/UATree/UAT_focusCommand.py

@@ -1,633 +1,711 @@
-# -*- coding:utf-8 -*-
-import time,sys,os
-from UAT_tree import UATTree
-from UAT_log import error,info,debug
-from UAT_PathManage import UATPathManage
-from UIParamUtil import UIParamUtil
-# from UAT_runnerCommand import UATRunnerCommand
-from ssat_sdk.python_uiautomator import PyUIAutomator,DirectionManageAndroid,FocusManageAndroid
-import math
-import traceback
-
-ERROR = True
-INFO = True
-DEBUG = True  # 上传SVN版本此参数关闭
-'''
-采用uiautomator技术,处理界面定位问题,用于移动焦点到目标组件上
-'''
-class FocusCommand():
-    cls = "FocusCommand"
-    UIObjParam = UIParamUtil.UIObjParam
-    UATParamMap = UIParamUtil.UATParamMap
-    def __init__(self, runnerCommand):
-        self.runnerCommand = runnerCommand
-        self.pyU = PyUIAutomator()
-        self.dm = DirectionManageAndroid()
-        self.fm = FocusManageAndroid(self.pyU, self.dm)
-        self.inLayout= False
-
-    '''
-    解析option的焦点组件参数,返回focusView或者focus-select组件参数
-    :param option: option字典
-    :return 返回 focus焦点参数字典,采用setObjParam可以转换成UIObject识别需要的参数。
-    如果为None,则表示不需要焦点选中。
-    '''
-    def getFPFromOption(self, option):
-        focusParam = None
-        fsParam = option[UATTree.TAB_FOCUS_SELECT]
-        fvParam = option[UATTree.TAB_FOCUSE_VIEW]
-        if fsParam[UATTree.FS_Type].__len__() > 1:
-            if fsParam[UATTree.FS_Type].lower() == "focus":
-                fsParam["focus"] = True
-                focusParam = fsParam
-            elif fsParam[UATTree.FS_Type].lower() == "select":
-                fsParam["select"] = True
-                focusParam = fsParam
-            elif fsParam[UATTree.FS_Type].lower() == "no":
-                focusParam = None
-            else:
-                error(self.cls, "getFPFromOption", "Option %s focus-select参数异常。" % option[UATTree.TAB_NAME], ERROR)
-        else:
-            focusParam = fvParam
-        return focusParam
-
-    '''
-    解析option的焦点组件参数,返回focusView或者focus-select组件参数.
-    在getFPFromOption返回值上,采用setObjParam转换成UIObject识别需要的参数。
-    :param option: option字典
-    :return 返回 focus焦点UIObject参数字典.
-    '''
-    def getFPObjParam(self, option):
-        focusParam = self.getFPFromOption(option)
-        fpObjParam = UIParamUtil.setObjParam(focusParam)
-        return fpObjParam
-
-    '''
-    解析存储在UATree里的moveKey字典,得到按键类型、键值数组、最大尝试次数
-    :param moveKey, parent字典中moveKey字典
-    :param optionCount: moveKey对应的parent的option个数
-    :return keyType, keyCodeList, Max_Trye
-    '''
-    def parseMoveKey(self,moveKey, optionCount):
-        if moveKey[UATTree.Max_Try] != "":
-            Max_Try = int(moveKey[UATTree.Max_Try])
-        else:
-            Max_Try = optionCount
-        findDirection = ["down","up"]
-        keyType = UATTree.Key_Event
-        if moveKey[UATTree.Key_Event].__len__() > 1:
-            findDirection = moveKey[UATTree.Key_Event]
-            keyType = UATTree.Key_Event
-        elif moveKey[UATTree.Key_IR].__len__() > 1:
-            findDirection = moveKey[UATTree.Key_IR]
-            keyType = UATTree.Key_IR
-        elif moveKey[UATTree.Key_Input].__len__() > 1:
-            inputCmd = moveKey[UATTree.Key_Input]
-            #TODO input情况的处理
-            return None,None,None
-        else:
-            return None,None,None
-        return keyType, findDirection, Max_Try
-    '''
-    在parent界面,将焦点移动到目标option上。
-    焦点定位:根据layout是不是限制焦点范围,进行焦点组件寻找,焦点组件类型:focus、select、focusView、long-click
-    目标定位:纯粹是根据optionView配置组建坐标定位
-    :param parent:当前电视界面的parent字典
-    :param option:目标option字典
-    :return True/False
-    '''
-    def focusOptionView(self, parent, option,chooseType):
-        #是否采用layout限制焦点判断范围
-        layout = parent[UATTree.TAB_LAYOUT]
-        print "focusOptionView,layout:",layout
-        self.inLayout = layout[UATTree.Layout_Limit]
-        # 找到目标optionView参数
-        layoutUIObj = {}
-        optionUIObjParam = UIParamUtil.setObjParam(option[UATTree.TAB_OPTION_VIEW])
-        if self.inLayout == 1:
-            layoutUIObjParam = UIParamUtil.setObjParam(layout)
-        else:
-            layoutUIObjParam = {}
-        # 获取move key按键
-        moveKey = parent[UATTree.TAB_MOVE_KEY]
-        keyType, findDirection,Max_Try = self.parseMoveKey(moveKey, parent[UATTree.TAB_OPTION].__len__())
-        if keyType is None:
-            error(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " option 读取 move_key失败", ERROR)
-            return False
-        #获取optionview的View_sambounds参数
-        osamBounds = option[UATTree.TAB_OPTION_VIEW][UATTree.View_sambounds]
-        #获取焦点view参数
-        focusParam = self.getFPFromOption(option)
-        focusUIParam = UIParamUtil.setObjParam(focusParam)
-        if focusParam is None:
-            fsamBounds = []
-        else:
-            fsamBounds = focusParam[UATTree.View_sambounds]
-        if chooseType.lower()== 'no':
-            return self.getNoFocusedObj(option, Max_Try, findDirection, keyType, layoutUIObjParam)
-        else:
-            return self.toDestObj(parent, option, Max_Try, findDirection, keyType, layoutUIObjParam,fsamBounds,osamBounds)
-
-    '''
-    传入uiautomator需要的目标组件参数,获取无聚焦属性的option对象  
-    '''
-    def getNoFocusedObj(self,option, Max_Try, findDirection, keyType, layoutUIObjParam):
-        count = 0
-        directIndex = 0
-        while(True):
-            if count >= Max_Try and directIndex >= findDirection.__len__()-1:
-                break
-            time.sleep(0.1)
-            destUIObject = self.getOptionUIObj(option, layoutUIObjParam)
-            if destUIObject and destUIObject.exists():
-                info(self.cls,"getNoFocusedObj","找到目标option %s"%option[UATTree.TAB_NAME],INFO)
-                return True
-            else:
-                if count < Max_Try:
-                    count = count + 1
-                elif count >= Max_Try and directIndex<findDirection.__len__() -1:
-                    count=0
-                    Max_Try *= 2
-                    directIndex = directIndex + 1
-                self.fm.pressKeyByType(findDirection[directIndex], keyType)
-        error(self.cls, "getNoFocusedObj", "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)), ERROR)
-        return False
-
-    '''
-    传入uiautomator需要的目标组件参数和焦点框参数,用于寻找目标。
-    '''
-    def toDestObj(self, parent, option, Max_Try=10, findDirections=["down","up"], keyType=UATTree.Key_Event,
-                  layoutUIParam={}, fsamBounds=[],osamBounds=[]):
-        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
-        print "toDestObj,enter:",Max_Try,findDirections,keyType,fsamBounds,osamBounds
-        count = 0
-        Max_Try = Max_Try
-        directIndex = 0
-        focusCount = 0  # 当目标和聚焦点在一个页面同时存在时,从当前聚焦点到目标的移动次数
-        focusedBoundsPre = {u'top': 0, u'left': 0, u'right': 0, u'bottom': 0}  # 上一轮聚焦点的坐标
-        destBoundsPre = {u'top': 0, u'left': 0, u'right': 0, u'bottom': 0}  # 上一轮目标的坐标
-        while (True):
-            if count >= Max_Try and directIndex >= findDirections.__len__()-1:
-                break
-            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
-            time.sleep(0.1)
-            destUIObject = self.getOptionUIObj(option,layoutUIParam)
-            focusParam = self.getFPFromOption(option)
-            #option是否需要聚焦
-            needFocus = True
-            if focusParam.has_key(UATTree.FS_Type) and \
-                focusParam[UATTree.FS_Type] == "no":
-                needFocus = False
-            if destUIObject and destUIObject.exists(): # 有聚焦属性的option存在不代表已聚焦,无聚焦属性的option存在即当作已聚焦
-                try:
-                    retCode, focusedUIObject = self.getChooseUIObjP(parent, layoutUIParam,needFocus=needFocus)
-                    debug(self.cls, "toDestObj", "Parent %s 当前聚焦框retCode:%d" % (parent[UATTree.TAB_NAME],retCode), DEBUG)
-                    #如果焦点不存在,只需要判断optionView存在即可算是聚焦成功
-                    if retCode == self.Focus_Need_Not:
-                        return True
-                    #根据已获取到destUIObject和focusedUIObject,进行是否选中目标option判断,
-                    #未选中,则继续利用moveKey查找
-                    focusedBounds = focusedUIObject.info['bounds']
-                    info(self.cls,"toDestObj","当前聚焦框所在坐标:%s"%focusedBounds,INFO)
-                    destBounds = destUIObject.info['bounds']
-                    info(self.cls, "toDestObj", "已寻找%s次" % focusCount, INFO)
-                    info(self.cls,"toDestObj","目标Option %s坐标:%s"%(option[UATTree.TAB_NAME], destBounds),INFO)
-                    if self.hasFocusDest(focusedBounds, destBounds, fsamBounds=fsamBounds, osamBounds=osamBounds):
-                        info(self.cls,"toDestObj","成功聚焦到目标焦点:%s"%option[UATTree.TAB_NAME],INFO)
-                        return True
-                    # 如果往同一个方向跑了5次,聚焦坐标和目标坐标的位置都没变化,则说明目标可能为非聚焦,跑不动了
-                    if focusCount<5:
-                        direction = self.dm.getTargetDirection(focusedBounds, destBounds, findDirections)
-                        self.dm.goOneStep(self.pyU, direction, keyType)
-                        isSameFocused = self.dm.isSameBounds(focusedBoundsPre,focusedBounds) # 前一次聚焦点与当前聚焦点坐标对比
-                        isSameDest = self.dm.isSameBounds(destBoundsPre,destBounds)
-                        if isSameFocused == True and isSameDest == True:
-                            focusCount += 1
-                            focusedBoundsPre = focusedBounds
-                            destBoundsPre = destBounds
-                        if focusCount == 0:
-                            focusCount += 1 # 如果focusCount=0,则将当前聚焦点和目标坐标赋值给前一次
-                            focusedBoundsPre = focusedBounds
-                            destBoundsPre = destBounds
-                    else:
-                        error(self.cls, "toDestObj", "未找到目标焦点!!!", ERROR)
-                        return False
-
-                except Exception,e:
-                    info(self.cls,"toDestObj","未获取到目标/焦点对象坐标:count:%d,Max_Try:%d,directIndex:%d"%(count,Max_Try,directIndex),INFO)
-                    traceback.print_exc()
-                    # 出现控件出现一半的时候,获取控件信息会报错
-                    if count < Max_Try:
-                        count = count + 1
-                    elif count >= Max_Try and directIndex < findDirections.__len__() -1:
-                        count=0
-                        Max_Try *= 2
-                        directIndex = directIndex + 1
-                    self.fm.pressKeyByType(findDirections[directIndex], keyType)
-            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
-            else:
-                if count < Max_Try:
-                    count = count + 1
-                elif count >= Max_Try and directIndex<findDirections.__len__() -1:
-                    count=0
-                    Max_Try *= 2
-                    directIndex = directIndex + 1
-                self.fm.pressKeyByType(findDirections[directIndex], keyType)
-
-        error(self.cls, "toDestObj", "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)), ERROR)
-        return False
-
-    '''
-    根据配置的样本焦点框和OptionView 区域坐标,计算是否聚焦
-    :param focusedBounds 当前电视焦点框坐标
-    :param destBounds 当前电视目标坐标
-    :param fsamBounds 取样时的焦点框坐标
-    :param osamBounds 取样时的目标坐标
-    :return True/False  True:焦点在目标上;False:焦点不在目标上
-    '''
-    def hasFocusDest(self,focusedBounds, destBounds, fsamBounds=[],osamBounds=[]):
-        # print "hasFocusDest,focusedBounds,destBounds,fsamBounds,osamBounds:",focusedBounds, destBounds,fsamBounds,osamBounds
-        if fsamBounds.__len__() < 1 or osamBounds.__len__()<1:
-            return self.dm.isHasAnotherBounds(focusedBounds, destBounds)
-        else:#在焦点框bounds特别大时,同时包含多个目标optionView时,用此方法判断是否选中。
-            focusBounds = UIParamUtil.atxBounds2UATBounds(focusedBounds)
-            destBounds = UIParamUtil.atxBounds2UATBounds(destBounds)
-            print "fsamBounds,osamBounds"
-            sdrate = self.calPointAngle(fsamBounds[0], osamBounds[0])
-            sdLine = self.calPointLine(fsamBounds[0], osamBounds[0])
-            print "focusBounds,destBounds"
-            drate = self.calPointAngle(focusBounds[0],destBounds[0])
-            dLine = self.calPointLine(focusBounds[0],destBounds[0])
-            if abs(drate - sdrate) < 5 and abs(dLine - sdLine) < 30:
-                return True
-            else:
-                return False
-    '''
-    计算点p2相对点p1的角度
-    '''
-    def calPointAngle(self, p1, p2):
-        angle = 0.0
-        print "calPointAngle,p1,p2:", p1,p2
-        dx = float(p2[0])-float(p1[0])
-        dy = float(p2[1])-float(p1[1])
-        print "calPointAngle,dx,dy:",dx,dy
-        if dx == 0 and dy >= 0:
-            return 90
-        elif dx == 0 and dy < 0:
-            return 180
-
-        radian = math.atan(dy/dx) #计算出弧度值
-        angle = 180 * radian/math.pi
-        print "calPointAngle,angle,radian:",angle,radian
-        return angle
-
-    def calPointLine(self, p1,p2):
-        print "calPointLine,p1,p2:", p1,p2
-        dx = float(p2[0])-float(p1[0])
-        dy = float(p2[1])-float(p1[1])
-        line = round(math.sqrt(dx*dx + dy*dy),1)
-        print "calPointLine,line:", line
-        return line
-
-    '''
-    检测option组件,在电视上是否被选中了
-    :param option 数据字典
-    :param layoutUIParam, parent的layout组件定位字典
-    :param focusUIObj, 焦点组件
-    :return -1:未进入option的页面,找不到option UIObject;0:进入了option的页面,未选中option;1 已经选中option
-        -2:代表未找到焦点
-    '''
-    def checkOptionChoose(self, option, layoutUIParam, focusUIObj):
-        optionUIObj = self.getOptionUIObj(option, layoutUIParam)
-        if optionUIObj is None or optionUIObj.exists() is False:
-            return -1
-        if focusUIObj is None or focusUIObj.exists() is False:
-            return -2
-        focusParam = self.getFPFromOption(option)
-        if focusParam is None:
-            return 1
-        try:
-            focusedBounds = focusUIObj.info()["bounds"]
-            destBounds = optionUIObj.info()['bounds']
-        except Exception,e:
-            return 0
-        fsamBounds = focusParam[UATTree.View_sambounds]
-        osamBounds = option[UATTree.TAB_OPTION_VIEW][UATTree.View_sambounds]
-        if self.hasFocusDest(focusedBounds, destBounds,fsamBounds, osamBounds):
-            return 1
-        else:
-            return 0
-
-    def checkOptionExist(self, option, parent):
-        # print "checkOptionExist,option:",option
-        moveKey = parent[UATTree.TAB_MOVE_KEY]
-        optionName = option['name']
-        print "checkOptionExist,moveKey:", moveKey
-        Max_Try = moveKey[UATTree.Max_Try]
-        if Max_Try == "":
-            Max_Try = 1
-        else:
-            Max_Try = int(Max_Try)
-        Reverse_Max_Try = Max_Try * 2
-        print "Max_Try:", Max_Try
-        count = 0
-        Reversecount = 0
-        lastObj = ""
-
-        while (True):
-            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
-                break
-            # 如果找到目标直接返回
-            debug(self.cls,"checkOptionExist","optionView:%s"%option,DEBUG)
-            opUIParam = UIParamUtil.setObjParam(option["optionView"])
-            ###
-            try:
-                opOBJ = self.pyU.getUiObject2(opUIParam)
-                opOBJInfo = opOBJ.info
-                debug(self.cls,"checkOptionExist","opOBJInfo: %s"%opOBJInfo,DEBUG)
-                break
-            except Exception,e:
-                opOBJ = None
-            if count < Max_Try:
-                flag = 1
-                count += 1
-                print "now count:", count
-            else:
-                flag = 0
-                Reversecount += 1
-                print "now Reversecount:", Reversecount
-            self.runnerCommand.executeMoveKey(moveKey, flag)
-        if opOBJ is not None and opOBJ.exists():
-            info(self.cls, "checkOptionExist", "当前页面已找到option: %s" % optionName, INFO)
-            return True
-        info(self.cls, "checkOptionExist", "注意!!!当前页面未找到option: %s" % optionName, INFO)
-        return False
-
-    '''
-    根据option配置的焦点方式,返回当前页面焦点组件
-    :param option: option字典
-    :param layoutUIObj : option对应组件的根组件
-    :return retCode,UIObject对象:reCode=0表示默认为不需要选中状态,retCode=1表示option默认为需要选中状态
-    '''
-    Focus_Need = 1
-    Focus_Need_Not = 0
-    def getChooseUIObj(self, option, layoutUIObj = None):
-        debug(self.cls, "getChooseUIObj", "option name:"+option[UATTree.TAB_NAME],DEBUG)
-        focusObjParam = self.getFPObjParam(option)
-        debug(self.cls, "getChooseUIObj", "focusObjParam:"+str(focusObjParam)+"; layoutUIObj:"+str(layoutUIObj),DEBUG)
-        if focusObjParam.__len__() <= 0:
-            return self.Focus_Need_Not,None
-        if layoutUIObj is None:
-            return self.Focus_Need,self.pyU.getUiObject2(focusObjParam)
-        else:
-            return self.Focus_Need,layoutUIObj.child(**focusObjParam)
-
-    '''
-    根据parent字典,检索parent是否有一个option被选中,任何一个备选中,返回option的UIObject
-    :param parent: parent 字典
-    :param needFocus: 目标option需要聚焦,则优先找焦点,再处理select type为no情况
-    :return retCode,UIObject对象:reCode=0表示默认为不需要选中状态,retCode=1表示option默认为需要选中状态
-    '''
-    def getChooseUIObjP(self, parent, layoutUIParam, needFocus = True):
-        if layoutUIParam.__len__() > 0:
-            layoutUIObj = self.pyU.getUiObject2(layoutUIParam)
-        else:
-            layoutUIObj = None
-        # 筛选焦点定位类型,减少重复判断
-        focusOptionDict = self.reMuFocusOption(parent[UATTree.TAB_OPTION])
-        debug(self.cls,"getChooseUIObjP","focusOptionDict:"+str(focusOptionDict.keys()),DEBUG)
-        for optionName in focusOptionDict:
-            option = focusOptionDict[optionName]
-            retCode, uiObj = self.getChooseUIObj(option, layoutUIObj)
-            if retCode == self.Focus_Need_Not and needFocus is False:
-                return self.Focus_Need_Not, None
-            elif retCode == self.Focus_Need and uiObj is not None and uiObj.exists():
-                return retCode, uiObj
-        return self.Focus_Need,None
-
-    def reMuFocusOption(self, optionDict):
-        retDict = {}
-        for optionName in optionDict:
-            option = optionDict[optionName]
-            focusObjParam = self.getFPObjParam(option)
-            if retDict.__len__() == 0:
-                retDict[optionName] = option
-                continue
-            isSame = False
-            for retName in retDict:
-                retOption = optionDict[retName]
-                retFocusObjParam = self.getFPObjParam(option)
-                if UIParamUtil.cmpObjParam(focusObjParam, retFocusObjParam):
-                    isSame = True
-                    break
-            if isSame is False:
-                retDict[optionName] = option
-        return retDict
-
-
-    '''
-    根据option和layoutUIParam参数,获取option UIObject对象
-    layoutUIParam参数有效,则在layout里查找子组件option,否则全界面查找子组件。
-    '''
-    def getOptionUIObj(self, option, layoutUIParam):
-        debug(self.cls, "getOptionUIObj","OptionView:"+str(option[UATTree.TAB_OPTION_VIEW]),DEBUG)
-        destUIParam = UIParamUtil.setObjParam(option[UATTree.TAB_OPTION_VIEW])
-        debug(self.cls, "getOptionUIObj", "destUIParam:" + str(destUIParam), DEBUG)
-        destUITextExist = False
-        try:
-            destUIText = destUIParam['text']
-            destUITextExist = True
-        except Exception, e:
-            info(self.cls, "getOptionUIObj", "目标对象%s的optionView无text属性"%option[UATTree.TAB_NAME], INFO)
-        if destUITextExist is True:
-            #####################
-            if type(destUIParam['text']) is type([]):
-                destUIObjectExist = False
-                tempTextList = destUIParam['text']
-                txtCount = 0
-                for txt in tempTextList:
-                    destUIParam['text'] = txt
-                    uix = self.pyU.dump_hierarchy()
-                    if txt not in uix:
-                        continue
-                    if layoutUIParam.__len__() > 0:
-                        layoutUIObj = self.pyU.getUiObject2(layoutUIParam)
-                        try:
-                            destUIObject = layoutUIObj.child(**destUIParam)
-                            destUIObjectInfo = destUIObject.info
-                            destUIObjectExist = True
-                            info(self.cls, "getOptionUIObj", "文本%s对应的option对象已找到" % txt, INFO)
-                        except Exception, e:
-                            nextText = tempTextList[txtCount + 1]
-                            error(self.cls, "getOptionUIObj", "文本%s对应的option对象未找到,匹配下一个文本%s" % (txt, nextText), ERROR)
-                        if destUIObjectExist is True:
-                            break
-                    else:
-                        try:
-                            destUIObject = self.pyU.getUiObject2(destUIParam)
-                            destUIObjectInfo = destUIObject.info
-                            destUIObjectExist = True
-                            info(self.cls, "getOptionUIObj", "文本%s对应的option对象已找到" % txt, INFO)
-                        except Exception, e:
-                            nextText = tempTextList[txtCount + 1]
-                            error(self.cls, "getOptionUIObj", "文本%s对应的option对象未找到,匹配下一个文本%s" % (txt, nextText), ERROR)
-                        if destUIObjectExist is True:
-                            break
-                    txtCount += 1
-            else:
-                if layoutUIParam.__len__() > 0:
-                    layoutUIObj = self.pyU.getUiObject2(layoutUIParam)
-                    destUIObject = layoutUIObj.child(**destUIParam)
-                else:
-                    destUIObject = self.pyU.getUiObject2(destUIParam)
-            #########################
-        else:
-            if layoutUIParam.__len__() > 0:
-                layoutUIObj = self.pyU.getUiObject2(layoutUIParam)
-                destUIObject = layoutUIObj.child(**destUIParam)
-            else:
-                destUIObject = self.pyU.getUiObject2(destUIParam)
-        return destUIObject
-
-    '''
-    检测parent,在电视上是否被选中了。一个option被选中,表示选中
-    :param option 数据字典
-    :return
-        -3:代表UIView判断未通过;
-        -2:代表选中了parent,但未找到焦点;
-        -1:未进入parent的页面,找不到parent layout;
-         0:进入了parent的页面,未选中parent;
-         1:已经选中parent
-    '''
-    def checkParentChoose(self, parent):
-        debug(self.cls, "checkParentChoose", "parent:" + parent[UATTree.TAB_NAME], DEBUG)
-        chooseTypeDict = {}
-        layoutParam = parent[UATTree.TAB_LAYOUT]
-        layoutUIParam = UIParamUtil.setObjParam(layoutParam)
-        layoutResId = layoutParam[UATTree.Layout_ID]
-        uiView = parent[UATTree.TAB_UI_VIEW]
-        uiViewResId = uiView[UATTree.View_ID]
-        uiViewText = uiView[UATTree.View_Text]
-        uiViewDesc = uiView[UATTree.View_Desc]
-        # print "checkParentChoose,layoutUIParam:",layoutUIParam
-        if layoutResId == "" and uiViewResId == "" and uiViewText == "" and uiViewDesc == "":
-            debug(self.cls, "checkParentChoose",
-                  "Warning:Parent %s的Layout resId和UIView信息获取失败!!请注意检查UATree文件!!!" % parent[UATTree.TAB_NAME], DEBUG)
-            return -1
-        elif layoutUIParam.__len__() > 0:
-            # 如果存在UIView信息,则先判断UIView
-            isExist = self.checkUIViewExist(uiView)
-            if isExist is False:
-                info(self.cls, "checkParentChoose", "当前页面不存在Parent:%s的UIView组件,判断该界面非此parent" % parent[UATTree.TAB_NAME],
-                     INFO)
-                return -3
-            else:
-                debug(self.cls, "checkParentChoose", "已识别出Parent:%s的UIView组件" % parent[UATTree.TAB_NAME], DEBUG)
-            # 判断Layout是否存在
-            layoutUIObj = self.pyU.getUiObject2(layoutUIParam)
-            if layoutUIObj is None or layoutUIObj.exists() is False:
-                info(self.cls, "checkParentChoose", "parent %s layout 不存在 "%parent[UATTree.TAB_NAME], INFO)
-                return -1
-            debug(self.cls, "checkParentChoose", "parent %s layout 存在"%parent[UATTree.TAB_NAME], DEBUG)
-
-            # 获取焦点组件,判断是否在layout中
-            retCode, focusObj = self.getChooseUIObjP(parent, layoutUIParam, needFocus=False)
-            if retCode == self.Focus_Need_Not:
-                debug(self.cls, "checkParentChoose",
-                     "已找到目标parent %s,不需要选中。" % (parent['name']), DEBUG)
-            elif focusObj is not None and focusObj.exists():
-                debug(self.cls, "checkParentChoose",
-                     "已找到目标parent %s,焦点在Layout中。" % (parent['name']), DEBUG)
-            else:
-                info(self.cls, "checkParentChoose",
-                     "已找到目标parent %s,但是焦点不在Layout中。" % (parent['name']), INFO)
-                return -2
-            # 判断parent中存在一个option,则标识parent被选中。注意:为了快速消失界面相应
-            if layoutParam[UATTree.Layout_Limit] <> 1:
-                layoutUIParam = {}
-            optionUIObj = None
-            optionExist = False
-            for optionName in parent["option"]:
-                option = parent["option"][optionName]
-                ret = self.checkOptionExist(option,parent)
-                if ret is True:
-                    info(self.cls, "checkParentChoose",
-                         "已找到目标parent %s,识别出该parent下的option %s。" % (parent['name'], option['name']),
-                         INFO)
-                    return 1
-            info(self.cls, "checkParentChoose",
-                 "已找到目标parent %s,但是parent下的option未被发现。" % (parent['name']), INFO)
-            return 0
-        else:
-            isExist = self.checkUIViewExist(uiView)
-            if not isExist:
-                return -3
-            else:
-                info(self.cls, "checkParentChoose",
-                     "识别出parent %s的UIView参数,判断处于该界面中" % (parent['name']), INFO)
-                return 1
-
-    '''
-    检测某个弹窗是否存在。
-    弹窗相关的参数放在UIView中配置。
-    当存在resId参数时,使用resId获取对象并获取文本,再与text做比较;
-    当不存在resId参数时,使用text直接获取对象,判断对象是否存在。
-    与checkParentChoose判断不一致,做特殊处理。
-    :return 整型:0代表不存在,1代表存在
-    '''
-    def checkDialogExist(self, dialog):
-        debug(self.cls, "checkDialogExist", "dialog:" + dialog["name"], DEBUG)
-        dialogText = dialog[UATTree.TAB_UI_VIEW][UATTree.UIView_Text]
-        dialogResId = dialog[UATTree.TAB_UI_VIEW][UATTree.UIView_ID]
-        # print "checkDialogExist.dialogText:", dialogText
-        # print "checkDialogExist.dialogResId:", dialogResId
-        if dialogResId != "":
-            textObj = self.pyU.getUiObject(resourceId=dialogResId)
-            if textObj.exists:
-                objText = textObj.info['text']
-                # print "checkDialogExist.objText:", objText
-                if dialogText in objText:
-                    return 1
-                else:
-                    return 0
-            else:
-                return 0
-        else:
-            textObj = self.pyU.getUiObject(text=dialogText)
-            if textObj.exists:
-                return 1
-            else:
-                return 0
-
-    def checkUIViewExist(self, uiView):
-        #uiview未配置默认为检测通过
-        if uiView.__len__() == 0:
-            return True
-        uiviewObjParam = UIParamUtil.setObjParam(uiView)
-        uiViewObj = self.pyU.getUiObject2(uiviewObjParam)
-        if uiViewObj is None or uiViewObj.exists is False:
-            return False
-        else:
-            return True
-
-if __name__ == "__main__":
-    uatPathManage = UATPathManage()
-    pyU = PyUIAutomator()
-    dm = DirectionManageAndroid()
-    fm = FocusManageAndroid(pyU, dm)
-    runnerCmd = UATRunnerCommand(uatPathManage, pyU, dm, fm)
-    focusCmd = FocusCommand(runnerCmd)
-
-    # ret = focusCmd.calPointAngle([0,0],[244,151])
-    ret = focusCmd.calPointAngle([244,151],[107,641])
-
-    # focusCmd.strToBounds('[801,116][1280,180]')
-
-    # option = focusCmd.runnerCommand.uatPathManage.uatData.getOption("av_devices_settings")
-    # parent = focusCmd.runnerCommand.uatPathManage.uatData.getParentByOption(option)
-    # focusCmd.focusOptionView(parent, option)
-
-    # print "dump_hierarchy 0:",time.time()
-    # print focusCmd.pyU.dump_hierarchy()
-    # print "dump_hierarchy 1:",time.time()
+# -*- coding:utf-8 -*-
+import time,sys,os
+from UAT_tree import UATTree
+from UAT_log import error,info,debug
+from UAT_PathManage import UATPathManage
+from UIParamUtil import UIParamUtil
+# from UAT_runnerCommand import UATRunnerCommand
+from ssat_sdk.python_uiautomator import PyUIAutomator,DirectionManageAndroid,FocusManageAndroid
+import math
+import traceback
+
+ERROR = True
+INFO = True
+DEBUG = True  # 上传SVN版本此参数关闭
+'''
+采用uiautomator技术,处理界面定位问题,用于移动焦点到目标组件上
+'''
+class FocusCommand():
+    cls = "FocusCommand"
+    UIObjParam = UIParamUtil.UIObjParam
+    UATParamMap = UIParamUtil.UATParamMap
+    def __init__(self, runnerCommand):
+        self.runnerCommand = runnerCommand
+        self.pyU = PyUIAutomator()
+        self.dm = DirectionManageAndroid()
+        self.fm = FocusManageAndroid(self.pyU, self.dm)
+        self.inLayout= False
+
+    '''
+    解析option的焦点组件参数,返回focusView或者focus-select组件参数
+    :param option: option字典
+    :return 返回 focus焦点参数字典,采用setObjParam可以转换成UIObject识别需要的参数。
+    如果为None,则表示不需要焦点选中。
+    '''
+    def getFPFromOption(self, option):
+        focusParam = None
+        fsParam = option[UATTree.TAB_FOCUS_SELECT]
+        fvParam = option[UATTree.TAB_FOCUSE_VIEW]
+        if fsParam[UATTree.FS_Type].__len__() > 1:
+            if fsParam[UATTree.FS_Type].lower() == "focus":
+                fsParam["focus"] = True
+                focusParam = fsParam
+            elif fsParam[UATTree.FS_Type].lower() == "select":
+                fsParam["select"] = True
+                focusParam = fsParam
+            elif fsParam[UATTree.FS_Type].lower() == "no":
+                focusParam = None
+            else:
+                error(self.cls, "getFPFromOption", "Option %s focus-select参数异常。" % option[UATTree.TAB_NAME], ERROR)
+        else:
+            focusParam = fvParam
+        return focusParam
+
+    '''
+    解析option的焦点组件参数,返回focusView或者focus-select组件参数.
+    在getFPFromOption返回值上,采用setObjParam转换成UIObject识别需要的参数。
+    :param option: option字典
+    :return 返回 focus焦点UIObject参数字典.
+    '''
+    def getFPObjParam(self, option):
+        focusParam = self.getFPFromOption(option)
+        fpObjParam = UIParamUtil.setObjParam(focusParam)
+        return fpObjParam
+
+    '''
+    解析存储在UATree里的moveKey字典,得到按键类型、键值数组、最大尝试次数
+    :param moveKey, parent字典中moveKey字典
+    :param optionCount: moveKey对应的parent的option个数
+    :return keyType, keyCodeList, Max_Trye
+    '''
+    def parseMoveKey(self,moveKey, optionCount):
+        if moveKey[UATTree.Max_Try] != "":
+            Max_Try = int(moveKey[UATTree.Max_Try])
+        else:
+            Max_Try = optionCount
+        findDirection = ["down","up"]
+        keyType = UATTree.Key_Event
+        if moveKey[UATTree.Key_Event].__len__() > 1:
+            findDirection = moveKey[UATTree.Key_Event]
+            keyType = UATTree.Key_Event
+        elif moveKey[UATTree.Key_IR].__len__() > 1:
+            findDirection = moveKey[UATTree.Key_IR]
+            keyType = UATTree.Key_IR
+        elif moveKey[UATTree.Key_Input].__len__() > 1:
+            inputCmd = moveKey[UATTree.Key_Input]
+            #TODO input情况的处理
+            return None,None,None
+        else:
+            return None,None,None
+        return keyType, findDirection, Max_Try
+    '''
+    在parent界面,将焦点移动到目标option上。
+    焦点定位:根据layout是不是限制焦点范围,进行焦点组件寻找,焦点组件类型:focus、select、focusView、long-click
+    目标定位:纯粹是根据optionView配置组建坐标定位
+    :param parent:当前电视界面的parent字典
+    :param option:目标option字典
+    :return True/False
+    '''
+    def focusOptionView(self, parent, option,chooseType):
+        #是否采用layout限制焦点判断范围
+        layout = parent[UATTree.TAB_LAYOUT]
+        print "focusOptionView,layout:",layout
+        self.inLayout = layout[UATTree.Layout_Limit]
+        # 找到目标optionView参数
+        layoutUIObj = {}
+        optionUIObjParam = UIParamUtil.setObjParam(option[UATTree.TAB_OPTION_VIEW])
+        if self.inLayout == 1:
+            layoutUIObjParam = UIParamUtil.setObjParam(layout)
+        else:
+            layoutUIObjParam = {}
+        # 获取move key按键
+        moveKey = parent[UATTree.TAB_MOVE_KEY]
+        keyType, findDirection,Max_Try = self.parseMoveKey(moveKey, parent[UATTree.TAB_OPTION].__len__())
+        if keyType is None:
+            error(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " option 读取 move_key失败", ERROR)
+            return False
+        #获取optionview的View_sambounds参数
+        osamBounds = option[UATTree.TAB_OPTION_VIEW][UATTree.View_sambounds]
+        #获取焦点view参数
+        focusParam = self.getFPFromOption(option)
+        focusUIParam = UIParamUtil.setObjParam(focusParam)
+        if focusParam is None:
+            fsamBounds = []
+        else:
+            fsamBounds = focusParam[UATTree.View_sambounds]
+        if chooseType.lower()== 'no':
+            return self.getNoFocusedObj(option, Max_Try, findDirection, keyType, layoutUIObjParam)
+        else:
+            return self.toDestObj(parent, option, Max_Try, findDirection, keyType, layoutUIObjParam,fsamBounds,osamBounds)
+
+    '''
+    传入uiautomator需要的目标组件参数,获取无聚焦属性的option对象  
+    '''
+    def getNoFocusedObj(self,option, Max_Try, findDirection, keyType, layoutUIObjParam):
+        count = 0
+        directIndex = 0
+        while(True):
+            if count >= Max_Try and directIndex >= findDirection.__len__()-1:
+                break
+            time.sleep(0.1)
+            destUIObject = self.getOptionUIObj(option, layoutUIObjParam)
+            if destUIObject and destUIObject.exists():
+                info(self.cls,"getNoFocusedObj","找到目标option %s"%option[UATTree.TAB_NAME],INFO)
+                return True
+            else:
+                if count < Max_Try:
+                    count = count + 1
+                elif count >= Max_Try and directIndex<findDirection.__len__() -1:
+                    count=0
+                    Max_Try *= 2
+                    directIndex = directIndex + 1
+                self.fm.pressKeyByType(findDirection[directIndex], keyType)
+        error(self.cls, "getNoFocusedObj", "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)), ERROR)
+        return False
+
+    '''
+    传入uiautomator需要的目标组件参数和焦点框参数,用于寻找目标。
+    '''
+    def toDestObj(self, parent, option, Max_Try=10, findDirections=["down","up"], keyType=UATTree.Key_Event,
+                  layoutUIParam={}, fsamBounds=[],osamBounds=[]):
+        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
+        print "toDestObj,enter:",Max_Try,findDirections,keyType,fsamBounds,osamBounds
+        count = 0
+        Max_Try = Max_Try
+        directIndex = 0
+        focusCount = 0  # 当目标和聚焦点在一个页面同时存在时,从当前聚焦点到目标的移动次数
+        focusedBoundsPre = {u'top': 0, u'left': 0, u'right': 0, u'bottom': 0}  # 上一轮聚焦点的坐标
+        destBoundsPre = {u'top': 0, u'left': 0, u'right': 0, u'bottom': 0}  # 上一轮目标的坐标
+        while (True):
+            if count >= Max_Try and directIndex >= findDirections.__len__()-1:
+                break
+            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
+            time.sleep(0.1)
+            destUIObject = self.getOptionUIObj(option,layoutUIParam)
+            focusParam = self.getFPFromOption(option)
+            #option是否需要聚焦
+            needFocus = True
+            if focusParam.has_key(UATTree.FS_Type) and \
+                focusParam[UATTree.FS_Type] == "no":
+                needFocus = False
+            if destUIObject and destUIObject.exists(): # 有聚焦属性的option存在不代表已聚焦,无聚焦属性的option存在即当作已聚焦
+                try:
+                    retCode, focusedUIObject = self.getChooseUIObjP(parent, layoutUIParam,needFocus=needFocus)
+                    debug(self.cls, "toDestObj", "Parent %s 当前聚焦框retCode:%d" % (parent[UATTree.TAB_NAME],retCode), DEBUG)
+                    #如果焦点不存在,只需要判断optionView存在即可算是聚焦成功
+                    if retCode == self.Focus_Need_Not:
+                        return True
+                    #根据已获取到destUIObject和focusedUIObject,进行是否选中目标option判断,
+                    #未选中,则继续利用moveKey查找
+                    focusedBounds = focusedUIObject.info['bounds']
+                    info(self.cls,"toDestObj","当前聚焦框所在坐标:%s"%focusedBounds,INFO)
+                    destBounds = destUIObject.info['bounds']
+                    info(self.cls, "toDestObj", "已寻找%s次" % focusCount, INFO)
+                    info(self.cls,"toDestObj","目标Option %s坐标:%s"%(option[UATTree.TAB_NAME], destBounds),INFO)
+                    if self.hasFocusDest(focusedBounds, destBounds, fsamBounds=fsamBounds, osamBounds=osamBounds):
+                        info(self.cls,"toDestObj","成功聚焦到目标焦点:%s"%option[UATTree.TAB_NAME],INFO)
+                        return True
+                    # 如果往同一个方向跑了5次,聚焦坐标和目标坐标的位置都没变化,则说明目标可能为非聚焦,跑不动了
+                    if focusCount<5:
+                        direction = self.dm.getTargetDirection(focusedBounds, destBounds, findDirections)
+                        self.dm.goOneStep(self.pyU, direction, keyType)
+                        isSameFocused = self.dm.isSameBounds(focusedBoundsPre,focusedBounds) # 前一次聚焦点与当前聚焦点坐标对比
+                        isSameDest = self.dm.isSameBounds(destBoundsPre,destBounds)
+                        info(self.cls,"toDestObj","focusedBoundsPre:%s"%focusedBoundsPre,INFO)
+                        info(self.cls, "toDestObj", "focusedBounds:%s" % focusedBounds, INFO)
+                        if isSameFocused == True and isSameDest == True:
+                            focusCount += 1
+                            focusedBoundsPre = focusedBounds
+                            destBoundsPre = destBounds
+                            info(self.cls,"toDestObj.focusCount","focusCount:%s"%focusCount,INFO)
+                            continue
+                        if focusCount == 0:
+                            focusCount += 1 # 如果focusCount=0,则将当前聚焦点和目标坐标赋值给前一次
+                            focusedBoundsPre = focusedBounds
+                            destBoundsPre = destBounds
+                            info(self.cls,"toDestObj.focusCount.countStart")
+                            continue
+                    else:
+                        error(self.cls, "toDestObj", "未找到目标焦点!!!", ERROR)
+                        return False
+
+                except Exception,e:
+                    info(self.cls,"toDestObj","未获取到目标/焦点对象坐标:count:%d,Max_Try:%d,directIndex:%d"%(count,Max_Try,directIndex),INFO)
+                    traceback.print_exc()
+                    # 出现控件出现一半的时候,获取控件信息会报错
+                    if count < Max_Try:
+                        count = count + 1
+                    elif count >= Max_Try and directIndex < findDirections.__len__() -1:
+                        count=0
+                        Max_Try *= 2
+                        directIndex = directIndex + 1
+                    self.fm.pressKeyByType(findDirections[directIndex], keyType)
+            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
+            else:
+                if count < Max_Try:
+                    count = count + 1
+                elif count >= Max_Try and directIndex<findDirections.__len__() -1:
+                    count=0
+                    Max_Try *= 2
+                    directIndex = directIndex + 1
+                self.fm.pressKeyByType(findDirections[directIndex], keyType)
+
+        error(self.cls, "toDestObj", "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)), ERROR)
+        return False
+
+    '''
+    根据配置的样本焦点框和OptionView 区域坐标,计算是否聚焦
+    :param focusedBounds 当前电视焦点框坐标
+    :param destBounds 当前电视目标坐标
+    :param fsamBounds 取样时的焦点框坐标
+    :param osamBounds 取样时的目标坐标
+    :return True/False  True:焦点在目标上;False:焦点不在目标上
+    '''
+    def hasFocusDest(self,focusedBounds, destBounds, fsamBounds=[],osamBounds=[]):
+        # print "hasFocusDest,focusedBounds,destBounds,fsamBounds,osamBounds:",focusedBounds, destBounds,fsamBounds,osamBounds
+        if fsamBounds.__len__() < 1 or osamBounds.__len__()<1:
+            return self.dm.isHasAnotherBounds(focusedBounds, destBounds)
+        else:#在焦点框bounds特别大时,同时包含多个目标optionView时,用此方法判断是否选中。
+            focusBounds = UIParamUtil.atxBounds2UATBounds(focusedBounds)
+            destBounds = UIParamUtil.atxBounds2UATBounds(destBounds)
+            print "fsamBounds,osamBounds"
+            sdrate = self.calPointAngle(fsamBounds[0], osamBounds[0])
+            sdLine = self.calPointLine(fsamBounds[0], osamBounds[0])
+            print "focusBounds,destBounds"
+            drate = self.calPointAngle(focusBounds[0],destBounds[0])
+            dLine = self.calPointLine(focusBounds[0],destBounds[0])
+            if abs(drate - sdrate) < 5 and abs(dLine - sdLine) < 30:
+                return True
+            else:
+                return False
+    '''
+    计算点p2相对点p1的角度
+    '''
+    def calPointAngle(self, p1, p2):
+        angle = 0.0
+        print "calPointAngle,p1,p2:", p1,p2
+        dx = float(p2[0])-float(p1[0])
+        dy = float(p2[1])-float(p1[1])
+        print "calPointAngle,dx,dy:",dx,dy
+        if dx == 0 and dy >= 0:
+            return 90
+        elif dx == 0 and dy < 0:
+            return 180
+
+        radian = math.atan(dy/dx) #计算出弧度值
+        angle = 180 * radian/math.pi
+        print "calPointAngle,angle,radian:",angle,radian
+        return angle
+
+    def calPointLine(self, p1,p2):
+        print "calPointLine,p1,p2:", p1,p2
+        dx = float(p2[0])-float(p1[0])
+        dy = float(p2[1])-float(p1[1])
+        line = round(math.sqrt(dx*dx + dy*dy),1)
+        print "calPointLine,line:", line
+        return line
+
+    '''
+    检测option组件,在电视上是否被选中了
+    :param option 数据字典
+    :param layoutUIParam, parent的layout组件定位字典
+    :param focusUIObj, 焦点组件
+    :return -1:未进入option的页面,找不到option UIObject;0:进入了option的页面,未选中option;1 已经选中option
+        -2:代表未找到焦点
+    '''
+    def checkOptionChoose(self, option, layoutUIParam, focusUIObj):
+        optionUIObj = self.getOptionUIObj(option, layoutUIParam)
+        if optionUIObj is None or optionUIObj.exists() is False:
+            return -1
+        if focusUIObj is None or focusUIObj.exists() is False:
+            return -2
+        focusParam = self.getFPFromOption(option)
+        if focusParam is None:
+            return 1
+        try:
+            focusedBounds = focusUIObj.info()["bounds"]
+            destBounds = optionUIObj.info()['bounds']
+        except Exception,e:
+            return 0
+        fsamBounds = focusParam[UATTree.View_sambounds]
+        osamBounds = option[UATTree.TAB_OPTION_VIEW][UATTree.View_sambounds]
+        if self.hasFocusDest(focusedBounds, destBounds,fsamBounds, osamBounds):
+            return 1
+        else:
+            return 0
+
+    def checkOptionExist(self, option, parent):
+        # print "checkOptionExist,option:",option
+        moveKey = parent[UATTree.TAB_MOVE_KEY]
+        optionName = option['name']
+        print "checkOptionExist,moveKey:", moveKey
+        Max_Try = moveKey[UATTree.Max_Try]
+        if Max_Try == "":
+            Max_Try = 1
+        else:
+            Max_Try = int(Max_Try)
+        Reverse_Max_Try = Max_Try * 2
+        print "Max_Try:", Max_Try
+        count = 0
+        Reversecount = 0
+        lastObj = ""
+
+        while (True):
+            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                break
+            # 如果找到目标直接返回
+            debug(self.cls,"checkOptionExist","optionView:%s"%option,DEBUG)
+            opUIParam = UIParamUtil.setObjParam(option["optionView"])
+            ###
+            try:
+                opOBJ = self.pyU.getUiObject2(opUIParam)
+                opOBJInfo = opOBJ.info
+                debug(self.cls,"checkOptionExist","opOBJInfo: %s"%opOBJInfo,DEBUG)
+                break
+            except Exception,e:
+                opOBJ = None
+            if count < Max_Try:
+                flag = 1
+                count += 1
+                print "now count:", count
+            else:
+                flag = 0
+                Reversecount += 1
+                print "now Reversecount:", Reversecount
+            self.runnerCommand.executeMoveKey(moveKey, flag)
+        if opOBJ is not None and opOBJ.exists():
+            info(self.cls, "checkOptionExist", "当前页面已找到option: %s" % optionName, INFO)
+            return True
+        info(self.cls, "checkOptionExist", "注意!!!当前页面未找到option: %s" % optionName, INFO)
+        return False
+
+    '''
+    根据option配置的焦点方式,返回当前页面焦点组件
+    :param option: option字典
+    :param layoutUIObj : option对应组件的根组件
+    :return retCode,UIObject对象:reCode=0表示默认为不需要选中状态,retCode=1表示option默认为需要选中状态
+    '''
+    Focus_Need = 1
+    Focus_Need_Not = 0
+    def getChooseUIObj(self, option, layoutUIObj = None):
+        debug(self.cls, "getChooseUIObj", "option name:"+option[UATTree.TAB_NAME],DEBUG)
+        focusObjParam = self.getFPObjParam(option)
+        debug(self.cls, "getChooseUIObj", "focusObjParam:"+str(focusObjParam)+"; layoutUIObj:"+str(layoutUIObj),DEBUG)
+        if focusObjParam.__len__() <= 0:
+            return self.Focus_Need_Not,None
+        if layoutUIObj is None:
+            return self.Focus_Need,self.pyU.getUiObject2(focusObjParam)
+        else:
+            return self.Focus_Need,layoutUIObj.child(**focusObjParam)
+
+    '''
+    根据parent字典,检索parent是否有一个option被选中,任何一个备选中,返回option的UIObject
+    :param parent: parent 字典
+    :param needFocus: 目标option需要聚焦,则优先找焦点,再处理select type为no情况
+    :return retCode,UIObject对象:reCode=0表示默认为不需要选中状态,retCode=1表示option默认为需要选中状态
+    '''
+    def getChooseUIObjP(self, parent, layoutUIParam, needFocus = True):
+        if layoutUIParam.__len__() > 0:
+            layoutUIObj = self.pyU.getUiObject2(layoutUIParam)
+        else:
+            layoutUIObj = None
+        # 筛选焦点定位类型,减少重复判断
+        focusOptionDict = self.reMuFocusOption(parent[UATTree.TAB_OPTION])
+        debug(self.cls,"getChooseUIObjP","focusOptionDict:"+str(focusOptionDict.keys()),DEBUG)
+        for optionName in focusOptionDict:
+            option = focusOptionDict[optionName]
+            retCode, uiObj = self.getChooseUIObj(option, layoutUIObj)
+            if retCode == self.Focus_Need_Not and needFocus is False:
+                return self.Focus_Need_Not, None
+            elif retCode == self.Focus_Need and uiObj is not None and uiObj.exists():
+                return retCode, uiObj
+        return self.Focus_Need,None
+
+    '''
+    根据parent字典,检索parent是否有一个option被选中,任何一个备选中,返回option的字典信息
+    :param parent: parent 字典
+    :param needFocus: 目标option需要聚焦,则优先找焦点,再处理select type为no情况
+    :return retCode,UIObject对象:reCode=0表示默认为不需要选中状态,retCode=1表示option默认为需要选中状态
+    '''
+    def getChooseOptionP(self, parent, layoutUIParam, needFocus = True):
+        if layoutUIParam.__len__() > 0:
+            layoutUIObj = self.pyU.getUiObject2(layoutUIParam)
+        else:
+            layoutUIObj = None
+        optionDict = parent[UATTree.TAB_OPTION]
+        # 筛选焦点定位类型,减少重复判断
+        focusOptionDict = self.reMuFocusOption(parent[UATTree.TAB_OPTION])
+        debug(self.cls,"getChooseUIObjP","focusOptionDict:"+str(focusOptionDict.keys()),DEBUG)
+        # 是否确认当前聚焦optionName的标识
+        # 用任意一option,获取当前聚焦的uiobj
+        optionName = focusOptionDict.keys()[0]
+        option = focusOptionDict[optionName]
+        retCode, choosedObj = self.getChooseUIObj(option, layoutUIObj)
+        if retCode == self.Focus_Need_Not and needFocus is False:
+            return self.Focus_Need_Not, None
+        elif retCode == self.Focus_Need and choosedObj is not None and choosedObj.exists():
+            for optionName in optionDict:
+                option = optionDict[optionName]
+                destUIParam = UIParamUtil.setObjParam(option[UATTree.TAB_OPTION_VIEW])
+                destObj = self.pyU.getUiObject2(destUIParam)
+                if destObj.exists():
+                    choosedBound = choosedObj.info['bounds']
+                    destBound = destObj.info['bounds']
+                    if self.dm.isHasAnotherBounds(choosedBound, destBound):
+                        return self.Focus_Need, option
+        return self.Focus_Need,None
+
+
+    def reMuFocusOption(self, optionDict):
+        retDict = {}
+        for optionName in optionDict:
+            option = optionDict[optionName]
+            focusObjParam = self.getFPObjParam(option)
+            if retDict.__len__() == 0:
+                retDict[optionName] = option
+                continue
+            isSame = False
+            for retName in retDict:
+                retOption = optionDict[retName]
+                retFocusObjParam = self.getFPObjParam(retOption)
+                if UIParamUtil.cmpObjParam(focusObjParam, retFocusObjParam):
+                    isSame = True
+                    break
+            if isSame is False:
+                retDict[optionName] = option
+        return retDict
+
+
+    '''
+    根据option和layoutUIParam参数,获取option UIObject对象
+    layoutUIParam参数有效,则在layout里查找子组件option,否则全界面查找子组件。
+    '''
+    def getOptionUIObj(self, option, layoutUIParam):
+        debug(self.cls, "getOptionUIObj","OptionView:"+str(option[UATTree.TAB_OPTION_VIEW]),DEBUG)
+        destUIParam = UIParamUtil.setObjParam(option[UATTree.TAB_OPTION_VIEW])
+        debug(self.cls, "getOptionUIObj", "destUIParam:" + str(destUIParam), DEBUG)
+        destUITextExist = False
+        try:
+            destUIText = destUIParam['text']
+            destUITextExist = True
+        except Exception, e:
+            info(self.cls, "getOptionUIObj", "目标对象%s的optionView无text属性"%option[UATTree.TAB_NAME], INFO)
+        if destUITextExist is True:
+            #####################
+            if type(destUIParam['text']) is type([]):
+                destUIObjectExist = False
+                tempTextList = destUIParam['text']
+                txtCount = 0
+                for txt in tempTextList:
+                    destUIParam['text'] = txt
+                    uix = self.pyU.dump_hierarchy()
+                    if txt not in uix:
+                        continue
+                    if layoutUIParam.__len__() > 0:
+                        layoutUIObj = self.pyU.getUiObject2(layoutUIParam)
+                        try:
+                            destUIObject = layoutUIObj.child(**destUIParam)
+                            destUIObjectInfo = destUIObject.info
+                            destUIObjectExist = True
+                            info(self.cls, "getOptionUIObj", "文本%s对应的option对象已找到" % txt, INFO)
+                        except Exception, e:
+                            nextText = tempTextList[txtCount + 1]
+                            error(self.cls, "getOptionUIObj", "文本%s对应的option对象未找到,匹配下一个文本%s" % (txt, nextText), ERROR)
+                        if destUIObjectExist is True:
+                            break
+                    else:
+                        try:
+                            destUIObject = self.pyU.getUiObject2(destUIParam)
+                            destUIObjectInfo = destUIObject.info
+                            destUIObjectExist = True
+                            info(self.cls, "getOptionUIObj", "文本%s对应的option对象已找到" % txt, INFO)
+                        except Exception, e:
+                            nextText = tempTextList[txtCount + 1]
+                            error(self.cls, "getOptionUIObj", "文本%s对应的option对象未找到,匹配下一个文本%s" % (txt, nextText), ERROR)
+                        if destUIObjectExist is True:
+                            break
+                    txtCount += 1
+            else:
+                if layoutUIParam.__len__() > 0:
+                    layoutUIObj = self.pyU.getUiObject2(layoutUIParam)
+                    destUIObject = layoutUIObj.child(**destUIParam)
+                else:
+                    destUIObject = self.pyU.getUiObject2(destUIParam)
+            #########################
+        else:
+            if layoutUIParam.__len__() > 0:
+                layoutUIObj = self.pyU.getUiObject2(layoutUIParam)
+                destUIObject = layoutUIObj.child(**destUIParam)
+            else:
+                destUIObject = self.pyU.getUiObject2(destUIParam)
+        return destUIObject
+
+    '''
+    检测parent,在电视上是否被选中了。一个option被选中,表示选中
+    :param option 数据字典
+    :return
+        -3:代表UIView判断未通过;
+        -2:代表选中了parent,但未找到焦点;
+        -1:未进入parent的页面,找不到parent layout;
+         0:进入了parent的页面,未选中parent;
+         1:已经选中parent
+    '''
+    def checkParentChoose(self, parent):
+        debug(self.cls, "checkParentChoose", "parent:" + parent[UATTree.TAB_NAME], DEBUG)
+        chooseTypeDict = {}
+        layoutParam = parent[UATTree.TAB_LAYOUT]
+        layoutUIParam = UIParamUtil.setObjParam(layoutParam)
+        layoutResId = layoutParam[UATTree.View_ID]
+        uiView = parent[UATTree.TAB_UI_VIEW]
+        uiViewResId = uiView[UATTree.View_ID]
+        uiViewText = uiView[UATTree.View_Text]
+        uiViewDesc = uiView[UATTree.View_Desc]
+        # print "checkParentChoose,layoutUIParam:",layoutUIParam
+        if layoutResId == "" and uiViewResId == "" and uiViewText == "" and uiViewDesc == "":
+            debug(self.cls, "checkParentChoose",
+                  "Warning:Parent %s的Layout resId和UIView信息获取失败!!请注意检查UATree文件!!!" % parent[UATTree.TAB_NAME], DEBUG)
+            return -1
+        elif layoutUIParam.__len__() > 0:
+            # 如果存在UIView信息,则先判断UIView
+            isExist = self.checkUIViewExist(uiView)
+            if isExist is False:
+                info(self.cls, "checkParentChoose", "当前页面不存在Parent:%s的UIView组件,判断该界面非此parent" % parent[UATTree.TAB_NAME],
+                     INFO)
+                return -3
+            else:
+                debug(self.cls, "checkParentChoose", "已识别出Parent:%s的UIView组件" % parent[UATTree.TAB_NAME], DEBUG)
+            # 判断Layout是否存在
+            layoutUIObj = self.pyU.getUiObject2(layoutUIParam)
+            if layoutUIObj is None or layoutUIObj.exists() is False:
+                info(self.cls, "checkParentChoose", "parent %s layout 不存在 "%parent[UATTree.TAB_NAME], INFO)
+                return -1
+            debug(self.cls, "checkParentChoose", "parent %s layout 存在"%parent[UATTree.TAB_NAME], DEBUG)
+
+            # 获取焦点组件,判断是否在layout中
+            retCode, focusObj = self.getChooseUIObjP(parent, layoutUIParam, needFocus=False)
+            if retCode == self.Focus_Need_Not:
+                debug(self.cls, "checkParentChoose",
+                     "已找到目标parent %s,不需要选中。" % (parent['name']), DEBUG)
+            elif focusObj is not None and focusObj.exists():
+                debug(self.cls, "checkParentChoose",
+                     "已找到目标parent %s,焦点在Layout中。" % (parent['name']), DEBUG)
+            else:
+                info(self.cls, "checkParentChoose",
+                     "已找到目标parent %s,但是焦点不在Layout中。" % (parent['name']), INFO)
+                return -2
+            # 判断parent中存在一个option,则标识parent被选中。注意:为了快速消失界面相应
+            if layoutParam[UATTree.Layout_Limit] <> 1:
+                layoutUIParam = {}
+            optionUIObj = None
+            optionExist = False
+            for optionName in parent["option"]:
+                option = parent["option"][optionName]
+                ret = self.checkOptionExist(option,parent)
+                if ret is True:
+                    info(self.cls, "checkParentChoose",
+                         "已找到目标parent %s,识别出该parent下的option %s。" % (parent['name'], option['name']),
+                         INFO)
+                    return 1
+            info(self.cls, "checkParentChoose",
+                 "已找到目标parent %s,但是parent下的option未被发现。" % (parent['name']), INFO)
+            return 0
+        else:
+            isExist = self.checkUIViewExist(uiView)
+            if not isExist:
+                return -3
+            else:
+                info(self.cls, "checkParentChoose",
+                     "识别出parent %s的UIView参数,判断处于该界面中" % (parent['name']), INFO)
+                return 1
+
+    '''
+    检测某个弹窗是否存在。
+    弹窗相关的参数放在UIView中配置。
+    当存在resId参数时,使用resId获取对象并获取文本,再与text做比较;
+    当不存在resId参数时,使用text直接获取对象,判断对象是否存在。
+    与checkParentChoose判断不一致,做特殊处理。
+    :return 整型:0代表不存在,1代表存在
+    '''
+    def checkDialogExist(self, dialog):
+        debug(self.cls, "checkDialogExist", "dialog:" + dialog["name"], DEBUG)
+        dialogText = dialog[UATTree.TAB_UI_VIEW][UATTree.View_Text]
+        dialogResId = dialog[UATTree.TAB_UI_VIEW][UATTree.View_ID]
+        # print "checkDialogExist.dialogText:", dialogText
+        # print "checkDialogExist.dialogResId:", dialogResId
+        if dialogResId != "":
+            textObj = self.pyU.getUiObject(resourceId=dialogResId)
+            if textObj.exists:
+                objText = textObj.info['text']
+                # print "checkDialogExist.objText:", objText
+                if dialogText in objText:
+                    return 1
+                else:
+                    return 0
+            else:
+                return 0
+        else:
+            textObj = self.pyU.getUiObject(text=dialogText)
+            if textObj.exists:
+                return 1
+            else:
+                return 0
+
+    def checkUIViewExist(self, uiView):
+        #uiview未配置默认为检测通过
+        if uiView.__len__() == 0:
+            return True
+        uiviewObjParam = UIParamUtil.setObjParam(uiView)
+        uiViewObj = self.pyU.getUiObject2(uiviewObjParam)
+        if uiViewObj is None or uiViewObj.exists is False:
+            return False
+        else:
+            return True
+
+
+    def moveToCheckedRedioButton(self, fsParam, findDirection="down", keyType=UATTree.Key_Event, maxTry=20):
+        count = 0
+        Reversecount = 0
+        tryCount = 0
+        while(True):
+            param={"checked":"true"}
+            checkedObj = self.pyU.getUiObject2(param)
+            if not checkedObj.exists():
+                if count < maxTry:
+                    self.fm.pressKeyByType(findDirection[1], keyType)
+                    count += 1
+                    continue
+                else:
+                    Reversecount = Reversecount + 1
+                    self.fm.pressKeyByType(self.fm.ReverseDirctionDict[findDirection], keyType)
+                    continue
+            choosedObj = self.pyU.getUiObject2(fsParam)
+            if not choosedObj.exists():
+                count += 1
+                continue
+            choosedBounds = choosedObj.info['bounds']
+            checkedBounds = checkedObj.info['bounds']
+            if self.dm.isHasAnotherBounds(choosedBounds, checkedBounds):
+                info(self.cls, "moveToCheckedRedioButton", "已聚焦至目标组件!!!", INFO)
+                return True, choosedObj
+            else:
+                direction = self.dm.getTargetDirection(choosedBounds, checkedBounds)
+                print "目标方位:",direction
+                self.dm.goOneStep(self.pyU, direction, keyType)
+                tryCount += 1
+            if tryCount >= maxTry:
+                error(self.cls, "moveToCheckedRedioButton", "已尝试至最大次数%s次,仍未能聚焦至目标组件!!!"%maxTry, error)
+                return False, choosedObj
+            if Reversecount >= maxTry:
+                error(self.cls, "moveToCheckedRedioButton", "已尝试至最大次数%s次,仍未能找到目标组件!!!"%maxTry, error)
+                return False, choosedObj
+if __name__ == "__main__":
+    uatPathManage = UATPathManage()
+    pyU = PyUIAutomator()
+    dm = DirectionManageAndroid()
+    fm = FocusManageAndroid(pyU, dm)
+    # runnerCmd = UATRunnerCommand(uatPathManage, pyU, dm, fm)
+    # focusCmd = FocusCommand(runnerCmd)
+
+    # ret = focusCmd.calPointAngle([0,0],[244,151])
+    # ret = focusCmd.calPointAngle([244,151],[107,641])
+
+    # focusCmd.strToBounds('[801,116][1280,180]')
+
+    # option = focusCmd.runnerCommand.uatPathManage.uatData.getOption("av_devices_settings")
+    # parent = focusCmd.runnerCommand.uatPathManage.uatData.getParentByOption(option)
+    # focusCmd.focusOptionView(parent, option)
+
+    # print "dump_hierarchy 0:",time.time()
+    # print focusCmd.pyU.dump_hierarchy()
+    # print "dump_hierarchy 1:",time.time()
     # uiobjg = focusCmd.pyU.getUiObject()

+ 223 - 183
ssat_sdk/UATree/UAT_menu.py

@@ -1,184 +1,224 @@
-# -*- coding:utf-8 -*-
-from UAT_runner import UATRunner
-from UAT_tree import UATTree
-
-import os, sys, time
-from UAT_log import error,info,debug
-DEBUG = True
-INFO = True
-ERROR = True
-
-class UATMenu():
-    def __init__(self):
-        self.uatRunner = UATRunner()
-        self.cls = "UATMenu"
-
-    '''
-    在目标option所在菜单下,聚焦至目标option(单层操作)
-    :param optionName 目标option名字
-    '''
-    def moveToOption(self, optionName):
-        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
-        if option is None:
-            error(self.cls, "moveToOption","Option %s not found"%optionName, ERROR)
-            return False
-        return self.uatRunner.moveToOption(option)
-
-
-    '''
-    在目标option所在菜单下,进入目标option(单层操作)
-    :param optionName 目标option名字
-    '''
-    def openMoveOption(self, optionName):
-        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
-        if option is None:
-            error(self.cls, "openMoveOption","Option %s not found"%optionName, ERROR)
-            return False
-        result = self.uatRunner.moveToOption(option)
-        if result == False:
-            return result
-        return self.uatRunner.sendOptionEnterKey(option)
-
-    '''
-    选中到目标option,如果在option所在页面,直接用moveToOption移动焦点选中,按路径执行选中步骤。
-    :param optionName 目标option名字
-    :param fromFirst 是否固定从firstparent到目标option
-    '''
-    def focusOption(self, optionName, fromFirst = False):
-        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
-        if option is None:
-            error(self.cls, "focusOption","Option %s not found"%optionName, ERROR)
-            return False
-        return self.uatRunner.focusOption(option, fromFirst)
-
-    '''
-    进入目标option,如果在option所在页面,直接用moveToOption移动焦点选中并进入,按路径执行选中步骤。
-    :param optionName 目标option名字
-    :param fromFirst 是否固定从firstparent到目标option
-    '''
-    def openOption(self, optionName, fromFirst = False, nextParentName=""):
-        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
-        if option is None:
-            error(self.cls, "openOption","Option %s not found"%optionName, ERROR)
-            return False
-        nextParent = self.uatRunner.uatPathManage.uatData.getParentDict(nextParentName)
-        if nextParent is None:
-            info(self.cls, "openOption","nextParentName %s not found"%nextParentName, INFO)
-        return self.uatRunner.openOption(option, fromFirst, nextParent)
-
-    '''
-    进入目标parent,在openOption基础上,增加了后续路过弹窗的无option界面parent。例如:usb的视频播放界面,有弹出提示框,continue或者replay。
-    实际效果,和openOption一样。
-    :param parentName 目标parent名字
-    :param fromFirst 是否固定从firstparent到目标option
-    '''
-    def openParent(self, parentName, fromFirst=False):
-        parent = self.uatRunner.uatPathManage.uatData.getParentDict(parentName)
-        if parent is None:
-            error(self.cls, "openParent","parent %s not found"%parentName, ERROR)
-            return False
-        return self.uatRunner.openParent(parent, fromFirst)
-	
-    '''
-    选中到目标option,如果在option所在页面,直接用moveToOption移动焦点选中,按路径执行选中步骤。
-    :param optionName 目标option名字
-    :param value 目标option所设的值,若为checkbox,值范围为[True,False],True表示勾选,False表示取消勾选
-	:param exitMenu 执行完以后,是否退出当前菜单
-	:param fromFirst 是否从第一层开始执行(菜单页面是否处于初始状态)
-    '''
-    def setOptionValue(self, optionName, value, exitMenu=True, fromFirst=True):
-        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
-        if option is None:
-            error(self.cls, "setOptionValue","Option %s not found"%optionName, ERROR)
-            return False
-        return self.uatRunner.setOptionValue(option, value, exitMenu, fromFirst)
-
-    '''
-    读取该option的infoView参数,找到infoView的组件信息,并根据传入参数决定对infoView信息的判断
-    :param optionName 目标option名字
-    :param cmpPic 传入的图片路径。 如果传入此参数,则会将infoView所在的坐标进行裁剪,并对传入路径的图片进行比对
-    :param cmpText 传入的文字内容。在不传入图片路径时,如果传入文字内容,则会对infoView所在坐标的文字进行识别,与传入文字比对
-            倘若cmpPic和cmpText均不传入,则只对infoView是否存在做判断。
-    :returns 返回三个参数。
-            返回第一个参数为结果状态,1为找到infoView,并且传入条件比对成功;
-                                    0为找到infoView,但传入条件比对不成功;
-                                    -1为未找到infoView,需要检查页面是否存在infoView,或者UATree中infoView配置是否正确
-            返回的第二个参数为测试时的整张截图;
-            返回的第三个参数为目标infoView所在坐标的裁剪图。
-    '''
-    def checkOptionInfoView(self, optionName, cmpText = "", cmpPic = ""):
-        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
-        if option is None:
-            error(self.cls, "checkOptionInfoView","Option %s not found"%optionName, ERROR)
-            return False
-        return self.uatRunner.checkOptionInfoView(option, cmpText = cmpText, cmpPic = cmpPic)
-
-    '''
-   读取该option的infoView参数,并返回infoView组件信息。如果返回为None,则表示未找到infoView
-    '''
-    def getOptionInfoView(self, optionName):
-        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
-        if option is None:
-            error(self.cls, "getOptionInfoView","Option %s not found"%optionName, ERROR)
-            return None
-        return self.uatRunner.getOptionInfoView(option)
-
-    '''
-    检查当前页面是否存在该option
-    '''
-    def checkOptionExist(self, optionName):
-        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
-        if option is None:
-            error(self.cls, "checkOptionExist","Option %s not found"%optionName, ERROR)
-            return False
-        return self.uatRunner.checkOptionExist(option)
-
-    def getOptionObject(self, optionName):
-        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
-        if option is None:
-            error(self.cls, "getOptionObject","Option %s not found"%optionName, ERROR)
-            return None
-        return self.uatRunner.getOptionObject(option)
-
-    '''
-    获取该option的取值范围,返回一个元组,包含左起最小值和右起最大值两个元素。
-    '''
-    def getOptionValueRange(self, optionName):
-        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
-        if option is None:
-            error(self.cls, "getOptionObject","Option %s not found"%optionName, ERROR)
-            return None
-        try:
-            vRange = (option[UATTree.TAB_TEXT_VALUE][UATTree.ValueView_Min],
-                      option[UATTree.TAB_TEXT_VALUE][UATTree.ValueView_Max])
-        except Exception,e:
-            error(self.cls, "getOptionValueRange", "option %s 获取取值范围失败!!!将返回空值!!!"%optionName, ERROR)
-            vRange = None
-        return vRange
-
-
-if __name__ == "__main__":
-    uatMenu = UATMenu()
-    # uatMenu.openOption("av_backlight_settings",nextParentName="av_backlight_settings")
-    # uatMenu.openParent("av_backlight_settings")
-    uatMenu.focusOption("av_backlight_settings")
-
-    # uatMenu.setOptionValue("av_backlight",50, fromFirst=True)
-    # uatMenu.setOptionValue("av_backlight",100, fromFirst=True)
-    # uatMenu.setOptionValue("av_picture_mode","av_movie")
-    # print uatMenu.getOptionValueRange("usb_Brightness")
-    # obj1 = uatMenu.uatRunner.pyU.getChoosedUIObject("select")
-    # print "input:"
-    # input()
-    # obj2 = uatMenu.uatRunner.pyU.getChoosedUIObject("select")
-    # if obj1 is None:
-    #     print "obj1 is None!!!"
-    # print "obj1.info:",obj1.info
-    # print "obj1 == obj2 is ", obj1.info == obj2.info
-    # print uatMenu.checkOptionInfoView("usb_mp3")
-
-    #测试打开弹窗,选择弹窗按钮.场景:1 在弹窗前面界面;2 在弹窗后面界面;
-    # print uatMenu.focusOption("usb_videoPlay_replay")
-    # print uatMenu.openParent("usb_h264AC3_replay")
-    # print uatMenu.openOption("usb_video_mpeg2-mp3")
+# -*- coding:utf-8 -*-
+from UAT_runner import UATRunner
+from UAT_tree import UATTree
+
+import os, sys, time
+from UAT_log import error,info,debug
+DEBUG = True
+INFO = True
+ERROR = True
+
+class UATMenu():
+    def __init__(self):
+        self.uatRunner = UATRunner()
+        self.cls = "UATMenu"
+
+    '''
+    在目标option所在菜单下,聚焦至目标option(单层操作)
+    :param optionName 目标option名字
+    '''
+    def moveToOption(self, optionName):
+        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
+        if option is None:
+            error(self.cls, "moveToOption","Option %s not found"%optionName, ERROR)
+            return False
+        return self.uatRunner.moveToOption(option)
+
+
+    '''
+    在目标option所在菜单下,进入目标option(单层操作)
+    :param optionName 目标option名字
+    '''
+    def openMoveOption(self, optionName):
+        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
+        if option is None:
+            error(self.cls, "openMoveOption","Option %s not found"%optionName, ERROR)
+            return False
+        result = self.uatRunner.moveToOption(option)
+        if result == False:
+            return result
+        return self.uatRunner.sendOptionEnterKey(option)
+
+    '''
+    选中到目标option,如果在option所在页面,直接用moveToOption移动焦点选中,按路径执行选中步骤。
+    :param optionName 目标option名字
+    :param fromFirst 是否固定从firstparent到目标option
+    '''
+    def focusOption(self, optionName, fromFirst = False):
+        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
+        if option is None:
+            error(self.cls, "focusOption","Option %s not found"%optionName, ERROR)
+            return False
+        return self.uatRunner.focusOption(option, fromFirst)
+
+    '''
+    进入目标option,如果在option所在页面,直接用moveToOption移动焦点选中并进入,按路径执行选中步骤。
+    :param optionName 目标option名字
+    :param fromFirst 是否固定从firstparent到目标option
+    '''
+    def openOption(self, optionName, fromFirst = False, nextParentName=""):
+        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
+        if option is None:
+            error(self.cls, "openOption","Option %s not found"%optionName, ERROR)
+            return False
+        nextParent = self.uatRunner.uatPathManage.uatData.getParentDict(nextParentName)
+        if nextParent is None:
+            info(self.cls, "openOption","nextParentName %s not found"%nextParentName, INFO)
+        return self.uatRunner.openOption(option, fromFirst, nextParent)
+
+    '''
+    进入目标parent,在openOption基础上,增加了后续路过弹窗的无option界面parent。例如:usb的视频播放界面,有弹出提示框,continue或者replay。
+    实际效果,和openOption一样。
+    :param parentName 目标parent名字
+    :param fromFirst 是否固定从firstparent到目标option
+    '''
+    def openParent(self, parentName, fromFirst=False):
+        parent = self.uatRunner.uatPathManage.uatData.getParentDict(parentName)
+        if parent is None:
+            error(self.cls, "openParent","parent %s not found"%parentName, ERROR)
+            return False
+        return self.uatRunner.openParent(parent, fromFirst)
+	
+    '''
+    选中到目标option,如果在option所在页面,直接用moveToOption移动焦点选中,按路径执行选中步骤。
+    :param optionName 目标option名字
+    :param value 目标option所设的值,若为checkbox,值范围为[True,False],True表示勾选,False表示取消勾选
+	:param exitMenu 执行完以后,是否退出当前菜单
+	:param fromFirst 是否从第一层开始执行(菜单页面是否处于初始状态)
+    '''
+    def setOptionValue(self, optionName, value, exitMenu=True, fromFirst=True):
+        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
+        if option is None:
+            error(self.cls, "setOptionValue","Option %s not found"%optionName, ERROR)
+            return False
+        return self.uatRunner.setOptionValue(option, value, exitMenu, fromFirst)
+
+    '''
+    读取该option的infoView参数,找到infoView的组件信息,并根据传入参数决定对infoView信息的判断
+    :param optionName 目标option名字
+    :param cmpPic 传入的图片路径。 如果传入此参数,则会将infoView所在的坐标进行裁剪,并对传入路径的图片进行比对
+    :param cmpText 传入的文字内容。在不传入图片路径时,如果传入文字内容,则会对infoView所在坐标的文字进行识别,与传入文字比对
+            倘若cmpPic和cmpText均不传入,则只对infoView是否存在做判断。
+    :returns 返回三个参数。
+            返回第一个参数为结果状态,1为找到infoView,并且传入条件比对成功;
+                                    0为找到infoView,但传入条件比对不成功;
+                                    -1为未找到infoView,需要检查页面是否存在infoView,或者UATree中infoView配置是否正确
+            返回的第二个参数为测试时的整张截图;
+            返回的第三个参数为目标infoView所在坐标的裁剪图。
+    '''
+    def checkOptionInfoView(self, optionName, cmpText = "", cmpPic = ""):
+        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
+        if option is None:
+            error(self.cls, "checkOptionInfoView","Option %s not found"%optionName, ERROR)
+            return False
+        return self.uatRunner.checkOptionInfoView(option, cmpText = cmpText, cmpPic = cmpPic)
+
+    '''
+   读取该option的infoView参数,并返回infoView组件信息。如果返回为None,则表示未找到infoView
+    '''
+    def getOptionInfoView(self, optionName):
+        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
+        if option is None:
+            error(self.cls, "getOptionInfoView","Option %s not found"%optionName, ERROR)
+            return None
+        return self.uatRunner.getOptionInfoView(option)
+
+    '''
+    检查当前页面是否存在该option
+    '''
+    def checkOptionExist(self, optionName):
+        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
+        if option is None:
+            error(self.cls, "checkOptionExist","Option %s not found"%optionName, ERROR)
+            return False
+        return self.uatRunner.checkOptionExist(option)
+
+    def getOptionObject(self, optionName):
+        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
+        if option is None:
+            error(self.cls, "getOptionObject","Option %s not found"%optionName, ERROR)
+            return None
+        return self.uatRunner.getOptionObject(option)
+
+    '''
+    获取该option的取值范围,返回一个元组,包含左起最小值和右起最大值两个元素。
+    '''
+    def getOptionValueRange(self, optionName):
+        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
+        if option is None:
+            error(self.cls, "getOptionValueRange","Option %s not found"%optionName, ERROR)
+            return None
+        try:
+            vRange = (option[UATTree.TAB_TEXT_VALUE][UATTree.ValueView_Min],
+                      option[UATTree.TAB_TEXT_VALUE][UATTree.ValueView_Max])
+        except Exception,e:
+            error(self.cls, "getOptionValueRange", "option %s 获取取值范围失败!!!将返回空值!!!"%optionName, ERROR)
+            vRange = None
+        return vRange
+
+
+    '''
+        获取目标option的值并返回
+    '''
+    def getOptionValue(self, optionName):
+        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
+        if option is None:
+            error(self.cls, "getOptionValue","Option %s not found"%optionName, ERROR)
+            return False, None, ""
+        ret = self.uatRunner.openOption(option, True)
+        if ret is False:
+            info(self.cls, "getOptionValue", "openOption %s Fail"%optionName, INFO)
+            self.uatRunner.runnerCmd.exitMenuByPath(self.uatRunner.executedPath)
+            return False, None, ""
+        result, optionValue, curValue = self.uatRunner.runnerCmd.getValue(option)
+        self.uatRunner.runnerCmd.exitMenuByPath(self.uatRunner.executedPath)
+        return result, optionValue, curValue
+
+    '''
+    检查option的值是否为目标value,返回boolean值
+    '''
+    def checkOptionValue(self, optionName, value):
+        result, optionValue, curValue = self.getOptionValue(optionName)
+        if optionValue == value:
+            return True
+        else:
+            return False
+
+
+
+if __name__ == "__main__":
+    uatMenu = UATMenu()
+    # uatMenu.openOption("av_backlight_settings",nextParentName="av_backlight_settings")
+    # uatMenu.openParent("av_backlight_settings")
+    # uatMenu.focusOption("av_backlight_settings")
+    # setResult = uatMenu.setOptionValue("hdmi_picture_mode", "hdmi_movie")
+    # print "setResult:", setResult
+
+    print uatMenu.getOptionValue("hdmi_picture_mode")
+
+    # uatMenu.openOption("hdmi_picture_mode")
+    # fsParam = {"focused":"true"}
+    # print uatMenu.uatRunner.runnerCmd.focusCmd.moveToCheckedRedioButton(fsParam,findDirection="up",keyType="ir")
+
+    # checkResult = uatMenu.checkOptionValue("hdmi_picture_mode", "hdmi_movie")
+    # print "checkResult:", checkResult
+
+    # uatMenu.setOptionValue("av_backlight",50, fromFirst=True)
+    # uatMenu.setOptionValue("av_backlight",100, fromFirst=True)
+    # uatMenu.setOptionValue("av_picture_mode","av_movie")
+    # print uatMenu.getOptionValueRange("usb_Brightness")
+    # obj1 = uatMenu.uatRunner.pyU.getChoosedUIObject("select")
+    # print "input:"
+    # input()
+    # obj2 = uatMenu.uatRunner.pyU.getChoosedUIObject("select")
+    # if obj1 is None:
+    #     print "obj1 is None!!!"
+    # print "obj1.info:",obj1.info
+    # print "obj1 == obj2 is ", obj1.info == obj2.info
+    # print uatMenu.checkOptionInfoView("usb_mp3")
+
+    #测试打开弹窗,选择弹窗按钮.场景:1 在弹窗前面界面;2 在弹窗后面界面;
+    # print uatMenu.focusOption("usb_videoPlay_replay")
+    # print uatMenu.openParent("usb_h264AC3_replay")
+    # print uatMenu.openOption("usb_video_mpeg2-mp3")
     # print uatMenu.openOption("usb_video_h264-ac3")

+ 687 - 608
ssat_sdk/UATree/UAT_runnerCommand.py

@@ -1,608 +1,687 @@
-# -*- coding:utf-8 -*-
-from UAT_tree import UATTree
-from ssat_sdk.python_uiautomator import PyUIAutomator,FocusManageAndroid, DirectionManageAndroid
-from UAT_log import error,debug,info
-from ssat_sdk.utils import  string_util
-from ssat_sdk.tv_operator import TvOperator
-
-from ssat_sdk.device_manage.capturecard_manager import CCardManager
-from ssat_sdk.sat_environment import getSATTmpDIR
-from ssat_sdk.pic_tool import ImageCMP
-from ssat_sdk.ocr_convert import OCRConvert
-from ssat_sdk.utils.string_util import strcmp, getDigitFromString
-from UAT_valueCommand import ValueCommand
-from UAT_focusCommand import FocusCommand
-
-import cv2 as cv
-
-import os, sys, time
-
-DEBUG = True
-INFO =True
-ERROR = True
-
-class UATRunnerCommand():
-    cls = "UATRunnerCommand"
-    def __init__(self, uatPathManage, pyU, dm, fm):
-        self.uatPathManage = uatPathManage
-        self.pyU = pyU
-        self.dm = dm
-        self.fm = fm
-        self.tvOperator = TvOperator()
-        self.eventKeyCode = self.uatPathManage.uatData.getKeyCodeDict()
-
-        self.CC = CCardManager()
-        self.imgCMP = ImageCMP()
-        self.ocrConvert = OCRConvert()
-        self.valueCmd = ValueCommand( self)
-        self.focusCmd = FocusCommand(self)
-
-    #检测option组件是不是在电视上显示了
-    def checkOptionExist(self, option, parent):
-        return self.focusCmd.checkOptionExist(option,parent)
-
-    '''
-    检测parent,在电视上是否被选中了。一个option被选中,表示选中
-    :param option 数据字典
-    :return
-        -3:代表UIView判断未通过;
-        -2:代表选中了parent,但未找到焦点;
-        -1:未进入parent的页面,找不到parent layout;
-         0:进入了parent的页面,未选中parent;
-         1:已经选中parent
-    '''
-    def checkParentChoose(self, parent):
-        return self.focusCmd.checkParentChoose(parent)
-
-    def focusTargetOption(self, parent, option):
-        if parent[UATTree.TAB_MOVE_KEY][UATTree.Max_Try] != "":
-            Max_Try = int(parent[UATTree.TAB_MOVE_KEY][UATTree.Max_Try])
-        else:
-            Max_Try = parent[UATTree.TAB_OPTION].__len__()
-        moveKey = parent[UATTree.TAB_MOVE_KEY]
-        print "moveKey:", moveKey
-        findDirection = ""
-        keyType = UATTree.Key_Event
-        if moveKey[UATTree.Key_Event].__len__() > 1:
-            findDirection = moveKey[UATTree.Key_Event][0]
-            keyType = UATTree.Key_Event
-        elif moveKey[UATTree.Key_IR].__len__() > 1:
-            findDirection = moveKey[UATTree.Key_IR][0]
-            keyType = UATTree.Key_IR
-        elif moveKey[UATTree.Key_Input].__len__() > 1:
-            inputCmd = moveKey[UATTree.Key_Input]
-        elif moveKey[UATTree.Key_AM].__len__() > 1:
-            amCmd = moveKey[UATTree.Key_AM]
-        else:
-            error(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " option 读取 move_key失败", ERROR)
-            return False
-
-        chooseType = option[UATTree.TAB_FOCUS_SELECT][UATTree.FS_Type]
-        optionView = option[UATTree.TAB_OPTION_VIEW]
-        viewId = optionView[UATTree.View_ID]
-        viewText = optionView[UATTree.View_Text]
-        viewIndex = optionView[UATTree.View_Index]
-        viewClass = optionView[UATTree.View_Class]
-        viewDesc = optionView[UATTree.View_Desc]
-        recyclerViewId = parent[UATTree.TAB_LAYOUT][UATTree.Layout_ID]
-        focuseView = option[UATTree.TAB_FOCUSE_VIEW]
-        focuseViewId = focuseView[UATTree.View_ID]
-        others = parent[UATTree.TAB_OTHERS]
-        hb_keyDict = others[UATTree.Key_HeartBeat]
-
-        print "%s chooseType: %s"%(option["name"], chooseType)
-
-        if chooseType.__len__() > 1:
-            if chooseType.lower() == "focus" or chooseType.lower() == "select" or chooseType.lower() == "no":
-                return self.focusCmd.focusOptionView(parent,option,chooseType)
-            elif chooseType.lower() == "long_click":
-                info(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " going to long click type!!!", info)
-                targetObj = self.pyU.getUiObject(resourceId=viewId)
-                if targetObj.exists:
-                    info(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " 已找到目标Object,长点击目标Object。", info)
-                    targetObj.long_click()
-                    return True
-                else:
-                    info(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " 目标Object当前界面未找到!!!", info)
-                    return False
-            # elif chooseType.lower() == "no":
-            #     info(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " 目标Object的chooseType为no,默认为不需要选中。", info)
-            #     retValue = self.checkOptionExist(option,parent)
-            #     if retValue is True:
-            #         return True
-            #     else:
-            #         error(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " option在当前界面不存在", ERROR)
-            #         return False
-            else:
-                error(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " option focus-select属性配置异常", ERROR)
-                return False
-        elif viewText.__len__() > 0\
-                or focuseViewId.__len__() > 2\
-                or focuseView[UATTree.Focus_Desc].__len__() > 0:
-            info(self.cls, "focusTargetOption",
-                 "going toDestFocusByText_for_FocusView, Option:%s" % option[UATTree.TAB_NAME], INFO)
-            return self.fm.toDestFocusByText_for_FocusView(text = viewText,
-                                                            FocusViewResourceId = focuseViewId,
-                                                            findDirection = findDirection,
-                                                            Max_Try = Max_Try,
-                                                            keyType = keyType,
-                                                            hb_keyDict=hb_keyDict)
-
-        else:
-            error(self.cls, "focusTargetOption", "Warning:Option %s读取的参数未能找到合适的执行方式,执行失败,请注意检查UATree文件!!!"%option[UATTree.TAB_NAME], ERROR)
-            return False
-
-    '''
-    执行uatree中的各种直接按键,不携带其他参数。限enter_key和toParent_Key。
-    am和input等带参数的指令,无法处理。
-    mkey:parent里面的key字典
-    '''
-    def executeKey(self, mkey):
-        eventList = mkey[UATTree.Key_Event]
-        irList = mkey[UATTree.Key_IR]
-        print "executeKey,eventList:",eventList
-        print "executeKey,irList:",irList
-
-        times = 1
-        wait = 0.5
-        keyList = []
-
-        if eventList.__len__() <= 0:
-            if irList.__len__() <= 0:
-                info(self.cls, "executeKey", "传入的keyDict中,event和ir的key list均为空!!!默认executeKey执行成功!!!", INFO)
-                return True
-            # 读取其中是否存在wait或者times属性
-            for irKey in irList:
-                if str(irKey).startswith("times="):
-                    times = int(irKey.lstrip("times="))
-                if str(irKey).startswith("wait="):
-                    wait = float(irKey.lstrip("wait="))
-                else:
-                    keyList.append(irKey)
-            #执行keyList
-            info(self.cls, "executeKey", "executing irKey, wait=%s, times=%s"%(wait, times), INFO)
-            for i in range(0, times):
-                for irKey in keyList:
-                    print "executeKey, sendKey:%s"%irKey
-                    self.tvOperator.sendKey(irKey, duration=wait)
-        else:
-            # 读取其中是否存在wait或者times属性
-            for eventKey in eventList:
-                if str(eventKey).startswith("times="):
-                    times = int(eventKey.lstrip("times="))
-                if str(eventKey).startswith("wait="):
-                    wait = float(eventKey.lstrip("wait="))
-                else:
-                    keyList.append(eventKey)
-
-            #执行keyList
-            info(self.cls, "executeKey", "executing eventKey, wait=%s, times=%s"%(wait, times), INFO)
-            for i in range(0, times):
-                for eventKey in keyList:
-                    print "executeKey, pressKeyTimes:%s"%eventKey
-                    if self.eventKeyCode.has_key(eventKey.upper()):
-                        keyCode = self.eventKeyCode[eventKey.upper()]
-                        info(self.cls, "executeKey", "eventKeyCode has key %s, code is %s" % (eventKey, keyCode), INFO)
-                        self.pyU.pressKeyTimes(keyCode)
-                    else:
-                        self.pyU.pressKeyTimes(eventKey)
-                    time.sleep(wait)
-        return True
-
-    '''
-    用于执行option的EnterKey
-    :param mkeys:option的EnterKey字典
-    :nextParent: option要进入的下一个parent字典,如果parent=None,则进入和option同名的parent。
-    '''
-    def executeEnterKey(self, mkeys, nextParent=None):
-        enterKey = None
-        if nextParent is None:
-            # 不指定nextParent,默认用第一个
-            try:
-                enterKey = mkeys[0][UATTree.Key_Param]
-            except Exception,e:
-                enterKey = None
-                error(self.cls, "executeEnterKey", "This option has no enterKey",ERROR)
-                return True
-        else:
-            # 指定nextParent,根据parentName查找匹配的enterKey
-            enterKey = self.uatPathManage.uatData.UATree.getEKByParent(mkeys, nextParent[UATTree.TAB_NAME])
-            if enterKey is None:
-                error(self.cls,"executeEnterKey","Next parent %s has not enterKey"%nextParent[UATTree.TAB_NAME],ERROR)
-                return True
-        for keyname in enterKey[UATTree.Key_Value]:
-            ret = self.executeKeyByType(keyname,
-                                        enterKey[UATTree.Key_Type],
-                                        times=enterKey[UATTree.Key_Times],
-                                        duration=enterKey[UATTree.Duration_Time])
-            time.sleep(enterKey[UATTree.Wait_Time])
-            if ret is False:
-                return False
-        return True
-
-    '''
-    执行uatree中的moveKey
-    :param mkey move_key的字典
-           flag 决定执行的方向,传入值为0和1
-    '''
-    def executeMoveKey(self, mkey, flag):
-        eventList = mkey[UATTree.Key_Event]
-        irList = mkey[UATTree.Key_IR]
-        print "executeMoveKey,eventList:",eventList
-        print "executeMoveKey,irList:",irList
-        times = 1
-        wait = 0.5
-        if eventList.__len__() <= 0:
-            if irList.__len__() <= 0:
-                return False
-            # 读取其中是否存在wait或者times属性
-            for item in irList:
-                if str(item).startswith("times="):
-                    times = int(item.lstrip("times="))
-                    irList.remove(item)
-                if str(item).startswith("wait="):
-                    wait = float(item.lstrip("wait="))
-                    irList.remove(item)
-            #执行move_key
-            info(self.cls, "executeMoveKey", "executing irMoveKey:%s, wait=%s, times=%s, flag=%s"%(irList[flag],wait, times, flag), INFO)
-            return self.tvOperator.sendKey(irList[flag], duration=wait)
-        else:
-            # 读取其中是否存在wait或者times属性
-            for item in eventList:
-                if str(item).startswith("times="):
-                    times = int(item.lstrip("times="))
-                    eventList.remove(item)
-                if str(item).startswith("wait="):
-                    wait = float(item.lstrip("wait="))
-                    eventList.remove(item)
-            #执行move_key
-                    info(self.cls, "executeMoveKey", "executing eventMoveKey:%s, wait=%s, times=%s, flag=%s"%(eventList[flag], wait, times, flag), INFO)
-            self.pyU.pressKeyTimes(eventList[flag])
-            time.sleep(wait)
-            # pressKeyTimes不返回Bool结果
-            return True
-
-    '''
-    根据keyType执行key值
-    '''
-    def executeKeyByType(self, key, keyType, times = 1, duration = 1.0):
-        info(self.cls, "executeKeyByType", "executeKeyByType key %s, keyType %s, " % (key, keyType), INFO)
-        if keyType == UATTree.Key_Event:
-            if self.eventKeyCode.has_key(key.upper()):
-                keyCode = self.eventKeyCode[key.upper()]
-                info(self.cls, "executeKeyByType", "eventKeyCode has key %s, code is %s" % (key, keyCode), INFO)
-                self.pyU.pressKeyTimes(keyCode, times, duration)
-            else:
-                self.pyU.pressKeyTimes(key, times, duration)
-            return True
-        elif keyType == UATTree.Key_IR:
-            self.tvOperator.sendKey(key, times, duration)
-            return True
-        else:
-            return False
-
-    '''
-    执行firstParent的shortCut_key
-    '''
-    def executeShortCutKey(self, parent):
-        amList = str(parent[UATTree.TAB_SHORTCUT_KEY][UATTree.Key_AM]).split(",")
-        waitTime = 0
-        print "amList:", amList
-        if amList[-1].startswith("wait="):
-            waitTime = float(amList[-1].lstrip("wait="))
-        print "executing ShortCutKey,amWaitTime:",waitTime
-        if amList[0].lower() == "activity":
-            print "executeShortCutKey,amList:",amList,parent[UATTree.TAB_UI_VIEW]
-            activityParam = parent[UATTree.TAB_UI_VIEW][UATTree.UIView_Activity]
-            # 通过 / 号分割pkgName和activity,部分项目不存在activity。
-            pkgList = string_util.strToList(activityParam,"/")
-            pkgName = pkgList[0]
-            if pkgList.__len__() == 1:
-                self.pyU.startApp(pkgName)
-            elif pkgList.__len__() == 2:
-                activity = pkgList[1]
-                self.pyU.startApp(pkgName, activity)
-            else:
-                debug(self.cls, "executeShortCutKey", "UATree中 %s activity参数异常!!请检查!!"%parent[UATTree.TAB_NAME], DEBUG)
-            time.sleep(waitTime)
-            return True
-        else:
-            return self.executeKey(parent[UATTree.TAB_SHORTCUT_KEY])
-
-    '''
-    用于处理parent可能出现的弹窗,isForword用于判断是处理进入弹窗还是退出弹窗
-    '''
-    def executeDialog(self, parent, isForward=True):
-        print "parent %s executeDialog parent[UATTree.TAB_UI_VIEW]:%s"%(parent[UATTree.TAB_NAME], parent[UATTree.TAB_UI_VIEW])
-        if isForward is True:
-            dialog_A = parent[UATTree.TAB_UI_VIEW][UATTree.UIView_Dialog_F]
-            type = "forward"
-        else:
-            dialog_A = parent[UATTree.TAB_UI_VIEW][UATTree.UIView_Dialog_B]
-            type = "back"
-
-        if dialog_A.__len__ > 0:
-            info(self.cls, "executeDialog", "parent %s has %s dialog:%s"%(parent[UATTree.TAB_NAME], type, dialog_A), INFO)
-            for dialogName in dialog_A:
-                dialog = dialog_A[dialogName]
-                ret = self.focusCmd.checkDialogExist(dialog)
-                if ret < 1:
-                    info(self.cls, "executeDialog", "parent %s dialog %s doesn't popup."%(parent[UATTree.TAB_NAME], dialogName), INFO)
-                    continue
-                tarOption = dialog["choose"]
-                if not self.focusTargetOption(dialog, tarOption):
-                    error(self.cls, "executeDialog", "dialog %s focus choose option %s fail"%(dialogName, tarOption), ERROR)
-                    return 0
-                print "executing enter_key"
-                self.executeEnterKey(tarOption[UATTree.TAB_ENTER_KEY])
-                time.sleep(1)
-                # 考虑到弹窗优先级与遍历顺序可能不一致的问题,重新再进行一次弹窗遍历
-                return self.executeDialog(parent, isForward)
-        else:
-            info(self.cls, "executeDialog", "parent %s has no %s dialog"%(parent[UATTree.TAB_NAME], type), INFO)
-        return 1
-
-    '''
-    根据excel表格的enter_key中读取的按键设置checkbox的动作
-    '''
-    def setCheckboxAction(self,option,textValue):
-        info(self.cls, "setCheckboxAction", "textValue:%s" % textValue, INFO)
-        if textValue != "":
-            enterKey = option[UATTree.TAB_ENTER_KEY]
-            return self.executeEnterKey(enterKey)
-        else:
-            error(self.cls, "setCheckboxAction", " Option %s textValue is empty!" % option[UATTree.TAB_NAME], ERROR)
-            return False
-
-    def setCheckbox(self, option, value, currentValue, textValue, resourceId):
-        if currentValue == value:
-            info(self.cls, "setCheckbox", " is %s, no need to change!" % value, INFO)
-        else:
-            self.setCheckboxAction(option,textValue)
-            objInfo = self.pyU.getFocusedUIObject().child(resourceId=resourceId).info  # checkbox的info
-            ifChecked = objInfo['checked']
-            if ifChecked == value:
-                info(self.cls, "setCheckbox", " to %s success!" % value, INFO)
-                return True
-            else:
-                info(self.cls, "setCheckbox", " to %s fail!" % value, INFO)
-                return False
-
-    '''
-    执行设值动作,区分数字设值以及非数字设值
-    '''
-    def setValue(self, option, value):
-        textValue = option[UATTree.TAB_TEXT_VALUE]
-        sign = textValue['sign']
-        info(self.cls, "setValue", "textValue:%s"%textValue, INFO)
-        if textValue[UATTree.ValueView_Value] != "":
-            #EnterKey作为textValue配置中的值设置时的按键,enterKey只能配置一个
-            enterKey = option[UATTree.TAB_ENTER_KEY][0][UATTree.Key_Param]
-            if enterKey[UATTree.Key_Value].__len__() > 0:
-                keyList = enterKey[UATTree.Key_Value]
-                keyType = enterKey[UATTree.Key_Type]
-            else:
-                info(self.cls, "setValue", "textValue在有值的情况下,enterKey读取失败,无法进行setValue的动作。", INFO)
-                return False
-            if textValue.has_key(UATTree.ValueView_Min) and textValue.has_key(UATTree.ValueView_Max):
-                valueViewResId = textValue[UATTree.ValueView_ID]
-                count = 0
-                # 默认步长为1
-                stepSize = 1.0
-                if textValue[UATTree.ValueView_StepSize] != "":
-                    stepSize = float(textValue[UATTree.ValueView_StepSize])
-                # 默认设值间隔为0.2s
-                duration = 0.2
-                if textValue[UATTree.ValueView_Duration] != "":
-                    duration = float(textValue[UATTree.ValueView_Duration])
-                info(self.cls, "setValue", "stepSize:%s, duration:%s"%(stepSize, duration), INFO)
-                while(count < 3):
-                    # 获取数值时存在两种情况,一种是数值文本在聚焦组件里,一种在聚焦组件之外,分别处理。
-                    # 先在聚焦组件内找数值
-                    retCode, choosedObj = self.focusCmd.getChooseUIObj(option)
-                    # print "choosedObj.info:", choosedObj.info
-                    text = self.getValueInObjectByTextResourceId(choosedObj, valueViewResId)
-                    if text is None:
-                        # 组件之中找不到时,则直接用resourceId在整个页面找
-                        textObj = self.pyU.getUiObject(resourceId=valueViewResId)
-                        if textObj is None or textObj.info is None:
-                            error(self.cls, "setValue", "查不到组件,请检查%s 的textValue resid是否正确" % option[UATTree.TAB_NAME], ERROR)
-                            return False
-                        text = textObj.info['text']
-                        if text is None:
-                            error(self.cls, "setValue", "获取数值失败,请检查%s 的textValue resid是否正确"%option[UATTree.TAB_NAME], ERROR)
-                            return False
-                    if text[0] not in sign:
-                        dList = getDigitFromString(text)
-                        if dList.__len__() > 0:
-                            text_num = float(dList[-1])
-                        else:
-                            text_num = None
-                    else:
-                        if text[0] == sign[0]:
-                            text_num = -float(text.strip(text[0]))
-                        elif text[0] == sign[1]:
-                            text_num = float(text.strip(text[0]))
-                        else:
-                            text_num = float(text)
-                    info(self.cls, "setValue", "当前设值为%s"%text, INFO)
-                    value = str(value)
-                    if len(sign)!=0:
-                        if value[0]==sign[0]:
-                            value_num = -float(value.strip(value[0]))
-                        elif value[0]==sign[1]:
-                            value_num = float(value.strip(value[0]))
-                        else:
-                            value_num = float(value)
-                    else:
-                        value_num = float(value)
-                    if text_num is None:
-                        return False
-                    if value_num < text_num:
-                        self.executeKeyByType(keyList[0], keyType, int((text_num - value_num)/stepSize), duration)
-                    elif value_num > text_num:
-                        self.executeKeyByType(keyList[1], keyType, int((value_num - text_num)/stepSize), duration)
-                    else:
-                        return True
-                    count += 1
-                else:
-                    info(self.cls, "setValue", "%s次设值仍未能设置为%s,设值失败"%(count+1, value), INFO)
-                    return False
-            elif textValue[UATTree.ValueView_Value].lower() == "input":
-                info(self.cls, "setValue", "设值类型为input类型,传入的值为%s"%value, INFO)
-                # 设值类型必须要有设值textBox Object
-                resid = textValue[UATTree.ValueView_ID]
-                text = textValue[UATTree.ValueView_Text]
-                desc = textValue[UATTree.ValueView_Desc]
-                textBoxObj = self.pyU.getUiObject(resourceId=resid, text=text, description=desc)
-                if not textBoxObj.exists:
-                    debug(self.cls, "setValue", "未能获取到设值的TextBox Object!!!请检查相关配置是否能获取到TextBox!!!", DEBUG)
-                    return False
-                setValueSuccess = textBoxObj.set_text(value)
-                if not setValueSuccess:
-                    debug(self.cls, "setValue", "TextBox使用set_text()接口设值失败,值为%s"%value, DEBUG)
-                    return False
-                else:
-                    print "executing enter_key"
-                    return self.executeEnterKey(option[UATTree.TAB_ENTER_KEY])
-            else:
-                info(self.cls, "setValue", "读取到value参数配置为非数值value,开始进行非数值型value设值。", INFO)
-                try:
-                    valueView = textValue[value]
-                    valueViewText = valueView["text"]
-                    valueViewResId = valueView["resid"]
-                except Exception,e:
-                    info(self.cls, "setValue", "value %s的配置信息读取失败!!!设值失败!!!请确保该value的text属性和resid属性均已按格式配好!!!"%value, INFO)
-                    return False
-
-                Max_Try = textValue[UATTree.ValueView_Value].__len__()
-                count = 0
-                reverse_count = 0
-
-
-                while (True):
-                    # 获取当前值时存在两种情况,一种是数值文本在聚焦组件里,一种在聚焦组件之外,分别处理。
-                    # 先在聚焦组件内找当前值
-                    retCode, choosedObj = self.focusCmd.getChooseUIObj(option)
-                    print "choosedObj.info:", choosedObj.info
-                    for i in range(choosedObj.info["childCount"]):
-                        childUIObject = choosedObj.child_by_instance(i, resourceId=valueViewResId)
-                        if childUIObject.exists:
-                            text = childUIObject.info['text']
-                            break
-                    else:
-                        # 组件之中找不到时,则直接用resourceId在整个页面找
-                        text = self.getValueByTextResourceId(valueViewResId)
-                        if text is None:
-                            error(self.cls, "setValue", "获取数值失败,请检查%s 的textValue resid是否正确" % option[UATTree.TAB_NAME],
-                                  ERROR)
-                            return False
-                    info(self.cls, "setValue", "当前设值为%s" % text, INFO)
-                    if text == valueViewText:
-                        info(self.cls, "setValue",
-                             "option %s设值为 %s 成功!!!"%(option[UATTree.TAB_NAME], value), INFO)
-                        return True
-                    if count < Max_Try:
-                        self.executeKeyByType(keyList[0], keyType)
-                        count += 1
-                    elif reverse_count < Max_Try:
-                        self.executeKeyByType(keyList[1], keyType)
-                        reverse_count += 1
-                    else:
-                        info(self.cls, "setValue",
-                             "option %s设值已经尝试最大次数%s次,未能切换至目标value %s"%(option[UATTree.TAB_NAME], Max_Try, value) % value, INFO)
-                        return False
-        else:
-            optionName = value
-            option = self.uatPathManage.uatData.getOption(optionName)
-            parent = self.uatPathManage.uatData.getParentByOption(option)
-            return self.valueCmd.setParentValue(parent, option)
-
-    '''
-    根据传入路径进行退出菜单的动作。
-    顺着传入的parent path,逐个确认当前页面是否为该parent。如果不是该parent,则往path后面继续遍历;如果是该parent,则执行该层parent的toParentKey
-    '''
-    def exitMenuByPath(self, path):
-        info(self.cls, "exitMenuByPath", "", INFO)
-        for i in range(0, path.__len__()):
-            parent = path[i]
-            ret = self.checkParentChoose(parent)
-            info(self.cls, "exitMenuByPath", "check parent:%s,ret=%d"%(parent[UATTree.TAB_NAME],ret), INFO)
-            if ret < 1:
-                continue
-            toParentKey = parent[UATTree.TAB_TOPARENT_KEY]
-            info(self.cls, "exitMenuByPath", "now is in parent:%s"%parent[UATTree.TAB_NAME], INFO)
-            if toParentKey[UATTree.Key_Event] == [] and toParentKey[UATTree.Key_IR] == []:
-                info(self.cls, "exitMenuByPath", "parent:%s has no toparent_key, using default back" % parent[UATTree.TAB_NAME], INFO)
-                self.executeKeyByType("back", UATTree.Key_Event)
-                time.sleep(1)
-            else:
-                self.executeKey(toParentKey)
-            time.sleep(1)
-            ret = self.executeDialog(parent, False)
-            if ret < 1:
-                return False
-        return True
-
-    def getUIObjPic(self, obj):
-        picName = time.time()
-        picPath = os.path.join(getSATTmpDIR(), "%s.png"%picName)
-        if obj == None:
-            return False, picPath, ""
-        self.CC.takePicture(picPath)
-        try:
-            bounds = obj.info["bounds"]
-            area = [bounds["left"], bounds["top"], bounds["right"], bounds["bottom"]]
-            cutPicPath = os.path.join(getSATTmpDIR(), "%s_cutPic_%s.png"%(picName, area))
-            self.imgCMP.saveCropPic(picPath, cutPicPath, area)
-        except Exception,e:
-            return False, picPath, ""
-        return True, picPath, cutPicPath
-
-    def infoViewCmpText(self, cmpText, picPath):
-        ocrParamList = [("ENG_CHN", 10000),
-                        ("ENG_CHN", 10001)]
-        for ocrParam in ocrParamList:
-            ocr_str = self.ocrConvert.getStr(picPath, ocrParam[0], ocrParam[1])
-            info(self.cls, "infoViewCmpText", "cmpText:%s ocr_str:%s ocrParam:%s"%(cmpText, ocr_str, ocrParam),INFO)
-            if strcmp(ocr_str, cmpText):
-                info(self.cls, "infoViewCmpText", "OCR文字识别对比成功",INFO)
-                return 1
-        else:
-            error(self.cls, "infoViewCmpText", "Waring:OCR文字识别对比失败!!",ERROR)
-            return 0
-
-    def infoViewCmpPic(self, cmpPic, picPath):
-        cmpImg = cv.imread(cmpPic)
-        cutImg = cv.imread(picPath)
-        result = self.imgCMP.cmpImgTotal(cmpImg, cutImg)
-        if result is True:
-            info(self.cls, "infoViewCmpText", "图片对比成功", INFO)
-            return 1
-        else:
-            error(self.cls, "infoViewCmpText", "Waring:图片对比失败!!",ERROR)
-            return 0
-
-
-    def getValueByTextResourceId(self, textResourceId):
-        try:
-            textObj = self.pyU.getUiObject(resourceId=textResourceId)
-            text = textObj.info['text']
-            digitList = getDigitFromString(text)
-            return digitList[-1]
-        except Exception, e:
-            return None
-
-    def getValueInObjectByTextResourceId(self, object, textResourceId):
-        try:
-            for i in range(object.info["childCount"]):
-                childUIObject = object.child_by_instance(i, resourceId=textResourceId)
-                if childUIObject.exists:
-                    text = childUIObject.info['text']
-                    digitList = getDigitFromString(text)
-                    return digitList[-1]
-            else:
-                return None
-        except Exception, e:
-            error(self.cls, "getValueInObjectByTextResourceId", "getValueInObjectByByTextResourceId:Object loading error!!!!!!error msg:%s"%e, ERROR)
-            return None
+# -*- coding:utf-8 -*-
+from UAT_tree import UATTree
+from ssat_sdk.python_uiautomator import PyUIAutomator,FocusManageAndroid, DirectionManageAndroid
+from UAT_log import error,debug,info
+from ssat_sdk.utils import  string_util
+from ssat_sdk.tv_operator import TvOperator
+
+from ssat_sdk.device_manage.capturecard_manager import CCardManager
+from ssat_sdk.sat_environment import getSATTmpDIR
+from ssat_sdk.pic_tool import ImageCMP
+from ssat_sdk.ocr_convert import OCRConvert
+from ssat_sdk.utils.string_util import strcmp, getDigitFromString
+from UAT_valueCommand import ValueCommand
+from UAT_focusCommand import FocusCommand
+from UIParamUtil import UIParamUtil
+
+import cv2 as cv
+
+import os, sys, time
+
+DEBUG = True
+INFO =True
+ERROR = True
+
+class UATRunnerCommand():
+    cls = "UATRunnerCommand"
+    def __init__(self, uatPathManage, pyU, dm, fm):
+        self.uatPathManage = uatPathManage
+        self.pyU = pyU
+        self.dm = dm
+        self.fm = fm
+        self.tvOperator = TvOperator()
+        self.eventKeyCode = self.uatPathManage.uatData.getKeyCodeDict()
+
+        self.CC = CCardManager()
+        self.imgCMP = ImageCMP()
+        self.ocrConvert = OCRConvert()
+        self.valueCmd = ValueCommand( self)
+        self.focusCmd = FocusCommand(self)
+
+    #检测option组件是不是在电视上显示了
+    def checkOptionExist(self, option, parent):
+        return self.focusCmd.checkOptionExist(option,parent)
+
+    '''
+    检测parent,在电视上是否被选中了。一个option被选中,表示选中
+    :param option 数据字典
+    :return
+        -3:代表UIView判断未通过;
+        -2:代表选中了parent,但未找到焦点;
+        -1:未进入parent的页面,找不到parent layout;
+         0:进入了parent的页面,未选中parent;
+         1:已经选中parent
+    '''
+    def checkParentChoose(self, parent):
+        return self.focusCmd.checkParentChoose(parent)
+
+    def focusTargetOption(self, parent, option):
+        if parent[UATTree.TAB_MOVE_KEY][UATTree.Max_Try] != "":
+            Max_Try = int(parent[UATTree.TAB_MOVE_KEY][UATTree.Max_Try])
+        else:
+            Max_Try = parent[UATTree.TAB_OPTION].__len__()
+        moveKey = parent[UATTree.TAB_MOVE_KEY]
+        print "moveKey:", moveKey
+        findDirection = ""
+        keyType = UATTree.Key_Event
+        if moveKey[UATTree.Key_Event].__len__() > 1:
+            findDirection = moveKey[UATTree.Key_Event][0]
+            keyType = UATTree.Key_Event
+        elif moveKey[UATTree.Key_IR].__len__() > 1:
+            findDirection = moveKey[UATTree.Key_IR][0]
+            keyType = UATTree.Key_IR
+        elif moveKey[UATTree.Key_Input].__len__() > 1:
+            inputCmd = moveKey[UATTree.Key_Input]
+        elif moveKey[UATTree.Key_AM].__len__() > 1:
+            amCmd = moveKey[UATTree.Key_AM]
+        else:
+            error(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " option 读取 move_key失败", ERROR)
+            return False
+
+        chooseType = option[UATTree.TAB_FOCUS_SELECT][UATTree.FS_Type]
+        optionView = option[UATTree.TAB_OPTION_VIEW]
+        viewId = optionView[UATTree.View_ID]
+        viewText = optionView[UATTree.View_Text]
+        viewIndex = optionView[UATTree.View_Index]
+        viewClass = optionView[UATTree.View_Class]
+        viewDesc = optionView[UATTree.View_Desc]
+        recyclerViewId = parent[UATTree.TAB_LAYOUT][UATTree.View_ID]
+        focuseView = option[UATTree.TAB_FOCUSE_VIEW]
+        focuseViewId = focuseView[UATTree.View_ID]
+        focusViewClass = focuseView[UATTree.View_Class]
+        focuseViewDesc = focuseView[UATTree.View_Desc]
+        others = parent[UATTree.TAB_OTHERS]
+        hb_keyDict = others[UATTree.Key_HeartBeat]
+
+        print "%s chooseType: %s"%(option["name"], chooseType)
+
+        if chooseType.__len__() > 1:
+            if chooseType.lower() == "focus" or chooseType.lower() == "select" or chooseType.lower() == "no":
+                return self.focusCmd.focusOptionView(parent,option,chooseType)
+            elif chooseType.lower() == "long_click":
+                info(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " going to long click type!!!", info)
+                targetObj = self.pyU.getUiObject(resourceId=viewId)
+                if targetObj.exists:
+                    info(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " 已找到目标Object,长点击目标Object。", info)
+                    targetObj.long_click()
+                    return True
+                else:
+                    info(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " 目标Object当前界面未找到!!!", info)
+                    return False
+            # elif chooseType.lower() == "no":
+            #     info(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " 目标Object的chooseType为no,默认为不需要选中。", info)
+            #     retValue = self.checkOptionExist(option,parent)
+            #     if retValue is True:
+            #         return True
+            #     else:
+            #         error(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " option在当前界面不存在", ERROR)
+            #         return False
+            else:
+                error(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " option focus-select属性配置异常", ERROR)
+                return False
+        elif viewText.__len__() > 0\
+                or focuseViewId.__len__() > 2\
+                or focuseViewDesc.__len__() > 0:
+            info(self.cls, "focusTargetOption",
+                 "going toDestFocusByText_for_FocusView, Option:%s" % option[UATTree.TAB_NAME], INFO)
+            return self.fm.toDestFocusByText_for_FocusView(text = viewText,
+                                                            FocusViewResourceId = focuseViewId,
+                                                           FocusViewClass = focusViewClass,
+                                                           FocusViewDesc = focuseViewDesc,
+                                                            findDirection = findDirection,
+                                                            Max_Try = Max_Try,
+                                                            keyType = keyType,
+                                                            hb_keyDict=hb_keyDict)
+
+        else:
+            error(self.cls, "focusTargetOption", "Warning:Option %s读取的参数未能找到合适的执行方式,执行失败,请注意检查UATree文件!!!"%option[UATTree.TAB_NAME], ERROR)
+            return False
+
+    '''
+    执行uatree中的各种直接按键,不携带其他参数。限enter_key和toParent_Key。
+    am和input等带参数的指令,无法处理。
+    mkey:parent里面的key字典
+    '''
+    def executeKey(self, mkey):
+        eventList = mkey[UATTree.Key_Event]
+        irList = mkey[UATTree.Key_IR]
+        print "executeKey,eventList:",eventList
+        print "executeKey,irList:",irList
+
+        times = 1
+        wait = 0.5
+        keyList = []
+
+        if eventList.__len__() <= 0:
+            if irList.__len__() <= 0:
+                info(self.cls, "executeKey", "传入的keyDict中,event和ir的key list均为空!!!默认executeKey执行成功!!!", INFO)
+                return True
+            # 读取其中是否存在wait或者times属性
+            for irKey in irList:
+                if str(irKey).startswith("times="):
+                    times = int(irKey.lstrip("times="))
+                if str(irKey).startswith("wait="):
+                    wait = float(irKey.lstrip("wait="))
+                else:
+                    keyList.append(irKey)
+            #执行keyList
+            info(self.cls, "executeKey", "executing irKey, wait=%s, times=%s"%(wait, times), INFO)
+            for i in range(0, times):
+                for irKey in keyList:
+                    print "executeKey, sendKey:%s"%irKey
+                    self.tvOperator.sendKey(irKey, duration=wait)
+        else:
+            # 读取其中是否存在wait或者times属性
+            for eventKey in eventList:
+                if str(eventKey).startswith("times="):
+                    times = int(eventKey.lstrip("times="))
+                if str(eventKey).startswith("wait="):
+                    wait = float(eventKey.lstrip("wait="))
+                else:
+                    keyList.append(eventKey)
+
+            #执行keyList
+            info(self.cls, "executeKey", "executing eventKey, wait=%s, times=%s"%(wait, times), INFO)
+            for i in range(0, times):
+                for eventKey in keyList:
+                    print "executeKey, pressKeyTimes:%s"%eventKey
+                    if self.eventKeyCode.has_key(eventKey.upper()):
+                        keyCode = self.eventKeyCode[eventKey.upper()]
+                        info(self.cls, "executeKey", "eventKeyCode has key %s, code is %s" % (eventKey, keyCode), INFO)
+                        self.pyU.pressKeyTimes(keyCode)
+                    else:
+                        self.pyU.pressKeyTimes(eventKey)
+                    time.sleep(wait)
+        return True
+
+    '''
+    用于执行option的EnterKey
+    :param mkeys:option的EnterKey字典
+    :nextParent: option要进入的下一个parent字典,如果parent=None,则进入和option同名的parent。
+    '''
+    def executeEnterKey(self, mkeys, nextParent=None):
+        if nextParent is None:
+            # 不指定nextParent,默认用第一个
+            try:
+                enterKey = mkeys[0][UATTree.Key_Param]
+            except Exception,e:
+                error(self.cls, "executeEnterKey", "This option has no enterKey",ERROR)
+                return True
+        else:
+            # 指定nextParent,根据parentName查找匹配的enterKey
+            enterKey = self.uatPathManage.uatData.UATree.getEKByParent(mkeys, nextParent[UATTree.TAB_NAME])
+            if enterKey is None:
+                error(self.cls,"executeEnterKey","Next parent %s has not enterKey"%nextParent[UATTree.TAB_NAME],ERROR)
+                return True
+        for keyname in enterKey[UATTree.Key_Value]:
+            ret = self.executeKeyByType(keyname,
+                                        enterKey[UATTree.Key_Type],
+                                        times=enterKey[UATTree.Key_Times],
+                                        duration=enterKey[UATTree.Duration_Time])
+            time.sleep(enterKey[UATTree.Wait_Time])
+            if ret is False:
+                return False
+        return True
+
+    '''
+    执行uatree中的moveKey
+    :param mkey move_key的字典
+           flag 决定执行的方向,传入值为0和1
+    '''
+    def executeMoveKey(self, mkey, flag):
+        eventList = mkey[UATTree.Key_Event]
+        irList = mkey[UATTree.Key_IR]
+        print "executeMoveKey,eventList:",eventList
+        print "executeMoveKey,irList:",irList
+        times = 1
+        wait = 0.5
+        if eventList.__len__() <= 0:
+            if irList.__len__() <= 0:
+                return False
+            # 读取其中是否存在wait或者times属性
+            for item in irList:
+                if str(item).startswith("times="):
+                    times = int(item.lstrip("times="))
+                    irList.remove(item)
+                if str(item).startswith("wait="):
+                    wait = float(item.lstrip("wait="))
+                    irList.remove(item)
+            #执行move_key
+            info(self.cls, "executeMoveKey", "executing irMoveKey:%s, wait=%s, times=%s, flag=%s"%(irList[flag],wait, times, flag), INFO)
+            return self.tvOperator.sendKey(irList[flag], duration=wait)
+        else:
+            # 读取其中是否存在wait或者times属性
+            for item in eventList:
+                if str(item).startswith("times="):
+                    times = int(item.lstrip("times="))
+                    eventList.remove(item)
+                if str(item).startswith("wait="):
+                    wait = float(item.lstrip("wait="))
+                    eventList.remove(item)
+            #执行move_key
+                    info(self.cls, "executeMoveKey", "executing eventMoveKey:%s, wait=%s, times=%s, flag=%s"%(eventList[flag], wait, times, flag), INFO)
+            self.pyU.pressKeyTimes(eventList[flag])
+            time.sleep(wait)
+            # pressKeyTimes不返回Bool结果
+            return True
+
+    '''
+    根据keyType执行key值
+    '''
+    def executeKeyByType(self, key, keyType, times = 1, duration = 1.0):
+        info(self.cls, "executeKeyByType", "executeKeyByType key %s, keyType %s, " % (key, keyType), INFO)
+        if keyType == UATTree.Key_Event:
+            if self.eventKeyCode.has_key(key.upper()):
+                keyCode = self.eventKeyCode[key.upper()]
+                info(self.cls, "executeKeyByType", "eventKeyCode has key %s, code is %s" % (key, keyCode), INFO)
+                self.pyU.pressKeyTimes(keyCode, times, duration)
+            else:
+                self.pyU.pressKeyTimes(key, times, duration)
+            return True
+        elif keyType == UATTree.Key_IR:
+            self.tvOperator.sendKey(key, times, duration)
+            return True
+        else:
+            return False
+
+    '''
+    执行firstParent的shortCut_key
+    '''
+    def executeShortCutKey(self, parent):
+        amList = str(parent[UATTree.TAB_SHORTCUT_KEY][UATTree.Key_AM]).split(",")
+        waitTime = 0
+        print "amList:", amList
+        if amList[-1].startswith("wait="):
+            waitTime = float(amList[-1].lstrip("wait="))
+        print "executing ShortCutKey,amWaitTime:",waitTime
+        if amList[0].lower() == "activity":
+            print "executeShortCutKey,amList:",amList,parent[UATTree.TAB_UI_VIEW]
+            activityParam = parent[UATTree.TAB_UI_VIEW][UATTree.UIView_Activity]
+            # 通过 / 号分割pkgName和activity,部分项目不存在activity。
+            pkgList = string_util.strToList(activityParam,"/")
+            pkgName = pkgList[0]
+            if pkgList.__len__() == 1:
+                self.pyU.startApp(pkgName)
+            elif pkgList.__len__() == 2:
+                activity = pkgList[1]
+                self.pyU.startApp(pkgName, activity)
+            else:
+                debug(self.cls, "executeShortCutKey", "UATree中 %s activity参数异常!!请检查!!"%parent[UATTree.TAB_NAME], DEBUG)
+            time.sleep(waitTime)
+            return True
+        else:
+            return self.executeKey(parent[UATTree.TAB_SHORTCUT_KEY])
+
+    '''
+    用于处理parent可能出现的弹窗,isForword用于判断是处理进入弹窗还是退出弹窗
+    '''
+    def executeDialog(self, parent, isForward=True):
+        print "parent %s executeDialog parent[UATTree.TAB_UI_VIEW]:%s"%(parent[UATTree.TAB_NAME], parent[UATTree.TAB_UI_VIEW])
+        if isForward is True:
+            dialog_A = parent[UATTree.TAB_UI_VIEW][UATTree.UIView_Dialog_F]
+            type = "forward"
+        else:
+            dialog_A = parent[UATTree.TAB_UI_VIEW][UATTree.UIView_Dialog_B]
+            type = "back"
+
+        if dialog_A.__len__ > 0:
+            info(self.cls, "executeDialog", "parent %s has %s dialog:%s"%(parent[UATTree.TAB_NAME], type, dialog_A), INFO)
+            for dialogName in dialog_A:
+                dialog = dialog_A[dialogName]
+                ret = self.focusCmd.checkDialogExist(dialog)
+                if ret < 1:
+                    info(self.cls, "executeDialog", "parent %s dialog %s doesn't popup."%(parent[UATTree.TAB_NAME], dialogName), INFO)
+                    continue
+                tarOption = dialog["choose"]
+                if not self.focusTargetOption(dialog, tarOption):
+                    error(self.cls, "executeDialog", "dialog %s focus choose option %s fail"%(dialogName, tarOption), ERROR)
+                    return 0
+                print "executing enter_key"
+                self.executeEnterKey(tarOption[UATTree.TAB_ENTER_KEY])
+                time.sleep(1)
+                # 考虑到弹窗优先级与遍历顺序可能不一致的问题,重新再进行一次弹窗遍历
+                return self.executeDialog(parent, isForward)
+        else:
+            info(self.cls, "executeDialog", "parent %s has no %s dialog"%(parent[UATTree.TAB_NAME], type), INFO)
+        return 1
+
+    '''
+    根据excel表格的enter_key中读取的按键设置checkbox的动作
+    '''
+    def setCheckboxAction(self,option,textValue):
+        info(self.cls, "setCheckboxAction", "textValue:%s" % textValue, INFO)
+        if textValue != "":
+            enterKey = option[UATTree.TAB_ENTER_KEY]
+            return self.executeEnterKey(enterKey)
+        else:
+            error(self.cls, "setCheckboxAction", " Option %s textValue is empty!" % option[UATTree.TAB_NAME], ERROR)
+            return False
+
+    def setCheckbox(self, option, value, currentValue, textValue, resourceId):
+        if currentValue == value:
+            info(self.cls, "setCheckbox", " is %s, no need to change!" % value, INFO)
+        else:
+            self.setCheckboxAction(option,textValue)
+            objInfo = self.pyU.getFocusedUIObject().child(resourceId=resourceId).info  # checkbox的info
+            ifChecked = objInfo['checked']
+            if ifChecked == value:
+                info(self.cls, "setCheckbox", " to %s success!" % value, INFO)
+                return True
+            else:
+                info(self.cls, "setCheckbox", " to %s fail!" % value, INFO)
+                return False
+
+    '''
+    执行设值动作,区分数字设值以及非数字设值
+    '''
+    def setValue(self, option, value):
+        textValue = option[UATTree.TAB_TEXT_VALUE]
+        sign = textValue['sign']
+        info(self.cls, "setValue", "textValue:%s"%textValue, INFO)
+        if textValue[UATTree.ValueView_Value] != "":
+            #EnterKey作为textValue配置中的值设置时的按键,enterKey只能配置一个
+            enterKey = option[UATTree.TAB_ENTER_KEY][0][UATTree.Key_Param]
+            if enterKey[UATTree.Key_Value].__len__() > 0:
+                keyList = enterKey[UATTree.Key_Value]
+                keyType = enterKey[UATTree.Key_Type]
+            else:
+                info(self.cls, "setValue", "textValue在有值的情况下,enterKey读取失败,无法进行setValue的动作。", INFO)
+                return False
+            if textValue.has_key(UATTree.ValueView_Min) and textValue.has_key(UATTree.ValueView_Max):
+                valueViewResId = textValue[UATTree.View_ID]
+                count = 0
+                # 默认步长为1
+                stepSize = 1.0
+                if textValue[UATTree.ValueView_StepSize] != "":
+                    stepSize = float(textValue[UATTree.ValueView_StepSize])
+                # 默认设值间隔为0.2s
+                duration = 0.2
+                if textValue[UATTree.ValueView_Duration] != "":
+                    duration = float(textValue[UATTree.ValueView_Duration])
+                info(self.cls, "setValue", "stepSize:%s, duration:%s"%(stepSize, duration), INFO)
+                while(count < 3):
+                    # 获取数值时存在两种情况,一种是数值文本在聚焦组件里,一种在聚焦组件之外,分别处理。
+                    # 先在聚焦组件内找数值
+                    retCode, choosedObj = self.focusCmd.getChooseUIObj(option)
+                    # print "choosedObj.info:", choosedObj.info
+                    text = self.getValueInObjectByTextResourceId(choosedObj, valueViewResId)
+                    if text is None:
+                        # 组件之中找不到时,则直接用resourceId在整个页面找
+                        textObj = self.pyU.getUiObject(resourceId=valueViewResId)
+                        if textObj is None or textObj.info is None:
+                            error(self.cls, "setValue", "查不到组件,请检查%s 的textValue resid是否正确" % option[UATTree.TAB_NAME], ERROR)
+                            return False
+                        text = textObj.info['text']
+                        if text is None:
+                            error(self.cls, "setValue", "获取数值失败,请检查%s 的textValue resid是否正确"%option[UATTree.TAB_NAME], ERROR)
+                            return False
+                    if text[0] not in sign:
+                        dList = getDigitFromString(text)
+                        if dList.__len__() > 0:
+                            text_num = float(dList[-1])
+                        else:
+                            text_num = None
+                    else:
+                        if text[0] == sign[0]:
+                            text_num = -float(text.strip(text[0]))
+                        elif text[0] == sign[1]:
+                            text_num = float(text.strip(text[0]))
+                        else:
+                            text_num = float(text)
+                    info(self.cls, "setValue", "当前设值为%s"%text, INFO)
+                    value = str(value)
+                    if len(sign)!=0:
+                        if value[0]==sign[0]:
+                            value_num = -float(value.strip(value[0]))
+                        elif value[0]==sign[1]:
+                            value_num = float(value.strip(value[0]))
+                        else:
+                            value_num = float(value)
+                    else:
+                        value_num = float(value)
+                    if text_num is None:
+                        return False
+                    if value_num < text_num:
+                        self.executeKeyByType(keyList[0], keyType, int((text_num - value_num)/stepSize), duration)
+                    elif value_num > text_num:
+                        self.executeKeyByType(keyList[1], keyType, int((value_num - text_num)/stepSize), duration)
+                    else:
+                        return True
+                    count += 1
+                else:
+                    info(self.cls, "setValue", "%s次设值仍未能设置为%s,设值失败"%(count+1, value), INFO)
+                    return False
+            elif textValue[UATTree.ValueView_Value].lower() == "input":
+                info(self.cls, "setValue", "设值类型为input类型,传入的值为%s"%value, INFO)
+                # 设值类型必须要有设值textBox Object
+                resid = textValue[UATTree.View_ID]
+                text = textValue[UATTree.View_Text]
+                desc = textValue[UATTree.View_Desc]
+                textBoxObj = self.pyU.getUiObject(resourceId=resid, text=text, description=desc)
+                if not textBoxObj.exists:
+                    debug(self.cls, "setValue", "未能获取到设值的TextBox Object!!!请检查相关配置是否能获取到TextBox!!!", DEBUG)
+                    return False
+                setValueSuccess = textBoxObj.set_text(value)
+                if not setValueSuccess:
+                    debug(self.cls, "setValue", "TextBox使用set_text()接口设值失败,值为%s"%value, DEBUG)
+                    return False
+                else:
+                    print "executing enter_key"
+                    return self.executeEnterKey(option[UATTree.TAB_ENTER_KEY])
+            else:
+                info(self.cls, "setValue", "读取到value参数配置为非数值value,开始进行非数值型value设值。", INFO)
+                try:
+                    valueView = textValue[value]
+                    valueViewText = valueView["text"]
+                    valueViewResId = valueView["resid"]
+                except Exception,e:
+                    info(self.cls, "setValue", "value %s的配置信息读取失败!!!设值失败!!!请确保该value的text属性和resid属性均已按格式配好!!!"%value, INFO)
+                    return False
+
+                Max_Try = textValue[UATTree.ValueView_Value].__len__()
+                count = 0
+                reverse_count = 0
+
+
+                while (True):
+                    # 获取当前值时存在两种情况,一种是数值文本在聚焦组件里,一种在聚焦组件之外,分别处理。
+                    # 先在聚焦组件内找当前值
+                    retCode, choosedObj = self.focusCmd.getChooseUIObj(option)
+                    print "choosedObj.info:", choosedObj.info
+                    for i in range(choosedObj.info["childCount"]):
+                        childUIObject = choosedObj.child_by_instance(i, resourceId=valueViewResId)
+                        if childUIObject.exists:
+                            text = childUIObject.info['text']
+                            break
+                    else:
+                        # 组件之中找不到时,则直接用resourceId在整个页面找
+                        text = self.getValueByTextResourceId(valueViewResId)
+                        if text is None:
+                            error(self.cls, "setValue", "获取数值失败,请检查%s 的textValue resid是否正确" % option[UATTree.TAB_NAME],
+                                  ERROR)
+                            return False
+                    info(self.cls, "setValue", "当前设值为%s" % text, INFO)
+                    if text == valueViewText:
+                        info(self.cls, "setValue",
+                             "option %s设值为 %s 成功!!!"%(option[UATTree.TAB_NAME], value), INFO)
+                        return True
+                    if count < Max_Try:
+                        self.executeKeyByType(keyList[0], keyType)
+                        count += 1
+                    elif reverse_count < Max_Try:
+                        self.executeKeyByType(keyList[1], keyType)
+                        reverse_count += 1
+                    else:
+                        info(self.cls, "setValue",
+                             "option %s设值已经尝试最大次数%s次,未能切换至目标value %s"%(option[UATTree.TAB_NAME], Max_Try, value) % value, INFO)
+                        return False
+        else:
+            optionName = value
+            option = self.uatPathManage.uatData.getOption(optionName)
+            parent = self.uatPathManage.uatData.getParentByOption(option)
+            return self.valueCmd.setParentValue(parent, option)
+
+    '''
+    执行读取值的动作,区分数字设值以及非数字设值,非数值设值中,如果选项为radioButton机制,则使用radioButton组件来定位。
+    '''
+    def getValue(self, option):
+        textValue = option[UATTree.TAB_TEXT_VALUE]
+        sign = textValue['sign']
+        info(self.cls, "getValue", "textValue:%s"%textValue, INFO)
+        if textValue[UATTree.ValueView_Value] != "":
+            result = True
+            if textValue.has_key(UATTree.ValueView_Min) and textValue.has_key(UATTree.ValueView_Max):
+                valueViewResId = textValue[UATTree.View_ID]
+                # 获取数值时存在两种情况,一种是数值文本在聚焦组件里,一种在聚焦组件之外,分别处理。
+                # 先在聚焦组件内找数值
+                retCode, choosedObj = self.focusCmd.getChooseUIObj(option)
+                # print "choosedObj.info:", choosedObj.info
+                text = self.getValueInObjectByTextResourceId(choosedObj, valueViewResId)
+                if text is None:
+                    # 组件之中找不到时,则直接用resourceId在整个页面找
+                    textObj = self.pyU.getUiObject(resourceId=valueViewResId)
+                    if textObj is None or textObj.info is None:
+                        error(self.cls, "getValue", "查不到组件,请检查%s 的textValue resid是否正确" % option[UATTree.TAB_NAME], ERROR)
+                        result = False
+                    text = textObj.info['text']
+                curValue = text
+                if curValue is None:
+                    error(self.cls, "getValue", "获取数值失败,请检查%s 的textValue resid是否正确"%option[UATTree.TAB_NAME], ERROR)
+                    result = False
+                info(self.cls, "getValue", "当前value值为%s" % curValue, INFO)
+                return result, option[UATTree.TAB_NAME], curValue
+        else:
+            parentName = option[UATTree.TAB_NAME]
+            parent = self.uatPathManage.uatData.getParentDict(parentName)
+            fsDict = parent['option'][parent['option'].keys()[0]][UATTree.TAB_FOCUS_SELECT]
+            print "fsDict:", fsDict
+            if fsDict[UATTree.FS_checkType] == "RadioButton":
+                fsParam = {}
+                if fsDict[UATTree.FS_Type].lower() == "focus":
+                    fsParam["focused"] = "true"
+                elif fsDict[UATTree.FS_Type].lower() == "select":
+                    fsParam["selected"] = "true"
+                if fsDict[UATTree.View_ID] != "":
+                    fsParam["resourceId"] = fsDict[UATTree.View_ID]
+                if fsDict[UATTree.View_Class] != "":
+                    fsParam["className"] = fsDict[UATTree.View_Class]
+                if fsDict[UATTree.View_Desc] != "":
+                    fsParam["description"] = fsDict[UATTree.View_Desc]
+                if parent[UATTree.TAB_MOVE_KEY][UATTree.Key_Event].__len__() > 0:
+                    moveKey = parent[UATTree.TAB_MOVE_KEY][UATTree.Key_Event]
+                    keyType = UATTree.Key_Event
+                else:
+                    moveKey = parent[UATTree.TAB_MOVE_KEY][UATTree.Key_IR]
+                    keyType = UATTree.Key_IR
+                maxTry = len(parent['option'])
+                ret, choosedObj = self.focusCmd.moveToCheckedRedioButton(fsParam, findDirection=moveKey[1], keyType=keyType, maxTry=maxTry)
+                # 聚焦至单选框失败,则取值失败
+                if not ret:
+                    return False, None, ""
+                # 获取聚焦框的文本内容
+                textObj = choosedObj.child(className="android.widget.TextView")
+                text = textObj.info['text']
+                for optionP in parent['option']:
+                    if text == parent['option'][optionP][UATTree.TAB_OPTION_VIEW][UATTree.View_Text]:
+                        return True, optionP, text
+                # 遍历完所有option仍未能对比到目标的文本,则取值失败
+                else:
+                    return False, None, text
+            else:
+                layoutParam = parent[UATTree.TAB_LAYOUT]
+                layoutUIParam = UIParamUtil.setObjParam(layoutParam)
+                retCode, option = self.focusCmd.getChooseOptionP(parent, layoutUIParam, needFocus=True)
+                if option is None:
+                    return False, None, ""
+                else:
+                    return True, option[UATTree.TAB_NAME], ""
+
+
+    '''
+    根据传入路径进行退出菜单的动作。
+    顺着传入的parent path,逐个确认当前页面是否为该parent。如果不是该parent,则往path后面继续遍历;如果是该parent,则执行该层parent的toParentKey
+    '''
+    def exitMenuByPath(self, path):
+        info(self.cls, "exitMenuByPath", "", INFO)
+        for i in range(0, path.__len__()):
+            parent = path[i]
+            ret = self.checkParentChoose(parent)
+            info(self.cls, "exitMenuByPath", "check parent:%s,ret=%d"%(parent[UATTree.TAB_NAME],ret), INFO)
+            if ret < 1:
+                continue
+            toParentKey = parent[UATTree.TAB_TOPARENT_KEY]
+            info(self.cls, "exitMenuByPath", "now is in parent:%s"%parent[UATTree.TAB_NAME], INFO)
+            if toParentKey[UATTree.Key_Event] == [] and toParentKey[UATTree.Key_IR] == []:
+                info(self.cls, "exitMenuByPath", "parent:%s has no toparent_key, using default back" % parent[UATTree.TAB_NAME], INFO)
+                self.executeKeyByType("back", UATTree.Key_Event)
+                time.sleep(1)
+            else:
+                self.executeKey(toParentKey)
+            time.sleep(1)
+            ret = self.executeDialog(parent, False)
+            if ret < 1:
+                return False
+        return True
+
+    def getUIObjPic(self, obj):
+        picName = time.time()
+        picPath = os.path.join(getSATTmpDIR(), "%s.png"%picName)
+        if obj == None:
+            return False, picPath, ""
+        self.CC.takePicture(picPath)
+        try:
+            bounds = obj.info["bounds"]
+            area = [bounds["left"], bounds["top"], bounds["right"], bounds["bottom"]]
+            cutPicPath = os.path.join(getSATTmpDIR(), "%s_cutPic_%s.png"%(picName, area))
+            self.imgCMP.saveCropPic(picPath, cutPicPath, area)
+        except Exception,e:
+            return False, picPath, ""
+        return True, picPath, cutPicPath
+
+    def infoViewCmpText(self, cmpText, picPath):
+        ocrParamList = [("ENG_CHN", 10000),
+                        ("ENG_CHN", 10001)]
+        for ocrParam in ocrParamList:
+            ocr_str = self.ocrConvert.getStr(picPath, ocrParam[0], ocrParam[1])
+            info(self.cls, "infoViewCmpText", "cmpText:%s ocr_str:%s ocrParam:%s"%(cmpText, ocr_str, ocrParam),INFO)
+            if strcmp(ocr_str, cmpText):
+                info(self.cls, "infoViewCmpText", "OCR文字识别对比成功",INFO)
+                return 1
+        else:
+            error(self.cls, "infoViewCmpText", "Waring:OCR文字识别对比失败!!",ERROR)
+            return 0
+
+    def infoViewCmpPic(self, cmpPic, picPath):
+        cmpImg = cv.imread(cmpPic)
+        cutImg = cv.imread(picPath)
+        result = self.imgCMP.cmpImgTotal(cmpImg, cutImg)
+        if result is True:
+            info(self.cls, "infoViewCmpText", "图片对比成功", INFO)
+            return 1
+        else:
+            error(self.cls, "infoViewCmpText", "Waring:图片对比失败!!",ERROR)
+            return 0
+
+
+    def getValueByTextResourceId(self, textResourceId):
+        try:
+            textObj = self.pyU.getUiObject(resourceId=textResourceId)
+            text = textObj.info['text']
+            digitList = getDigitFromString(text)
+            return digitList[-1]
+        except Exception, e:
+            return None
+
+    def getValueInObjectByTextResourceId(self, object, textResourceId):
+        try:
+            for i in range(object.info["childCount"]):
+                childUIObject = object.child_by_instance(i, resourceId=textResourceId)
+                if childUIObject.exists:
+                    text = childUIObject.info['text']
+                    digitList = getDigitFromString(text)
+                    return digitList[-1]
+            else:
+                return None
+        except Exception, e:
+            error(self.cls, "getValueInObjectByTextResourceId", "getValueInObjectByByTextResourceId:Object loading error!!!!!!error msg:%s"%e, ERROR)
+            return None

+ 663 - 660
ssat_sdk/UATree/UAT_tree.py

@@ -1,661 +1,664 @@
-# -*- coding:utf-8 -*-
-import os, sys, time,json
-from collections import OrderedDict
-from ssat_sdk.utils.string_util import strToList
-from UAT_log import error,info,debug
-DEBUG = True
-INFO = True
-ERROR = True
-class FirstLevel():
-    ParentCol = 0
-    ShortCutKeyCol = ParentCol + 1
-    UIViewCol = ParentCol + 2
-    MoveKeyCol = ParentCol + 3
-    ToParentKeyCol = ParentCol + 4
-    LayoutCol = ParentCol + 5
-    OptionCol = ParentCol + 6
-    OptionViewCol = ParentCol + 7
-    FocusSelectCol = ParentCol + 8
-    FocuseViewCol = ParentCol + 9
-    EnterKeyCol = ParentCol + 10
-    TextValueCol = ParentCol + 11
-    InfoViewCol = ParentCol + 12
-    OthersCol = ParentCol + 13
-
-class NLevel():
-    ParentCol = 0
-    UIViewCol = ParentCol + 1
-    MoveKeyCol = ParentCol + 2
-    ToParentKeyCol = ParentCol + 3
-    LayoutCol = ParentCol + 4
-    OptionCol = ParentCol + 5
-    OptionViewCol = ParentCol + 6
-    FocusSelectCol = ParentCol + 7
-    FocuseViewCol = ParentCol + 8
-    EnterKeyCol = ParentCol + 9
-    TextValueCol = ParentCol + 10
-    InfoViewCol = ParentCol + 11
-    OthersCol = ParentCol + 12
-
-class DialogLevel():
-    DialogCol = 0
-    UIViewCol = DialogCol + 1
-    MoveKeyCol = DialogCol + 2
-    ToParentKeyCol = DialogCol + 3
-    LayoutCol = DialogCol + 4
-    OptionCol = DialogCol + 5
-    OptionViewCol = DialogCol + 6
-    FocusSelectCol = DialogCol + 7
-    FocuseViewCol = DialogCol + 8
-    EnterKeyCol = DialogCol + 9
-    TextValueCol = DialogCol + 10
-    InfoViewCol = DialogCol + 11
-    OthersCol = DialogCol + 12
-
-FirstPrev = "first-prev"
-class UATTree():
-    TAB_NAME = "name"
-    TAB_SHORTCUT_KEY = "shortcut_key"
-    TAB_UI_VIEW = "uiview"
-    TAB_MOVE_KEY = "move_key"
-    TAB_TOPARENT_KEY = "toparent_key"
-    TAB_LAYOUT = "layout"
-    TAB_OPTION = "option"
-    TAB_LEVEL = "level"
-    TAB_PREV_PARENT = "prev_parent"
-    TAB_NEXT_PARENT = "next_parent"
-    TAB_OPTION_VIEW = "optionView"
-    TAB_FOCUS_SELECT = "focus-select"
-    TAB_FOCUSE_VIEW = "focuseView"
-    TAB_TEXT_VALUE = "textValue"
-    TAB_ENTER_KEY = "enter_key"
-    TAB_PARENT_NAME = "parentName"
-    TAB_INFO_VIEW = "infoView"
-    TAB_OTHERS = "others"
-
-    def __init__(self):
-        self.treeDict = OrderedDict()
-        self.dialogDict = OrderedDict()
-        self.cls = "UATTree"
-
-    '''
-    char为该项参数的括符符号,必须成对带入。默认为中括号[]
-    '''
-    #将数据从excel表格里取出来,只返回括号里面的内容
-    def parseParam(self, key, params, char = "[]"):
-        # 为防止key在其他地方存在相同关键字,添加一个"["做区别
-        key1 = key + char[0]
-        keyIndex = params.find(key1)
-        if keyIndex == -1:
-            return ""
-        # key.__len__()-1 为去掉"["的处理
-        str1 = params[keyIndex + key1.__len__()-1:params.__len__()]
-        i1 = str1.find(char[0])
-        if key == self.View_Bounds or key == self.View_sambounds:
-            i2 = str1.find("]]")+1
-        else:
-            i2 = str1.find(char[1])
-        if i1 == -1 or i2 == -1:
-            return ""
-        str2 = str1[i1 + 1: i2]
-        return str2.strip()
-
-    def findAllParam(self, key, params,seg="[]"):
-        paramArr = []
-        # for i in range(2):
-        while True:
-            keyIndex = params.find(key)
-            if keyIndex == -1:
-                break
-            endIndex = params.find(seg[1])
-            oneParam = params[keyIndex+key.__len__()+1:endIndex]
-            params = params[endIndex+1:]
-            paramArr.append(oneParam)
-        return paramArr
-
-    '''
-    返回:{key:value,key2:value2,...}
-    '''
-    def parseMulParam(self, keyList, params):
-        paramDict = {}
-        for key in keyList:
-            if key == 'text':
-                value = self.parseMulText(params)
-            else:
-                value = self.parseParam(key, params)
-            paramDict[key] = value
-        return paramDict
-
-    '''
-    例如:enter_key,event[]ir[]同时存在多个
-    返回:[[key,value],[key,value],...]
-    '''
-    def parseMulParam_Same(self, keyList, params):
-        mulParam=[]
-        for key in keyList:
-            paramArr = self.findAllParam(key, params)
-            for param in paramArr:
-                keyItem = [key]
-                keyItem.append(param)
-                mulParam.append(keyItem)
-        return mulParam
-
-    #KeyDict数据
-    Key_AM = "am" #android am命令
-    Key_Event = "event" #android KeyEvent
-    Key_IR = "ir" #红外按键
-    Key_Input = "input" #Android input命令
-    Max_Try = "max_try"
-    #根据key的字符串,解析出key的字典。格式:am[]event[]ir[]组合
-    '''
-    用于解析move key参数
-    '''
-    def parseKey(self, keyStr):
-        keyList = [self.Key_AM, self.Key_Event, self.Key_IR, self.Key_Input, self.Max_Try]
-        keyDict = self.parseMulParam(keyList, keyStr)
-        keyDict[self.Key_Event] = strToList(keyDict[self.Key_Event], ",")
-        keyDict[self.Key_IR] = strToList(keyDict[self.Key_IR], ",")
-        return keyDict
-
-    '''
-    不考虑按键种类,解析按键名、等待时间等参数,enter,wait=1,enterParent=hdmi_settings
-    返回结果:{"keyvalue":[left,right,enter],"wait":'1',"times":"1"}
-    '''
-    def parseKeyParam(self, keyParamStr):
-        paramDict = {self.Key_Value:[],self.Wait_Time:'0', self.Key_Times:'1', self.Duration_Time:'1'}
-        paramArr = strToList(keyParamStr,",")
-        keyValue=[]
-        for param in paramArr:
-            if "=" not in param:
-                keyValue.append(param.strip())
-            else:
-                pArr = strToList(param,"=")
-                paramDict[pArr[0].strip().lower()] = pArr[1].strip()
-        paramDict[self.Key_Value] = keyValue
-        return paramDict
-
-    # enterKeyDict数据
-    Wait_Time = "wait"
-    Duration_Time = "duration"
-    Enter_Parent = "enterparent"
-    Key_Value = "keyvalue"
-    Key_Type = "keytype"
-    Key_Param = "key_param"
-    Key_Times = "times"
-    # 根据key的字符串,解析出key的字典。格式:am[]event[]ir[]组合
-    '''
-    返回值:[{"enterParent":"","key_param":{"keyType","event","keyvalue":[left,right,enter],"wait":1,"enterParent":"hdmi_settings"}}
-    ,...]
-    '''
-    def parseEnterKey(self, keyStr, optionName):
-        keyList = [self.Key_Event, self.Key_IR, self.Key_Input]
-        keyArr = self.parseMulParam_Same(keyList, keyStr)
-        retArr = []
-        for keyItem in keyArr:
-            nextPDict = {} #{"enterParent":"","key_param":{"keyType","event","keyvalue":[left,right,enter],"wait":1,"enterParent":"hdmi_settings"}}
-            keyParamDict = self.parseKeyParam(keyItem[1])
-            keyParamDict[self.Key_Type] = keyItem[0]
-            nextPDict[self.Key_Param] = keyParamDict
-            keyParamDict[self.Wait_Time] = float(keyParamDict[self.Wait_Time])
-            keyParamDict[self.Duration_Time] = float(keyParamDict[self.Duration_Time])
-            keyParamDict[self.Key_Times] = int(keyParamDict[self.Key_Times])
-            if keyParamDict.has_key(self.Enter_Parent) is False:
-                keyParamDict[self.Enter_Parent] = optionName
-            nextPDict[self.Enter_Parent] = keyParamDict[self.Enter_Parent]
-            retArr.append(nextPDict)
-        return retArr
-
-    #Android View数据
-    View_ID = "resid" #android view resource-id
-    View_Text = "text" #android view 文本内容。 用于焦点定位,文本组件坐标被焦点组件坐标包含
-    View_Desc = "desc" #android view描述
-    View_Bounds = "bounds" #android view 坐标
-    View_Index = "index"
-    View_Class = "class"
-    '''
-    主要用于infoView数据解析
-    '''
-    def parseView(self, viewStr):
-        keyList = [self.View_ID, self.View_Text, self.View_Desc, self.View_Bounds, self.View_Index, self.View_Class]
-        paramDict = self.parseMulParam(keyList, viewStr)
-        paramDict[self.View_Bounds] = self.strToBounds(paramDict[self.View_Bounds])
-        return paramDict
-
-    '''
-    将‘[801,116][1280,180]’转成数组[[801,116][1280,180]]
-    '''
-    def strToBounds(self, bstr):
-        char = "[]"
-        # print "strToBounds,bstr:", bstr,bstr.__len__()
-        if bstr.__len__() < 10:
-            return []
-        keyIndex = 1
-        # key.__len__()-1 为去掉"["的处理
-        i1 = bstr.find(char[0])
-        i2 = bstr.find(char[1])
-        if i1 == -1 or i2 == -1:
-            return []
-        str1 = bstr[i1: i2 + 1]
-        str2 = bstr[i2 + 1: bstr.__len__()]
-        return [eval(str1), eval(str2)]
-
-    '''
-    解析多文本option,更改key亦可作为其他参数
-    '''
-    def parseMulText(self,params, char="[]",key = 'text'):
-        key1 = key + char[0] + char[0]
-        keyIndex = params.find(key1)
-        if keyIndex == -1:
-            text = self.parseParam(key, params)  # 只有一层中括号
-        else:
-            text = []
-            str1 = params[keyIndex + key1.__len__() - 1:len(params)]
-            i1 = str1.find(char[0])
-            i2 = str1.find((char[1] + char[1]))
-            if i1 == -1 or i2 == -1:
-                return ""
-            str2 = str1[i1:i2 + 1]  # 只去掉外括号
-            strList = strToList(str2, char[1] + "," + char[0])
-            for strs in strList:
-                text.append(strs.strip(char[0]).strip(char[1]))
-        return text
-		
-    View_sambounds = "sambounds" #采样时的样本坐标,用于和select-focus、focusView的坐标做相对比较,计算相对位置,用于确定是否被选中。
-    '''
-    用于optionView的数据解析
-    '''
-    def parseOptionView(self, viewStr):
-        keyList = [self.View_ID, self.View_Text, self.View_Desc, self.View_Bounds, self.View_Index, self.View_Class
-                   ,self.View_sambounds]
-        paramDict = self.parseMulParam(keyList, viewStr)
-        paramDict[self.View_Bounds] = self.strToBounds(paramDict[self.View_Bounds])
-        paramDict[self.View_sambounds] = self.strToBounds(paramDict[self.View_sambounds])
-        return paramDict
-
-    # UI 界面数据
-    UIView_Activity = "activity"  # android activity
-    UIVIew_Action = "action"
-    UIView_ID = "resid"  # android view resource-id
-    UIView_Text = "text"  # android view 文本内容
-    UIView_Desc = "desc"  # android view描述
-    UIView_Bounds = "bounds"  # android view 坐标
-    UIView_Dialog_F = "dialog_forward"  # 进入时可能出现的弹窗
-    UIView_Dialog_B = "dialog_back"  # 退出时可能出现的弹窗
-    def parseUIView(self, viewStr):
-        keyList = [self.UIView_Activity, self.UIVIew_Action, self.UIView_ID, self.UIView_Text, self.UIView_Desc,
-                   self.UIView_Bounds, self.UIView_Dialog_F, self.UIView_Dialog_B]
-        uiView = self.parseMulParam(keyList, viewStr)
-        return self.parseDialogParam(uiView)
-
-    def parseDialogParam(self, uiView):
-        dialog_F_List = strToList(uiView[self.UIView_Dialog_F], ",")
-        dialog_B_List = strToList(uiView[self.UIView_Dialog_B], ",")
-        self.addDialogList(dialog_F_List, uiView, True)
-        self.addDialogList(dialog_B_List, uiView, False)
-        return uiView
-
-    def addDialogList(self, dialog_List, uiView, isForward):
-        for dialog in dialog_List:
-            dialog_A = strToList(dialog.lower(), "&")
-            if dialog_A.__len__() == 2:
-                dialog_Name = dialog_A[0]
-                dialog_Choose = dialog_A[1]
-            else:
-                error(str(self.__class__), "parseDialogParam", "Dialog %s set error"%dialog, ERROR)
-                return
-            if self.dialogDict.has_key(dialog_Name):
-                if isForward is True:
-                    dialog_Type = self.UIView_Dialog_F
-                else:
-                    dialog_Type = self.UIView_Dialog_B
-                uiView[dialog_Type] = {}
-                uiView[dialog_Type][dialog_Name] = self.dialogDict[dialog_Name]
-                uiView[dialog_Type][dialog_Name]["choose"] = self.dialogDict[dialog_Name][UATTree.TAB_OPTION][dialog_Choose]
-            else:
-                error(str(self.__class__), "parseDialogParam", "Dialog %s read fail"%dialog_Name, ERROR)
-
-
-    # Android Layout数据
-    Layout_ID = "resid"  # android view resource-id
-    Layout_Desc = "desc"  # android view描述
-    Layout_Bounds = "bounds"  # android view 坐标
-    Layout_Class = "class" #android view class类型
-    Layout_Limit = "limit" #layout是否限制optionView焦点操作范围。0:不限制,1:限制,默认为0
-    '''
-    layout:各属性值,用于综合确定一个布局组件
-    :return layout 字典:{"resid":"","desc":"","bounds":""}
-    '''
-    def parseLayout(self, viewStr):
-        keyList = [self.Layout_ID, self.Layout_Desc, self.Layout_Bounds, self.Layout_Class, self.Layout_Limit]
-        paramDict = self.parseMulParam(keyList, viewStr)
-        if paramDict.has_key(self.Layout_Limit) and paramDict[self.Layout_Limit].__len__()>0:
-            paramDict[self.Layout_Limit] = int(paramDict[self.Layout_Limit]) #字符'0'转数字0
-        paramDict[self.View_Bounds] = self.strToBounds(paramDict[self.View_Bounds])
-        return paramDict
-
-    #Android FocusView数据
-    Focus_ID = "resid" #android view resource-id
-    Focus_Text = "text" #android view 文本内容
-    Focus_Desc = "desc" #android view描述
-    View_Bounds = "bounds" #android view 坐标
-    View_Class = "class"
-    def parseFocus(self, viewStr):
-        keyList = [self.Focus_ID, self.Focus_Text, self.Focus_Desc, self.View_Bounds, self.View_Class,
-                   self.View_sambounds]
-        paramDict = self.parseMulParam(keyList, viewStr)
-        paramDict[self.View_Bounds] = self.strToBounds(paramDict[self.View_Bounds])
-        paramDict[self.View_sambounds] = self.strToBounds(paramDict[self.View_sambounds])
-        return paramDict
-
-    #Value数据
-    ValueView_ID = "resid" #android view resource-id
-    ValueView_Text = "text" #android view 文本内容
-    ValueView_Desc = "desc" #android view描述
-    ValueView_Bounds = "bounds"
-    ValueView_Class = "class"
-    ValueView_Value = "value"  # 输入的值范围。格式:first, second, ...  或者 0-100
-    ValueView_Min = "minvalue" # 如果是数值范围形式的值,返回最小值
-    ValueView_Max = "maxvalue" # 如果是数值范围形式的值,返回最小值
-    ValueView_StepSize = "stepsize" # 如果是数值范围形式的值,每次调整数值的步长。读不到此参数时,默认为1。
-    ValueView_Duration = "duration" # 如果是数值范围形式的值,每次调整数值的间隔时间。读不到此参数时,默认为0.2。
-    ValueView_Sign = "sign" # 针对前缀为字母,后缀为数字形式的值,用来保存前缀
-    def parseValueView(self, viewStr):
-        keyList = [self.ValueView_ID, self.ValueView_Text, self.ValueView_Desc, self.ValueView_Value,
-                   self.ValueView_StepSize, self.ValueView_Duration, self.ValueView_Bounds, self.ValueView_Class,
-                   self.View_sambounds]
-        paramDict = self.parseMulParam(keyList, viewStr)
-        valueStr = paramDict[self.ValueView_Value]
-        sign = []
-        valueListDelStr = []
-        flagList = []
-        flag_v = False # value前缀不为字母
-        if valueStr != "":
-            i = valueStr.find(",")
-            if i != -1:
-                valueList = strToList(valueStr, ",")
-                isRange = False
-
-                # 前缀字母,后缀数字
-                for val in valueList:
-                    if (ord(val[0])>57 or ord(val[0])<48) and (47 < ord(val[1]) < 58):
-                        sign.append(val[0])
-                        valueDelStr = val.strip(val[0])
-                        valueListDelStr.append(valueDelStr)
-                        flagList.append(True)  #value前缀为字母
-                    else:
-                        flagList.append(False)
-                for f in flagList:
-                    flag_v = f
-                    flag_v = flag_v and f
-                if flag_v == False:
-                    sign = []
-                # 处理range类型(数值)
-                try:
-                    if valueList.__len__() == 2:
-                        if flag_v == True: #//
-                            valueList = valueListDelStr
-                            paramDict[self.ValueView_Min] = -float(valueList[0])#//
-                        else:
-                            paramDict[self.ValueView_Min] = float(valueList[0])
-                        paramDict[self.ValueView_Max] = float(valueList[1])
-                        isRange = True
-                except Exception,ValueError:
-                    info(self.cls, "parseValueView", "valueList %s not range value.", INFO)
-                # 处理非range类型(选项)
-                if isRange is False:
-                    for value in valueList:
-                        valueViewStr = self.parseParam(value, viewStr, "{}")
-                        if valueViewStr == "":
-                            debug(self.cls, "parseValueView", "value %s 没有任何配置信息!!!请加入该value信息,否则可能将导致设值失败!!!"%value, DEBUG)
-                        # 读出来的数值未带大括号,需加上大括号,并且json格式化
-                        valueView = eval("{%s}" % valueViewStr)
-                        paramDict[value] = valueView
-        paramDict[self.ValueView_Sign] = sign
-        return paramDict
-
-    #focus-select数据,不仅有select或者focus类型,还有指定组件属性情况,用于处理界面多个聚焦方式出现的场景
-    FS_Type = "type"
-    FS_ID = "resid" #android view resource-id
-    FS_Text = "text" #android view 文本内容。
-    FS_Desc = "desc" #android view描述
-    FS_Bounds = "bounds" #android view 坐标
-    FS_Class = "class"
-    def parseFocusSelectType(self, fsType):
-        keyList = [self.FS_Type,self.FS_ID,self.FS_Text,self.FS_Desc,self.FS_Bounds,
-                   self.View_sambounds,self.FS_Class]
-        paramDict = self.parseMulParam(keyList, fsType)
-        paramDict[self.View_sambounds] = self.strToBounds(paramDict[self.View_sambounds])
-        return paramDict
-
-    '''
-    扫描parent数据行时,要把建立当前parent的prevParent和nextParent关系。
-    '''
-    def addParent(self,level, parentName, shortkey, uiview, move_key, toparent_key, layout, others):
-        # print "addParent, level,parentName:", level, parentName
-        if not self.treeDict.has_key(level):
-            self.treeDict[level] = {}
-        parentDict = {}
-        parentDict[UATTree.TAB_NAME] = parentName
-        parentDict[UATTree.TAB_SHORTCUT_KEY] = self.parseKey(shortkey)
-        parentDict[UATTree.TAB_UI_VIEW] = self.parseUIView(uiview)
-        parentDict[UATTree.TAB_MOVE_KEY] = self.parseKey(move_key)
-        parentDict[UATTree.TAB_TOPARENT_KEY] = self.parseKey(toparent_key)
-        parentDict[UATTree.TAB_LAYOUT] = self.parseLayout(layout)
-        parentDict[UATTree.TAB_OPTION] = {}
-        parentDict[UATTree.TAB_LEVEL] = level
-        parentDict[UATTree.TAB_PREV_PARENT] = None
-        parentDict[UATTree.TAB_NEXT_PARENT] = {}
-        parentDict[UATTree.TAB_OTHERS] = self.parseOthers(others)
-        if "first" <> level:
-            prevParent = self.getPreParentByParent(parentDict)
-            if prevParent is not None:
-                parentDict[UATTree.TAB_PREV_PARENT] = prevParent
-                prevParent[UATTree.TAB_NEXT_PARENT][parentName] = parentDict
-        self.treeDict[level][parentName] = parentDict
-
-    '''
-    扫描option行的时候,会把option字典关联到相应的parent字典里
-    '''
-    def addOption(self, level, parentName, optionName, optionView, focusSelect, focusView, enterKey, textValue, infoView):
-        # print "addOption, level,parentName,optionName:", level, parentName,optionName
-        if self.treeDict.has_key(level) and self.treeDict[level].has_key(parentName):
-            optionDict = {}
-            optionDict[UATTree.TAB_NAME] = optionName
-            optionDict[UATTree.TAB_OPTION_VIEW] = self.parseOptionView(optionView)
-            optionDict[UATTree.TAB_FOCUS_SELECT] = self.parseFocusSelectType(focusSelect)
-            optionDict[UATTree.TAB_FOCUSE_VIEW] = self.parseFocus(focusView)
-            optionDict[UATTree.TAB_ENTER_KEY] = self.parseEnterKey(enterKey, optionName)
-            optionDict[UATTree.TAB_TEXT_VALUE] = self.parseValueView(textValue)
-            optionDict[UATTree.TAB_INFO_VIEW] = self.parseView(infoView)
-            optionDict[UATTree.TAB_PARENT_NAME] = parentName
-            optionDict[UATTree.TAB_LEVEL] = level
-            self.treeDict[level][parentName][UATTree.TAB_OPTION][optionName] = optionDict
-
-    def addDialog(self, parentName, shortkey, uiview, move_key, toparent_key, layout, others):
-        # print "addDialog, parentName:", parentName
-        parentDict = {}
-        parentDict[UATTree.TAB_NAME] = parentName
-        parentDict[UATTree.TAB_SHORTCUT_KEY] = self.parseKey(shortkey)
-        parentDict[UATTree.TAB_UI_VIEW] = self.parseUIView(uiview)
-        parentDict[UATTree.TAB_MOVE_KEY] = self.parseKey(move_key)
-        parentDict[UATTree.TAB_TOPARENT_KEY] = self.parseKey(toparent_key)
-        parentDict[UATTree.TAB_LAYOUT] = self.parseLayout(layout)
-        parentDict[UATTree.TAB_OPTION] = {}
-        parentDict[UATTree.TAB_OTHERS] = self.parseDialogOthers(others)
-
-        self.dialogDict[parentName] = parentDict
-
-    def addDialogOption(self, parentName, optionName, optionView, focusSelect, focusView, enterKey, textValue, infoView):
-        # print "addDialogOption, parentName,optionName:", parentName, optionName
-        if self.dialogDict.has_key(parentName):
-            optionDict = {}
-            optionDict[UATTree.TAB_NAME] = optionName
-            optionDict[UATTree.TAB_OPTION_VIEW] = self.parseOptionView(optionView)
-            optionDict[UATTree.TAB_FOCUS_SELECT] = self.parseFocusSelectType(focusSelect)
-            optionDict[UATTree.TAB_FOCUSE_VIEW] = self.parseFocus(focusView)
-            optionDict[UATTree.TAB_ENTER_KEY] = self.parseEnterKey(enterKey,optionName)
-            optionDict[UATTree.TAB_TEXT_VALUE] = self.parseValueView(textValue)
-            optionDict[UATTree.TAB_INFO_VIEW] = self.parseView(infoView)
-            optionDict[UATTree.TAB_PARENT_NAME] = parentName
-            self.dialogDict[parentName][UATTree.TAB_OPTION][optionName] = optionDict
-
-
-    def findParentDict(self, parentName):
-        parentName = parentName.lower()
-        levelList = self.treeDict.keys()
-        for index in range(levelList.__len__()-1, -1, -1):
-            level = levelList[index]
-            if self.treeDict[level].has_key(parentName):
-                return self.treeDict[level][parentName]
-        return None
-
-    def getSubOptionDict(self, parentName):
-        parentDict = self.findParentDict(parentName)
-        if parentDict is not None:
-            return parentDict[UATTree.TAB_OPTION]
-        return None
-
-    def findOption(self, optionName):
-        optionName = optionName.lower()
-        levelList = self.treeDict.keys()
-        for index in range(levelList.__len__()-1, -1, -1):
-            level = levelList[index]
-            levelDict = self.treeDict[level]
-            for parent in levelDict.keys():
-                parentDict = levelDict[parent]
-                for option in parentDict.keys():
-                    optionDict = parentDict[UATTree.TAB_OPTION]
-                    if optionDict.has_key(optionName):
-                        return optionDict[optionName]
-
-    def getOpitonInLevel(self,targetOptionName, level):
-        levelDict = self.treeDict[level]
-        for parentName in levelDict.keys():
-            parentDict = levelDict[parentName]
-            for optionName in parentDict[UATTree.TAB_OPTION].keys():
-                if targetOptionName == optionName:
-                    return parentDict[UATTree.TAB_OPTION][optionName]
-        else:
-            return None
-
-    '''
-   检测option的enter_key中是否存在对应parentName
-   '''
-    def checkEnterParentEK(self, enterKey, parentName):
-        # print "checkEnterParentEK,enterKey,parentName:",enterKey, parentName
-        for keyItem in enterKey:
-            if keyItem[self.Enter_Parent] == parentName:
-                return True
-        return False
-    '''
-    检测option字典,是否下一级nextParent包含这一个parentName
-    '''
-    def checkOptionEnterParent(self, option, parentName):
-        enterKey = option[self.TAB_ENTER_KEY]
-        if option[self.TAB_NAME] == parentName:
-            return True
-        return self.checkEnterParentEK(enterKey, parentName)
-
-    '''
-    根据parentName,在EnterKey所有列表中,找到匹配的enterKey字典选项
-    返回值:{"enterParent":"","key_param":{"keyType","event","keyvalue":[left,right,enter],"wait":1,"enterParent":"hdmi_settings"}
-    '''
-    def getEKByParent(self, enterKeys, parentName):
-        for keyItem in enterKeys:
-            if keyItem[self.Enter_Parent] == parentName:
-                return keyItem[self.Key_Param]
-        return None
-    '''
-    获取option的parent
-    '''
-    def getParentByOption(self, option):
-        parentName = option[UATTree.TAB_PARENT_NAME]
-        levelName = option[UATTree.TAB_LEVEL]
-        if self.treeDict[levelName].has_key(parentName):
-            parent = self.treeDict[levelName][parentName]
-            return parent
-        else:
-            return None
-
-    '''
-    根据Parent数据,查上一级parent。
-    '''
-    def getPreParentByParent(self, parent):
-        prevLevel = self.getPrevLevel(parent[self.TAB_LEVEL])
-        levelDict = self.treeDict[prevLevel]
-        # print "getPreParentByParent,levelDict:",levelDict
-        for parentName in levelDict.keys():
-            parentDict = levelDict[parentName]
-            # print "getPreParentByParent,parentDict:", parentDict
-            for optionName in parentDict[UATTree.TAB_OPTION].keys():
-                option = parentDict[UATTree.TAB_OPTION][optionName]
-                # print "getPreParentByParent,option:", option
-                if self.checkOptionEnterParent(option, parent[self.TAB_NAME]):
-                    return parentDict
-        error(str(self.__class__), "getPreParentByParent",
-              "Parent %s's prevparent not found in level %s"%(parent[self.TAB_NAME], prevLevel), ERROR)
-        return None
-
-    '''
-    获取当前level的前一个level名
-    '''
-    def getPrevLevel(self, level):
-        self.levelList = self.treeDict.keys()
-        index = self.levelList.index(level)
-        if index == 0:
-            return FirstPrev
-        else:
-            return self.levelList[index-1]
-    '''
-    获取当前Parent的上一级parent
-    '''
-    def getPrevParent(self, parent):
-        return parent[UATTree.TAB_PREV_PARENT]
-
-    '''
-    获取当前parent下一级所有parent的字典
-    '''
-    def getNextParentList(self, parent):
-        return parent[UATTree.TAB_NEXT_PARENT]
-
-    def printTree(self):
-        for level in self.treeDict.keys():
-            print level
-            for parent in self.treeDict[level].keys():
-                dict1 = self.treeDict[level][parent]
-                print level, "::parent ", dict1["name"], "name", "shortcut_key", "uiview", "move_key", "toparent_key", "layout"
-                print level, "::parent ", dict1["name"], dict1["shortcut_key"], dict1["uiview"], dict1["move_key"], dict1["toparent_key"], dict1["layout"]
-                for option in dict1["option"].keys():
-                    dict2 = dict1["option"][option]
-                    print level, "::option ", dict2
-
-    Key_HeartBeat = "heartbeat_key"
-    def parseOthers(self, othersStr):
-        othersDict = {}
-        othersDict[self.Key_HeartBeat] = self.parseParam(self.Key_HeartBeat, othersStr, char="{}")
-        hb_keyDict = {}
-        if othersDict[self.Key_HeartBeat] != "":
-            print "Key_HeartBeat_Str:",othersDict[self.Key_HeartBeat]
-            hb_keyDict = self.parseHeartBeatKey(othersDict[self.Key_HeartBeat])
-        othersDict[self.Key_HeartBeat] = hb_keyDict
-        return othersDict
-		
-    def parseHeartBeatKey(self, hb_keyStr):
-        keyList = [self.Key_Event, self.Key_IR]
-        keyDict = self.parseMulParam(keyList, hb_keyStr)
-        keyDict[self.Key_Event] = strToList(keyDict[self.Key_Event], ",")
-        keyDict[self.Key_IR] = strToList(keyDict[self.Key_IR], ",")
-        return keyDict
-		
-    def parseDialogOthers(self, othersStr):
-        othersDict = {}
-        othersDict[self.Key_HeartBeat] = self.parseParam(self.Key_HeartBeat, othersStr, char="{}")
-        hb_keyDict = {}
-        if othersDict[self.Key_HeartBeat] != "":
-            print "Key_HeartBeat_Str:",othersDict[self.Key_HeartBeat]
-            hb_keyDict = self.parseHeartBeatKey(othersDict[self.Key_HeartBeat])
-        othersDict[self.Key_HeartBeat] = hb_keyDict
-        return othersDict
-
-if __name__ == '__main__':
+# -*- coding:utf-8 -*-
+import os, sys, time,json
+from collections import OrderedDict
+from ssat_sdk.utils.string_util import strToList
+from UAT_log import error,info,debug
+DEBUG = True
+INFO = True
+ERROR = True
+class FirstLevel():
+    ParentCol = 0
+    ShortCutKeyCol = ParentCol + 1
+    UIViewCol = ParentCol + 2
+    MoveKeyCol = ParentCol + 3
+    ToParentKeyCol = ParentCol + 4
+    LayoutCol = ParentCol + 5
+    OptionCol = ParentCol + 6
+    OptionViewCol = ParentCol + 7
+    FocusSelectCol = ParentCol + 8
+    FocuseViewCol = ParentCol + 9
+    EnterKeyCol = ParentCol + 10
+    TextValueCol = ParentCol + 11
+    InfoViewCol = ParentCol + 12
+    OthersCol = ParentCol + 13
+
+class NLevel():
+    ParentCol = 0
+    UIViewCol = ParentCol + 1
+    MoveKeyCol = ParentCol + 2
+    ToParentKeyCol = ParentCol + 3
+    LayoutCol = ParentCol + 4
+    OptionCol = ParentCol + 5
+    OptionViewCol = ParentCol + 6
+    FocusSelectCol = ParentCol + 7
+    FocuseViewCol = ParentCol + 8
+    EnterKeyCol = ParentCol + 9
+    TextValueCol = ParentCol + 10
+    InfoViewCol = ParentCol + 11
+    OthersCol = ParentCol + 12
+
+class DialogLevel():
+    DialogCol = 0
+    UIViewCol = DialogCol + 1
+    MoveKeyCol = DialogCol + 2
+    ToParentKeyCol = DialogCol + 3
+    LayoutCol = DialogCol + 4
+    OptionCol = DialogCol + 5
+    OptionViewCol = DialogCol + 6
+    FocusSelectCol = DialogCol + 7
+    FocuseViewCol = DialogCol + 8
+    EnterKeyCol = DialogCol + 9
+    TextValueCol = DialogCol + 10
+    InfoViewCol = DialogCol + 11
+    OthersCol = DialogCol + 12
+
+FirstPrev = "first-prev"
+class UATTree():
+    TAB_NAME = "name"
+    TAB_SHORTCUT_KEY = "shortcut_key"
+    TAB_UI_VIEW = "uiview"
+    TAB_MOVE_KEY = "move_key"
+    TAB_TOPARENT_KEY = "toparent_key"
+    TAB_LAYOUT = "layout"
+    TAB_OPTION = "option"
+    TAB_LEVEL = "level"
+    TAB_PREV_PARENT = "prev_parent"
+    TAB_NEXT_PARENT = "next_parent"
+    TAB_OPTION_VIEW = "optionView"
+    TAB_FOCUS_SELECT = "focus-select"
+    TAB_FOCUSE_VIEW = "focuseView"
+    TAB_TEXT_VALUE = "textValue"
+    TAB_ENTER_KEY = "enter_key"
+    TAB_PARENT_NAME = "parentName"
+    TAB_INFO_VIEW = "infoView"
+    TAB_OTHERS = "others"
+
+    def __init__(self):
+        self.treeDict = OrderedDict()
+        self.dialogDict = OrderedDict()
+        self.cls = "UATTree"
+
+    '''
+    char为该项参数的括符符号,必须成对带入。默认为中括号[]
+    '''
+    #将数据从excel表格里取出来,只返回括号里面的内容
+    def parseParam(self, key, params, char = "[]"):
+        # 为防止key在其他地方存在相同关键字,添加一个"["做区别
+        key1 = key + char[0]
+        keyIndex = params.find(key1)
+        if keyIndex == -1:
+            return ""
+        # key.__len__()-1 为去掉"["的处理
+        str1 = params[keyIndex + key1.__len__()-1:params.__len__()]
+        i1 = str1.find(char[0])
+        if key == self.View_Bounds or key == self.View_sambounds:
+            i2 = str1.find("]]")+1
+        else:
+            i2 = str1.find(char[1])
+        if i1 == -1 or i2 == -1:
+            return ""
+        str2 = str1[i1 + 1: i2]
+        return str2.strip()
+
+    def findAllParam(self, key, params,seg="[]"):
+        paramArr = []
+        # for i in range(2):
+        while True:
+            keyIndex = params.find(key)
+            if keyIndex == -1:
+                break
+            endIndex = params.find(seg[1])
+            oneParam = params[keyIndex+key.__len__()+1:endIndex]
+            params = params[endIndex+1:]
+            paramArr.append(oneParam)
+        return paramArr
+
+    '''
+    返回:{key:value,key2:value2,...}
+    '''
+    def parseMulParam(self, keyList, params):
+        paramDict = {}
+        for key in keyList:
+            if key == 'text':
+                value = self.parseMulText(params)
+            else:
+                value = self.parseParam(key, params)
+            paramDict[key] = value
+        return paramDict
+
+    '''
+    例如:enter_key,event[]ir[]同时存在多个
+    返回:[[key,value],[key,value],...]
+    '''
+    def parseMulParam_Same(self, keyList, params):
+        mulParam=[]
+        for key in keyList:
+            paramArr = self.findAllParam(key, params)
+            for param in paramArr:
+                keyItem = [key]
+                keyItem.append(param)
+                mulParam.append(keyItem)
+        return mulParam
+
+    #KeyDict数据
+    Key_AM = "am" #android am命令
+    Key_Event = "event" #android KeyEvent
+    Key_IR = "ir" #红外按键
+    Key_Input = "input" #Android input命令
+    Max_Try = "max_try"
+    #根据key的字符串,解析出key的字典。格式:am[]event[]ir[]组合
+    '''
+    用于解析move key参数
+    '''
+    def parseKey(self, keyStr):
+        keyList = [self.Key_AM, self.Key_Event, self.Key_IR, self.Key_Input, self.Max_Try]
+        keyDict = self.parseMulParam(keyList, keyStr)
+        keyDict[self.Key_Event] = strToList(keyDict[self.Key_Event], ",")
+        keyDict[self.Key_IR] = strToList(keyDict[self.Key_IR], ",")
+        return keyDict
+
+    '''
+    不考虑按键种类,解析按键名、等待时间等参数,enter,wait=1,enterParent=hdmi_settings
+    返回结果:{"keyvalue":[left,right,enter],"wait":'1',"times":"1"}
+    '''
+    def parseKeyParam(self, keyParamStr):
+        paramDict = {self.Key_Value:[],self.Wait_Time:'0', self.Key_Times:'1', self.Duration_Time:'1'}
+        paramArr = strToList(keyParamStr,",")
+        keyValue=[]
+        for param in paramArr:
+            if "=" not in param:
+                keyValue.append(param.strip())
+            else:
+                pArr = strToList(param,"=")
+                paramDict[pArr[0].strip().lower()] = pArr[1].strip()
+        paramDict[self.Key_Value] = keyValue
+        return paramDict
+
+    # enterKeyDict数据
+    Wait_Time = "wait"
+    Duration_Time = "duration"
+    Enter_Parent = "enterparent"
+    Key_Value = "keyvalue"
+    Key_Type = "keytype"
+    Key_Param = "key_param"
+    Key_Times = "times"
+    # 根据key的字符串,解析出key的字典。格式:am[]event[]ir[]组合
+    '''
+    返回值:[{"enterParent":"","key_param":{"keyType","event","keyvalue":[left,right,enter],"wait":1,"enterParent":"hdmi_settings"}}
+    ,...]
+    '''
+    def parseEnterKey(self, keyStr, optionName):
+        keyList = [self.Key_Event, self.Key_IR, self.Key_Input]
+        keyArr = self.parseMulParam_Same(keyList, keyStr)
+        retArr = []
+        for keyItem in keyArr:
+            nextPDict = {} #{"enterParent":"","key_param":{"keyType","event","keyvalue":[left,right,enter],"wait":1,"enterParent":"hdmi_settings"}}
+            keyParamDict = self.parseKeyParam(keyItem[1])
+            keyParamDict[self.Key_Type] = keyItem[0]
+            nextPDict[self.Key_Param] = keyParamDict
+            keyParamDict[self.Wait_Time] = float(keyParamDict[self.Wait_Time])
+            keyParamDict[self.Duration_Time] = float(keyParamDict[self.Duration_Time])
+            keyParamDict[self.Key_Times] = int(keyParamDict[self.Key_Times])
+            if keyParamDict.has_key(self.Enter_Parent) is False:
+                keyParamDict[self.Enter_Parent] = optionName
+            nextPDict[self.Enter_Parent] = keyParamDict[self.Enter_Parent]
+            retArr.append(nextPDict)
+        return retArr
+
+    #Android View数据
+    View_ID = "resid" #android view resource-id
+    View_Text = "text" #android view 文本内容。 用于焦点定位,文本组件坐标被焦点组件坐标包含
+    View_Desc = "desc" #android view描述
+    View_Bounds = "bounds" #android view 坐标
+    View_Index = "index"
+    View_Class = "class"
+    '''
+    主要用于infoView数据解析
+    '''
+    def parseView(self, viewStr):
+        keyList = [self.View_ID, self.View_Text, self.View_Desc, self.View_Bounds, self.View_Index, self.View_Class]
+        paramDict = self.parseMulParam(keyList, viewStr)
+        paramDict[self.View_Bounds] = self.strToBounds(paramDict[self.View_Bounds])
+        return paramDict
+
+    '''
+    将‘[801,116][1280,180]’转成数组[[801,116][1280,180]]
+    '''
+    def strToBounds(self, bstr):
+        char = "[]"
+        # print "strToBounds,bstr:", bstr,bstr.__len__()
+        if bstr.__len__() < 10:
+            return []
+        keyIndex = 1
+        # key.__len__()-1 为去掉"["的处理
+        i1 = bstr.find(char[0])
+        i2 = bstr.find(char[1])
+        if i1 == -1 or i2 == -1:
+            return []
+        str1 = bstr[i1: i2 + 1]
+        str2 = bstr[i2 + 1: bstr.__len__()]
+        return [eval(str1), eval(str2)]
+
+    '''
+    解析多文本option,更改key亦可作为其他参数
+    '''
+    def parseMulText(self,params, char="[]",key = 'text'):
+        key1 = key + char[0] + char[0]
+        keyIndex = params.find(key1)
+        if keyIndex == -1:
+            text = self.parseParam(key, params)  # 只有一层中括号
+        else:
+            text = []
+            str1 = params[keyIndex + key1.__len__() - 1:len(params)]
+            i1 = str1.find(char[0])
+            i2 = str1.find((char[1] + char[1]))
+            if i1 == -1 or i2 == -1:
+                return ""
+            str2 = str1[i1:i2 + 1]  # 只去掉外括号
+            strList = strToList(str2, char[1] + "," + char[0])
+            for strs in strList:
+                text.append(strs.strip(char[0]).strip(char[1]))
+        return text
+		
+    View_sambounds = "sambounds" #采样时的样本坐标,用于和select-focus、focusView的坐标做相对比较,计算相对位置,用于确定是否被选中。
+    # 即选中状态的坐标,option中组件选中状态下的坐标和focus-select/focusView中组件选中状态下的坐标
+    # 分别写在optionView和focus-select/focusView中,其实就类似用sambounds做为组件
+    '''
+    用于optionView的数据解析
+    '''
+    def parseOptionView(self, viewStr):
+        keyList = [self.View_ID, self.View_Text, self.View_Desc, self.View_Bounds, self.View_Index, self.View_Class
+                   ,self.View_sambounds]
+        paramDict = self.parseMulParam(keyList, viewStr)
+        paramDict[self.View_Bounds] = self.strToBounds(paramDict[self.View_Bounds])
+        paramDict[self.View_sambounds] = self.strToBounds(paramDict[self.View_sambounds])
+        return paramDict
+
+    # UI 界面数据
+    UIView_Activity = "activity"  # android activity
+    UIVIew_Action = "action"
+    # UIView_ID = "resid"  # android view resource-id
+    # UIView_Text = "text"  # android view 文本内容
+    # UIView_Desc = "desc"  # android view描述
+    # UIView_Bounds = "bounds"  # android view 坐标
+    UIView_Dialog_F = "dialog_forward"  # 进入时可能出现的弹窗
+    UIView_Dialog_B = "dialog_back"  # 退出时可能出现的弹窗
+    def parseUIView(self, viewStr):
+        keyList = [self.UIView_Activity, self.UIVIew_Action, self.View_ID, self.View_Text, self.View_Desc,
+                   self.View_Bounds, self.UIView_Dialog_F, self.UIView_Dialog_B]
+        uiView = self.parseMulParam(keyList, viewStr)
+        return self.parseDialogParam(uiView)
+
+    def parseDialogParam(self, uiView):
+        dialog_F_List = strToList(uiView[self.UIView_Dialog_F], ",")
+        dialog_B_List = strToList(uiView[self.UIView_Dialog_B], ",")
+        self.addDialogList(dialog_F_List, uiView, True)
+        self.addDialogList(dialog_B_List, uiView, False)
+        return uiView
+
+    def addDialogList(self, dialog_List, uiView, isForward):
+        for dialog in dialog_List:
+            dialog_A = strToList(dialog.lower(), "&")
+            if dialog_A.__len__() == 2:
+                dialog_Name = dialog_A[0]
+                dialog_Choose = dialog_A[1]
+            else:
+                error(str(self.__class__), "parseDialogParam", "Dialog %s set error"%dialog, ERROR)
+                return
+            if self.dialogDict.has_key(dialog_Name):
+                if isForward is True:
+                    dialog_Type = self.UIView_Dialog_F
+                else:
+                    dialog_Type = self.UIView_Dialog_B
+                uiView[dialog_Type] = {}
+                uiView[dialog_Type][dialog_Name] = self.dialogDict[dialog_Name]
+                uiView[dialog_Type][dialog_Name]["choose"] = self.dialogDict[dialog_Name][UATTree.TAB_OPTION][dialog_Choose]
+            else:
+                error(str(self.__class__), "parseDialogParam", "Dialog %s read fail"%dialog_Name, ERROR)
+
+
+    # Android Layout数据
+    # Layout_ID = "resid"  # android view resource-id
+    # Layout_Desc = "desc"  # android view描述
+    # Layout_Bounds = "bounds"  # android view 坐标
+    # Layout_Class = "class" #android view class类型
+    Layout_Limit = "limit" #layout是否限制optionView焦点操作范围。0:不限制,1:限制,默认为0
+    '''
+    layout:各属性值,用于综合确定一个布局组件
+    :return layout 字典:{"resid":"","desc":"","bounds":""}
+    '''
+    def parseLayout(self, viewStr):
+        keyList = [self.View_ID, self.View_Desc, self.View_Bounds, self.View_Class, self.Layout_Limit]
+        paramDict = self.parseMulParam(keyList, viewStr)
+        if paramDict.has_key(self.Layout_Limit) and paramDict[self.Layout_Limit].__len__()>0:
+            paramDict[self.Layout_Limit] = int(paramDict[self.Layout_Limit]) #字符'0'转数字0
+        paramDict[self.View_Bounds] = self.strToBounds(paramDict[self.View_Bounds])
+        return paramDict
+
+    #Android FocusView数据
+    # Focus_ID = "resid" #android view resource-id
+    # Focus_Text = "text" #android view 文本内容
+    # Focus_Desc = "desc" #android view描述
+    # Focus_Bounds = "bounds" #android view 坐标
+    # Focus_Class = "class"
+    def parseFocus(self, viewStr):
+        keyList = [self.View_ID, self.View_Text, self.View_Desc, self.View_Bounds, self.View_Class,
+                   self.View_sambounds]
+        paramDict = self.parseMulParam(keyList, viewStr)
+        paramDict[self.View_Bounds] = self.strToBounds(paramDict[self.View_Bounds])
+        paramDict[self.View_sambounds] = self.strToBounds(paramDict[self.View_sambounds])
+        return paramDict
+
+    #Value数据
+    # ValueView_ID = "resid" #android view resource-id
+    # ValueView_Text = "text" #android view 文本内容
+    # ValueView_Desc = "desc" #android view描述
+    # ValueView_Bounds = "bounds"
+    # ValueView_Class = "class"
+    ValueView_Value = "value"  # 输入的值范围。格式:first, second, ...  或者 0-100
+    ValueView_Min = "minvalue" # 如果是数值范围形式的值,返回最小值
+    ValueView_Max = "maxvalue" # 如果是数值范围形式的值,返回最小值
+    ValueView_StepSize = "stepsize" # 如果是数值范围形式的值,每次调整数值的步长。读不到此参数时,默认为1。
+    ValueView_Duration = "duration" # 如果是数值范围形式的值,每次调整数值的间隔时间。读不到此参数时,默认为0.2。
+    ValueView_Sign = "sign" # 针对前缀为字母,后缀为数字形式的值,用来保存前缀
+    def parseValueView(self, viewStr):
+        keyList = [self.View_ID, self.View_Text, self.View_Desc, self.ValueView_Value,
+                   self.ValueView_StepSize, self.ValueView_Duration, self.View_Bounds, self.View_Class,
+                   self.View_sambounds]
+        paramDict = self.parseMulParam(keyList, viewStr)
+        valueStr = paramDict[self.ValueView_Value]
+        sign = []
+        valueListDelStr = []
+        flagList = []
+        flag_v = False # value前缀不为字母
+        if valueStr != "":
+            i = valueStr.find(",")
+            if i != -1:
+                valueList = strToList(valueStr, ",")
+                isRange = False
+
+                # 前缀字母,后缀数字
+                for val in valueList:
+                    if (ord(val[0])>57 or ord(val[0])<48) and (47 < ord(val[1]) < 58):
+                        sign.append(val[0])
+                        valueDelStr = val.strip(val[0])
+                        valueListDelStr.append(valueDelStr)
+                        flagList.append(True)  #value前缀为字母
+                    else:
+                        flagList.append(False)
+                for f in flagList:
+                    flag_v = f
+                    flag_v = flag_v and f
+                if flag_v == False:
+                    sign = []
+                # 处理range类型(数值)
+                try:
+                    if valueList.__len__() == 2:
+                        if flag_v == True: #//
+                            valueList = valueListDelStr
+                            paramDict[self.ValueView_Min] = -float(valueList[0])#//
+                        else:
+                            paramDict[self.ValueView_Min] = float(valueList[0])
+                        paramDict[self.ValueView_Max] = float(valueList[1])
+                        isRange = True
+                except Exception,ValueError:
+                    info(self.cls, "parseValueView", "valueList %s not range value.", INFO)
+                # 处理非range类型(选项)
+                if isRange is False:
+                    for value in valueList:
+                        valueViewStr = self.parseParam(value, viewStr, "{}")
+                        if valueViewStr == "":
+                            debug(self.cls, "parseValueView", "value %s 没有任何配置信息!!!请加入该value信息,否则可能将导致设值失败!!!"%value, DEBUG)
+                        # 读出来的数值未带大括号,需加上大括号,并且json格式化
+                        valueView = eval("{%s}" % valueViewStr)
+                        paramDict[value] = valueView
+        paramDict[self.ValueView_Sign] = sign
+        return paramDict
+
+    #focus-select数据,不仅有select或者focus类型,还有指定组件属性情况,用于处理界面多个聚焦方式出现的场景
+    FS_Type = "type"
+    FS_checkType = "checkType"
+    # FS_ID = "resid" #android view resource-id
+    # FS_Text = "text" #android view 文本内容。
+    # FS_Desc = "desc" #android view描述
+    # FS_Bounds = "bounds" #android view 坐标
+    # FS_Class = "class"
+    def parseFocusSelectType(self, fsType):
+        keyList = [self.FS_Type,self.View_ID,self.View_Text,self.View_Desc,self.View_Bounds,
+                   self.View_sambounds,self.View_Class,self.FS_checkType]
+        paramDict = self.parseMulParam(keyList, fsType)
+        paramDict[self.View_sambounds] = self.strToBounds(paramDict[self.View_sambounds])
+        return paramDict
+
+    '''
+    扫描parent数据行时,要把建立当前parent的prevParent和nextParent关系。
+    '''
+    def addParent(self,level, parentName, shortkey, uiview, move_key, toparent_key, layout, others):
+        # print "addParent, level,parentName:", level, parentName
+        if not self.treeDict.has_key(level):
+            self.treeDict[level] = {}
+        parentDict = {}
+        parentDict[UATTree.TAB_NAME] = parentName
+        parentDict[UATTree.TAB_SHORTCUT_KEY] = self.parseKey(shortkey)
+        parentDict[UATTree.TAB_UI_VIEW] = self.parseUIView(uiview)
+        parentDict[UATTree.TAB_MOVE_KEY] = self.parseKey(move_key)
+        parentDict[UATTree.TAB_TOPARENT_KEY] = self.parseKey(toparent_key)
+        parentDict[UATTree.TAB_LAYOUT] = self.parseLayout(layout)
+        parentDict[UATTree.TAB_OPTION] = {}
+        parentDict[UATTree.TAB_LEVEL] = level
+        parentDict[UATTree.TAB_PREV_PARENT] = None
+        parentDict[UATTree.TAB_NEXT_PARENT] = {}
+        parentDict[UATTree.TAB_OTHERS] = self.parseOthers(others)
+        if "first" <> level:
+            prevParent = self.getPreParentByParent(parentDict)
+            if prevParent is not None:
+                parentDict[UATTree.TAB_PREV_PARENT] = prevParent
+                prevParent[UATTree.TAB_NEXT_PARENT][parentName] = parentDict
+        self.treeDict[level][parentName] = parentDict
+
+    '''
+    扫描option行的时候,会把option字典关联到相应的parent字典里
+    '''
+    def addOption(self, level, parentName, optionName, optionView, focusSelect, focusView, enterKey, textValue, infoView):
+        # print "addOption, level,parentName,optionName:", level, parentName,optionName
+        if self.treeDict.has_key(level) and self.treeDict[level].has_key(parentName):
+            optionDict = {}
+            optionDict[UATTree.TAB_NAME] = optionName
+            optionDict[UATTree.TAB_OPTION_VIEW] = self.parseOptionView(optionView)
+            optionDict[UATTree.TAB_FOCUS_SELECT] = self.parseFocusSelectType(focusSelect)
+            optionDict[UATTree.TAB_FOCUSE_VIEW] = self.parseFocus(focusView)
+            optionDict[UATTree.TAB_ENTER_KEY] = self.parseEnterKey(enterKey, optionName)
+            optionDict[UATTree.TAB_TEXT_VALUE] = self.parseValueView(textValue)
+            optionDict[UATTree.TAB_INFO_VIEW] = self.parseView(infoView)
+            optionDict[UATTree.TAB_PARENT_NAME] = parentName
+            optionDict[UATTree.TAB_LEVEL] = level
+            self.treeDict[level][parentName][UATTree.TAB_OPTION][optionName] = optionDict
+
+    def addDialog(self, parentName, shortkey, uiview, move_key, toparent_key, layout, others):
+        # print "addDialog, parentName:", parentName
+        parentDict = {}
+        parentDict[UATTree.TAB_NAME] = parentName
+        parentDict[UATTree.TAB_SHORTCUT_KEY] = self.parseKey(shortkey)
+        parentDict[UATTree.TAB_UI_VIEW] = self.parseUIView(uiview)
+        parentDict[UATTree.TAB_MOVE_KEY] = self.parseKey(move_key)
+        parentDict[UATTree.TAB_TOPARENT_KEY] = self.parseKey(toparent_key)
+        parentDict[UATTree.TAB_LAYOUT] = self.parseLayout(layout)
+        parentDict[UATTree.TAB_OPTION] = {}
+        parentDict[UATTree.TAB_OTHERS] = self.parseDialogOthers(others)
+
+        self.dialogDict[parentName] = parentDict
+
+    def addDialogOption(self, parentName, optionName, optionView, focusSelect, focusView, enterKey, textValue, infoView):
+        # print "addDialogOption, parentName,optionName:", parentName, optionName
+        if self.dialogDict.has_key(parentName):
+            optionDict = {}
+            optionDict[UATTree.TAB_NAME] = optionName
+            optionDict[UATTree.TAB_OPTION_VIEW] = self.parseOptionView(optionView)
+            optionDict[UATTree.TAB_FOCUS_SELECT] = self.parseFocusSelectType(focusSelect)
+            optionDict[UATTree.TAB_FOCUSE_VIEW] = self.parseFocus(focusView)
+            optionDict[UATTree.TAB_ENTER_KEY] = self.parseEnterKey(enterKey,optionName)
+            optionDict[UATTree.TAB_TEXT_VALUE] = self.parseValueView(textValue)
+            optionDict[UATTree.TAB_INFO_VIEW] = self.parseView(infoView)
+            optionDict[UATTree.TAB_PARENT_NAME] = parentName
+            self.dialogDict[parentName][UATTree.TAB_OPTION][optionName] = optionDict
+
+
+    def findParentDict(self, parentName):
+        parentName = parentName.lower()
+        levelList = self.treeDict.keys()
+        for index in range(levelList.__len__()-1, -1, -1):
+            level = levelList[index]
+            if self.treeDict[level].has_key(parentName):
+                return self.treeDict[level][parentName]
+        return None
+
+    def getSubOptionDict(self, parentName):
+        parentDict = self.findParentDict(parentName)
+        if parentDict is not None:
+            return parentDict[UATTree.TAB_OPTION]
+        return None
+
+    def findOption(self, optionName):
+        optionName = optionName.lower()
+        levelList = self.treeDict.keys()
+        for index in range(levelList.__len__()-1, -1, -1):
+            level = levelList[index]
+            levelDict = self.treeDict[level]
+            for parent in levelDict.keys():
+                parentDict = levelDict[parent]
+                for option in parentDict.keys():
+                    optionDict = parentDict[UATTree.TAB_OPTION]
+                    if optionDict.has_key(optionName):
+                        return optionDict[optionName]
+
+    def getOpitonInLevel(self,targetOptionName, level):
+        levelDict = self.treeDict[level]
+        for parentName in levelDict.keys():
+            parentDict = levelDict[parentName]
+            for optionName in parentDict[UATTree.TAB_OPTION].keys():
+                if targetOptionName == optionName:
+                    return parentDict[UATTree.TAB_OPTION][optionName]
+        else:
+            return None
+
+    '''
+   检测option的enter_key中是否存在对应parentName
+   '''
+    def checkEnterParentEK(self, enterKey, parentName):
+        # print "checkEnterParentEK,enterKey,parentName:",enterKey, parentName
+        for keyItem in enterKey:
+            if keyItem[self.Enter_Parent] == parentName:
+                return True
+        return False
+    '''
+    检测option字典,是否下一级nextParent包含这一个parentName
+    '''
+    def checkOptionEnterParent(self, option, parentName):
+        enterKey = option[self.TAB_ENTER_KEY]
+        if option[self.TAB_NAME] == parentName:
+            return True
+        return self.checkEnterParentEK(enterKey, parentName)
+
+    '''
+    根据parentName,在EnterKey所有列表中,找到匹配的enterKey字典选项
+    返回值:{"enterParent":"","key_param":{"keyType":"event","keyvalue":[left,right,enter],"wait":1,"enterParent":"hdmi_settings"}
+    '''
+    def getEKByParent(self, enterKeys, parentName):
+        for keyItem in enterKeys:
+            if keyItem[self.Enter_Parent] == parentName:
+                return keyItem[self.Key_Param]
+        return None
+    '''
+    获取option的parent
+    '''
+    def getParentByOption(self, option):
+        parentName = option[UATTree.TAB_PARENT_NAME]
+        levelName = option[UATTree.TAB_LEVEL]
+        if self.treeDict[levelName].has_key(parentName):
+            parent = self.treeDict[levelName][parentName]
+            return parent
+        else:
+            return None
+
+    '''
+    根据Parent数据,查上一级parent。
+    '''
+    def getPreParentByParent(self, parent):
+        prevLevel = self.getPrevLevel(parent[self.TAB_LEVEL])
+        levelDict = self.treeDict[prevLevel]
+        # print "getPreParentByParent,levelDict:",levelDict
+        for parentName in levelDict.keys():
+            parentDict = levelDict[parentName]
+            # print "getPreParentByParent,parentDict:", parentDict
+            for optionName in parentDict[UATTree.TAB_OPTION].keys():
+                option = parentDict[UATTree.TAB_OPTION][optionName]
+                # print "getPreParentByParent,option:", option
+                if self.checkOptionEnterParent(option, parent[self.TAB_NAME]):
+                    return parentDict
+        error(str(self.__class__), "getPreParentByParent",
+              "Parent %s's prevparent not found in level %s"%(parent[self.TAB_NAME], prevLevel), ERROR)
+        return None
+
+    '''
+    获取当前level的前一个level名
+    '''
+    def getPrevLevel(self, level):
+        self.levelList = self.treeDict.keys()
+        index = self.levelList.index(level)
+        if index == 0:
+            return FirstPrev
+        else:
+            return self.levelList[index-1]
+    '''
+    获取当前Parent的上一级parent
+    '''
+    def getPrevParent(self, parent):
+        return parent[UATTree.TAB_PREV_PARENT]
+
+    '''
+    获取当前parent下一级所有parent的字典
+    '''
+    def getNextParentList(self, parent):
+        return parent[UATTree.TAB_NEXT_PARENT]
+
+    def printTree(self):
+        for level in self.treeDict.keys():
+            print level
+            for parent in self.treeDict[level].keys():
+                dict1 = self.treeDict[level][parent]
+                print level, "::parent ", dict1["name"], "name", "shortcut_key", "uiview", "move_key", "toparent_key", "layout"
+                print level, "::parent ", dict1["name"], dict1["shortcut_key"], dict1["uiview"], dict1["move_key"], dict1["toparent_key"], dict1["layout"]
+                for option in dict1["option"].keys():
+                    dict2 = dict1["option"][option]
+                    print level, "::option ", dict2
+
+    Key_HeartBeat = "heartbeat_key"
+    def parseOthers(self, othersStr):
+        othersDict = {}
+        othersDict[self.Key_HeartBeat] = self.parseParam(self.Key_HeartBeat, othersStr, char="{}")
+        hb_keyDict = {}
+        if othersDict[self.Key_HeartBeat] != "":
+            print "Key_HeartBeat_Str:",othersDict[self.Key_HeartBeat]
+            hb_keyDict = self.parseHeartBeatKey(othersDict[self.Key_HeartBeat])
+        othersDict[self.Key_HeartBeat] = hb_keyDict
+        return othersDict
+		
+    def parseHeartBeatKey(self, hb_keyStr):
+        keyList = [self.Key_Event, self.Key_IR]
+        keyDict = self.parseMulParam(keyList, hb_keyStr)
+        keyDict[self.Key_Event] = strToList(keyDict[self.Key_Event], ",")
+        keyDict[self.Key_IR] = strToList(keyDict[self.Key_IR], ",")
+        return keyDict
+		
+    def parseDialogOthers(self, othersStr):
+        othersDict = {}
+        othersDict[self.Key_HeartBeat] = self.parseParam(self.Key_HeartBeat, othersStr, char="{}")
+        hb_keyDict = {}
+        if othersDict[self.Key_HeartBeat] != "":
+            print "Key_HeartBeat_Str:",othersDict[self.Key_HeartBeat]
+            hb_keyDict = self.parseHeartBeatKey(othersDict[self.Key_HeartBeat])
+        othersDict[self.Key_HeartBeat] = hb_keyDict
+        return othersDict
+
+if __name__ == '__main__':
     uatTree = UATTree()

+ 180 - 179
ssat_sdk/UATree/UIParamUtil.py

@@ -1,180 +1,181 @@
-# -*- coding:utf-8 -*-
-from UAT_log import error,info,debug
-
-ERROR = True
-INFO = True
-DEBUG = True
-
-'''
-UIObject Info:
-{
-	u 'contentDescription': None,
-	u 'checked': False,
-	u 'clickable': True,
-	u 'scrollable': False,
-	u 'text': None,
-	u 'packageName': u 'com.android.tv',
-	u 'selected': False,
-	u 'enabled': True,
-	u 'bounds': {
-		u 'top': 491,
-		u 'left': 75,
-		u 'right': 264,
-		u 'bottom': 619
-	},
-	u 'className': u 'android.widget.RelativeLayout',
-	u 'focusable': True,
-	u 'focused': True,
-	u 'checkable': False,
-	u 'resourceName': None,
-	u 'longClickable': False,
-	u 'visibleBounds': {
-		u 'top': 491,
-		u 'left': 75,
-		u 'right': 264,
-		u 'bottom': 619
-	},
-	u 'childCount': 3
-}
-UATree中的UI param:
-
-
-'''
-'''
-UI界面组件参数的工具类
-'''
-class UIParamUtil:
-    UIObjParam = {
-        "text": None,  # MASK_TEXT,
-        "textContains": None,  # MASK_TEXTCONTAINS,
-        "textMatches": None,  # MASK_TEXTMATCHES,
-        "textStartsWith": None,  # MASK_TEXTSTARTSWITH,
-        "className": None,  # MASK_CLASSNAME
-        "classNameMatches": None,  # MASK_CLASSNAMEMATCHES
-        "description": None,  # MASK_DESCRIPTION
-        "descriptionContains": None,  # MASK_DESCRIPTIONCONTAINS
-        "descriptionMatches": None,  # MASK_DESCRIPTIONMATCHES
-        "descriptionStartsWith": None,  # MASK_DESCRIPTIONSTARTSWITH
-        "checkable": None,  # MASK_CHECKABLE
-        "checked": None,  # MASK_CHECKED
-        "clickable": None,  # MASK_CLICKABLE
-        "longClickable": None,  # MASK_LONGCLICKABLE,
-        "scrollable": None,  # MASK_SCROLLABLE,
-        "enabled": None,  # MASK_ENABLED,
-        "focusable": None,  # MASK_FOCUSABLE,
-        "focused": None,  # MASK_FOCUSED,
-        "selected": None,  # MASK_SELECTED,
-        "packageName": None,  # MASK_PACKAGENAME,
-        "packageNameMatches": None,  # MASK_PACKAGENAMEMATCHES,
-        "resourceId": None,  # MASK_RESOURCEID,
-        "resourceIdMatches": None,  # MASK_RESOURCEIDMATCHES,
-        "index": None,  # MASK_INDEX,
-        "instance": None  # MASK_INSTANCE,
-    }
-    UATParamMap = {
-        "text": "text",  # MASK_TEXT,
-        "textContains": "textContains",  # MASK_TEXTCONTAINS,
-        "textMatch": "textMatches",  # MASK_TEXTMATCHES,
-        "textSWith": "textStartsWith",  # MASK_TEXTSTARTSWITH,
-        "class": "className",  # MASK_CLASSNAME
-        "classMatches": "classNameMatches",  # MASK_CLASSNAMEMATCHES
-        "desc": "description",  # MASK_DESCRIPTION
-        "descContain": "descriptionContains",  # MASK_DESCRIPTIONCONTAINS
-        "descMatch": "descriptionMatches",  # MASK_DESCRIPTIONMATCHES
-        "descSWith": "descriptionStartsWith",  # MASK_DESCRIPTIONSTARTSWITH
-        "checkable": "checkable",  # MASK_CHECKABLE
-        "checked": "checked",  # MASK_CHECKED
-        "clickable": "clickable",  # MASK_CLICKABLE
-        "lClickable": "longClickable",  # MASK_LONGCLICKABLE,
-        "scrollable": "scrollable",  # MASK_SCROLLABLE,
-        "enable": "enabled",  # MASK_ENABLED,
-        "focusable": "focusable",  # MASK_FOCUSABLE,
-        "focus": "focused",  # MASK_FOCUSED,
-        "select": "selected",  # MASK_SELECTED,
-        "pkg": "packageName",  # MASK_PACKAGENAME,
-        "pkgMatch": "packageNameMatches",  # MASK_PACKAGENAMEMATCHES,
-        "resid": "resourceId",  # MASK_RESOURCEID,
-        "residMatch": "resourceIdMatches",  # MASK_RESOURCEIDMATCHES,
-        "index": "index",  # MASK_INDEX,
-        "instance": "instance"  # MASK_INSTANCE,
-    }
-    cls = "UIObjParam"
-    def __init__(self):
-        pass
-
-    '''
-    将atx抓取到的bounds字典:'visibleBounds': {
-		u 'top': 491,
-		u 'left': 75,
-		u 'right': 264,
-		u 'bottom': 619
-	}
-	转换成UATree格式的:
-	[[75,491][264,619]]
-    '''
-    @staticmethod
-    def atxBounds2UATBounds(atxBounds):
-        try:
-            bounds = [[atxBounds['left'], atxBounds['top']], [atxBounds['right'], atxBounds['bottom']]]
-        except Exception,e:
-            error(self.cls, "atxBounds2UATBounds", "atxBounds has error format:"+str(atxBounds),ERROR)
-            bounds=[]
-        return bounds
-
-    ''''
-    UATree格式的:[[75,491][264,619]]    
-    转换成:
-    atx抓取到的bounds字典:'visibleBounds': {
-        u 'top': 491,
-        u 'left': 75,
-        u 'right': 264,
-        u 'bottom': 619
-    }
-    '''
-    @staticmethod
-    def uatBounds2ATXBounds(uatBounds):
-        bounds = {}
-        try:
-            bounds['left'] = uatBounds[0][0]
-            bounds["top"] = uatBounds[0][1]
-            bounds['right'] = uatBounds[1][0]
-            bounds['bottom'] = uatBounds[1][1]
-        except Exception,e:
-            error(self.cls, "uatBounds2ATXBounds", "uatBounds has error format:"+str(uatBounds),ERROR)
-            bounds={}
-        return bounds
-
-    '''
-    根据传入的uat界面界面参数(例如:optionView、UIView),转换成UIObject参数
-    参数均用字典存储。
-    '''
-    @staticmethod
-    def setObjParam(uatObjParam):
-        uiObjParam = {}
-        if uatObjParam is None:
-            return uiObjParam
-        for uatKey in uatObjParam:
-            uatParam = uatObjParam[uatKey]
-            if UIParamUtil.UATParamMap.has_key(uatKey) and uatParam is not None and str(uatParam).__len__() > 0:
-                uiObjParam[UIParamUtil.UATParamMap[uatKey]] = uatParam
-        return uiObjParam
-
-    '''
-    比对两个UIObject参数,是否一样,采用字典逐个比对方式。
-    objParam1, objParam2必须是同一中字典格式
-    return :True/False. True表示相同,False标识不同
-    '''
-    @staticmethod
-    def cmpObjParam(objParam1, objParam2):
-        for key in objParam1:
-            if "sambounds" == key: #sambounds只用于相对位置计算,不做重复考虑
-                break
-            try:
-                value1 = objParam1[key]
-                value2 = objParam2[key]
-                if value1 <> value2:
-                    return False
-            except Exception,e:
-                error(self.cls, "cmpObjParam", "objParam1 and objParam2 are different", ERROR)
-                return False
+# -*- coding:utf-8 -*-
+from UAT_log import error,info,debug
+
+ERROR = True
+INFO = True
+DEBUG = True
+
+'''
+UIObject Info:
+{
+	u 'contentDescription': None,
+	u 'checked': False,
+	u 'clickable': True,
+	u 'scrollable': False,
+	u 'text': None,
+	u 'packageName': u 'com.android.tv',
+	u 'selected': False,
+	u 'enabled': True,
+	u 'bounds': {
+		u 'top': 491,
+		u 'left': 75,
+		u 'right': 264,
+		u 'bottom': 619
+	},
+	u 'className': u 'android.widget.RelativeLayout',
+	u 'focusable': True,
+	u 'focused': True,
+	u 'checkable': False,
+	u 'resourceName': None,
+	u 'longClickable': False,
+	u 'visibleBounds': {
+		u 'top': 491,
+		u 'left': 75,
+		u 'right': 264,
+		u 'bottom': 619
+	},
+	u 'childCount': 3
+}
+UATree中的UI param:
+
+
+'''
+'''
+UI界面组件参数的工具类
+'''
+class UIParamUtil:
+    UIObjParam = {
+        "text": None,  # MASK_TEXT,
+        "textContains": None,  # MASK_TEXTCONTAINS,
+        "textMatches": None,  # MASK_TEXTMATCHES,
+        "textStartsWith": None,  # MASK_TEXTSTARTSWITH,
+        "className": None,  # MASK_CLASSNAME
+        "classNameMatches": None,  # MASK_CLASSNAMEMATCHES
+        "description": None,  # MASK_DESCRIPTION
+        "descriptionContains": None,  # MASK_DESCRIPTIONCONTAINS
+        "descriptionMatches": None,  # MASK_DESCRIPTIONMATCHES
+        "descriptionStartsWith": None,  # MASK_DESCRIPTIONSTARTSWITH
+        "checkable": None,  # MASK_CHECKABLE
+        "checked": None,  # MASK_CHECKED
+        "clickable": None,  # MASK_CLICKABLE
+        "longClickable": None,  # MASK_LONGCLICKABLE,
+        "scrollable": None,  # MASK_SCROLLABLE,
+        "enabled": None,  # MASK_ENABLED,
+        "focusable": None,  # MASK_FOCUSABLE,
+        "focused": None,  # MASK_FOCUSED,
+        "selected": None,  # MASK_SELECTED,
+        "packageName": None,  # MASK_PACKAGENAME,
+        "packageNameMatches": None,  # MASK_PACKAGENAMEMATCHES,
+        "resourceId": None,  # MASK_RESOURCEID,
+        "resourceIdMatches": None,  # MASK_RESOURCEIDMATCHES,
+        "index": None,  # MASK_INDEX,
+        "instance": None  # MASK_INSTANCE,
+    }
+    UATParamMap = {
+        "text": "text",  # MASK_TEXT,
+        "textContains": "textContains",  # MASK_TEXTCONTAINS,
+        "textMatch": "textMatches",  # MASK_TEXTMATCHES,
+        "textSWith": "textStartsWith",  # MASK_TEXTSTARTSWITH,
+        "class": "className",  # MASK_CLASSNAME
+        "classMatches": "classNameMatches",  # MASK_CLASSNAMEMATCHES
+        "desc": "description",  # MASK_DESCRIPTION
+        "descContain": "descriptionContains",  # MASK_DESCRIPTIONCONTAINS
+        "descMatch": "descriptionMatches",  # MASK_DESCRIPTIONMATCHES
+        "descSWith": "descriptionStartsWith",  # MASK_DESCRIPTIONSTARTSWITH
+        "checkable": "checkable",  # MASK_CHECKABLE
+        "checked": "checked",  # MASK_CHECKED
+        "clickable": "clickable",  # MASK_CLICKABLE
+        "lClickable": "longClickable",  # MASK_LONGCLICKABLE,
+        "scrollable": "scrollable",  # MASK_SCROLLABLE,
+        "enable": "enabled",  # MASK_ENABLED,
+        "focusable": "focusable",  # MASK_FOCUSABLE,
+        "focus": "focused",  # MASK_FOCUSED,
+        "select": "selected",  # MASK_SELECTED,
+        "pkg": "packageName",  # MASK_PACKAGENAME,
+        "pkgMatch": "packageNameMatches",  # MASK_PACKAGENAMEMATCHES,
+        "resid": "resourceId",  # MASK_RESOURCEID,
+        "residMatch": "resourceIdMatches",  # MASK_RESOURCEIDMATCHES,
+        "index": "index",  # MASK_INDEX,
+        "instance": "instance"  # MASK_INSTANCE,
+    }
+    global cls
+    cls = "UIObjParam"
+    def __init__(self):
+        pass
+
+    '''
+    将atx抓取到的bounds字典:'visibleBounds': {
+		u 'top': 491,
+		u 'left': 75,
+		u 'right': 264,
+		u 'bottom': 619
+	}
+	转换成UATree格式的:
+	[[75,491][264,619]]
+    '''
+    @staticmethod
+    def atxBounds2UATBounds(atxBounds):
+        try:
+            bounds = [[atxBounds['left'], atxBounds['top']], [atxBounds['right'], atxBounds['bottom']]]
+        except Exception,e:
+            error(self.cls, "atxBounds2UATBounds", "atxBounds has error format:"+str(atxBounds),ERROR)
+            bounds=[]
+        return bounds
+
+    ''''
+    UATree格式的:[[75,491][264,619]]    
+    转换成:
+    atx抓取到的bounds字典:'visibleBounds': {
+        u 'top': 491,
+        u 'left': 75,
+        u 'right': 264,
+        u 'bottom': 619
+    }
+    '''
+    @staticmethod
+    def uatBounds2ATXBounds(uatBounds):
+        bounds = {}
+        try:
+            bounds['left'] = uatBounds[0][0]
+            bounds["top"] = uatBounds[0][1]
+            bounds['right'] = uatBounds[1][0]
+            bounds['bottom'] = uatBounds[1][1]
+        except Exception,e:
+            error(cls, "uatBounds2ATXBounds", "uatBounds has error format:"+str(uatBounds),ERROR)
+            bounds={}
+        return bounds
+
+    '''
+    根据传入的uat界面界面参数(例如:optionView、UIView),转换成UIObject参数
+    参数均用字典存储。
+    '''
+    @staticmethod
+    def setObjParam(uatObjParam):
+        uiObjParam = {}
+        if uatObjParam is None:
+            return uiObjParam
+        for uatKey in uatObjParam:
+            uatParam = uatObjParam[uatKey]
+            if UIParamUtil.UATParamMap.has_key(uatKey) and uatParam is not None and str(uatParam).__len__() > 0:
+                uiObjParam[UIParamUtil.UATParamMap[uatKey]] = uatParam
+        return uiObjParam
+
+    '''
+    比对两个UIObject参数,是否一样,采用字典逐个比对方式。
+    objParam1, objParam2必须是同一中字典格式
+    return :True/False. True表示相同,False标识不同
+    '''
+    @staticmethod
+    def cmpObjParam(objParam1, objParam2):
+        for key in objParam1:
+            if "sambounds" == key: #sambounds只用于相对位置计算,不做重复考虑
+                break
+            try:
+                value1 = objParam1[key]
+                value2 = objParam2[key]
+                if value1 <> value2:
+                    return False
+            except Exception,e:
+                error(cls, "cmpObjParam", "objParam1 and objParam2 are different", ERROR)
+                return False
         return True

+ 196 - 0
ssat_sdk/basessh2.py

@@ -0,0 +1,196 @@
+# -*- coding:utf-8 -*-
+import os
+import sys
+import time
+import datetime
+import socket
+import hashlib  # md5验证;
+from ssh2.session import Session
+from ssh2.sftp import LIBSSH2_FXF_READ, LIBSSH2_SFTP_S_IRUSR
+
+
+class baseSSH2:
+    def __init__(self):
+        self.__user = ""
+        self.__pwd = ""
+        self.__host = ""
+        self.__port = 22
+
+    '''
+    函数:创建tcp sock连接;
+    参数:无
+    返回:成功创建tcp连接返回sock,否则返回None
+    注意:创建成功后的sock,在外部使用完后必须调用sock.close()释放;
+    '''
+
+    def __create_sock(self):
+        # 创建tcp连接;
+        try:
+            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            sock.connect((self.__host, int(self.__port)))  # 没有返回值;
+            return sock
+        except Exception, e:
+            print u'创建ssh2 socket失败', e
+            return None
+
+    '''
+    函数:初始化ssh2所需参数;
+    参数:user、pwd、host、port
+    返回:无
+    '''
+
+    def init_ssh2(self, user, pwd, host, port=22):
+        self.__host = host
+        self.__port = port
+        self.__user = user
+        self.__pwd = pwd
+
+    '''
+    函数:执行命令;
+    参数:cmd
+    返回:执行成功,返回True及执行结果;
+    '''
+
+    def execute_cmd(self, cmd):
+        print u"ssh2.execute_cmd=",cmd
+        sock = self.__create_sock()
+        if sock is None:
+            return False, ""
+
+        # 创建ssh2会话连接;
+        session = Session()
+        # 关联tcp socket;
+        if session.handshake(sock) is None:
+            sock.close()
+            print u'建立ssh2 会话失败'
+            return False, ""
+        # 以用户+密码方式建立认证连接;
+        if session.userauth_password(self.__user, self.__pwd) is None:
+            sock.close()
+            print u'登录ssh2 会话失败'
+            return False, ""
+
+        strdata = ''
+        # 创建会话通道;
+        channel = session.open_session()
+        channel.execute(cmd)
+        size, data = channel.read()
+        strdata = data
+        while size > 0:
+            size, data = channel.read()
+            strdata += data
+        channel.close()
+        # print("Exit status: %s" % channel.get_exit_status())
+        sock.close()
+
+        return True if channel.get_exit_status() == 0 else False, strdata
+
+    '''
+    函数:下载文件;
+    参数:ftp_path要下载的文件路径, local_path要保存的文件路径;
+    返回:执行成功,返回True;
+    '''
+
+    def sftp_download(self, ftp_path, local_path):
+        sock = self.__create_sock()
+        if sock is None:
+            return False
+
+        # 创建ssh2会话连接;
+        session = Session()
+        # 关联tcp socket;
+        if session.handshake(sock) is None:
+            sock.close()
+            print u'建立ssh2 会话失败'
+            return False
+        # 以用户+密码方式建立认证连接;
+        if session.userauth_password(self.__user, self.__pwd) is None:
+            sock.close()
+            print u'登录ssh2 会话失败'
+            return False
+
+        sftp = session.sftp_init()
+        # now = datetime.time()
+        # print("Starting read for remote file %s" % ftp_path)
+        try:
+            with sftp.open(ftp_path, LIBSSH2_FXF_READ, LIBSSH2_SFTP_S_IRUSR) as fh, open(local_path, 'wb+') as lh:
+                for size, data in fh:
+                    lh.write(data)
+                    # print 'download size=',size
+            lh.close()
+            # print("Finished file read in %s" % (datetime.time() - now))
+            sock.close()
+            return True
+        except Exception, e:
+            print u'下载失败:',e
+            return False
+
+    '''
+    函数:计算文件md5值;
+    参数:file要计算的文件路径;
+    返回:执行成功返回md5值,否则返回None;
+    '''
+
+    def get_md5_sum(self, sftp_file):
+        cmd = 'md5sum %s' % sftp_file
+        boolean, data = self.execute_cmd(cmd)
+        if boolean is False:
+            return None
+        # 分组;
+        str_list = data.split('  ')
+        if str_list.__len__() != 2:
+            return None
+
+        return str_list[0]
+
+    '''
+    函数:下载文件,并验证md5是否正确;
+    参数:ftp_path要下载的文件路径, local_path要保存的文件路径;
+    返回:执行成功,返回True;
+    '''
+    def sftp_download_md5(self, ftp_path, local_path):
+        # 先计算md5值;
+        sftp_md5 = self.get_md5_sum(ftp_path)
+        if sftp_md5 is None:
+            print 'sftp_md5值空'
+            return False
+        
+        print "SFTP MD5=", sftp_md5
+        # 下载文件后计算;
+        if self.sftp_download(ftp_path, local_path) is True:
+            local_md5 = ""
+            md5_local = hashlib.md5()
+            time.sleep(5)
+            file_data = []
+            if os.path.exists(local_path):
+                with open(local_path, mode='rb') as f:
+                    while True:
+                        data = f.read(8192)
+                        if not data:
+                            break
+                        md5_local.update(data)
+                local_md5 = md5_local.hexdigest()
+                print u"本地MD5=", local_md5
+
+            return True if sftp_md5 == local_md5 else False
+
+        print u'下载文件失败'
+        return False
+
+
+if __name__ == "__main__":
+    host = "10.201.251.254"
+    user = "wjf"
+    pwd = "wjf2019"
+    myssh2 = baseSSH2()
+    myssh2.init_ssh2(user, pwd, host)
+    # cmd = "md5sum /home/RT2841_2851_dailybuild/DailyBuild_RT2851_0509/signed-ota_rt2851_update.zip"
+    # bolean, data = myssh2.execute_cmd(cmd)
+    # print data.split('  ')[0]
+    # print u'MD5值=', myssh2.get_md5_sum(
+    #     "/home/RT2841_2851_dailybuild/DailyBuild_RT2851_0509/signed-ota_rt2851_update.zip")
+    # myssh2.sftp_download("rt2851/Buildimg/V8-T841T01-LF1V001/Images/USB/build.prop", "D:\\sat\\build.prop")
+    # myssh2.sftp_download("/home/RT2841_2851_dailybuild/DailyBuild_RT2851_0509/signed-ota_rt2851_update.zip", "D:\\sat\\a.img")
+    if myssh2.sftp_download_md5("/home/RT2841_2851_dailybuild/DailyBuild_RT2851_0509/signed-ota_rt2851_update.zip", "D:\\sat\\b.img"):
+        print u"下载文件成功"
+        

+ 185 - 218
ssat_sdk/device_manage/testWizardClient.py

@@ -1,219 +1,186 @@
-# -*- coding:utf-8 -*-
-import os, sys, time
-import abc
-import socket
-import json
-import numpy as np
-import struct
-import TestWizardBLK
-import binascii
-
-# 创建ProHeader数据类型
-ProHead = np.dtype({'names': ['version', 'len'], 'formats': ['u1', 'u4']})
-
-
-class TestWizardClient():
-    def __init__(self):
-        '''设备通讯超时值:毫秒'''
-        self.device_timeout = 300
-
-        '''通信sock'''
-        self.sock = None
-        '''通信端口号'''
-        self.port = 5566
-        '''通信状态'''
-        self.constate = False
-        '''连接服务器'''
-        self.connect()
-
-    def __def__(self):
-        self.disconnect()
-
-    '''连接服务器'''
-
-    def connect(self):
-        try:
-            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-            self.sock.connect(('127.0.0.1', self.port))  # 没有返回值;
-            self.sock.sendall('111')
-            self.constate = True
-        except Exception, e:
-            print "TestWizardClient=> socket connect error:", e, self.port
-            self.constate = False
-
-        return self.constate
-
-    # 发送消息;
-    def __sendkey(self, keyname):
-
-        '''是否连接'''
-        if self.constate is False:
-            if self.connect() is False:
-                return None
-        '''发送请求数据'''
-        try:
-            self.sock.settimeout(3)
-            keyname = "ir>"+keyname
-            phead = np.array([(0xAC, keyname.__len__() + np.array([(0xAA, 0)], dtype=ProHead).itemsize)], dtype=ProHead)
-            '''一次性发送完整包'''
-            self.sock.sendall(bytearray(phead.tobytes() + bytes(keyname)))
-            '''分包发送:头、体'''
-            # self.sock.sendall(phead.tobytes())
-            # self.sock.sendall(bytes(keyname))
-            self.sock.settimeout(None)
-            # return False
-        except Exception, e:
-            print "__sendkey=> send Error:", e
-            import traceback
-            traceback.print_exc()
-            self.disconnect()
-            self.connect()
-            return None
-
-        try:
-            self.sock.settimeout(2)
-            recvBytes = bytearray(self.sock.recv(1024))
-            data = self.parseRetData(recvBytes, phead.itemsize)
-            if int(data) == 1:
-                print "Info:__sendkey %s success!"%keyname
-                return True
-            print "Error:__sendkey %s fail!"%keyname
-        except Exception, e:
-            print "__sendkey=> recv Error:", e
-            return None
-
-    def parseRetData(self, recvByteArray, headSize):
-        version = recvByteArray[0]&0xff
-        recvLen = self.byte4ToInt(recvByteArray, 1)
-        if version == 0xAC and recvLen == recvByteArray.__len__():
-            byteArr = bytes(recvByteArray)
-            data = byteArr[headSize: recvByteArray.__len__()]
-            return data
-        return "0"
-
-    def byte4ToInt(self,bytearray, start):
-        i = (bytearray[start] & 0xff) \
-            | (bytearray[start+1] & 0xff) << 8 \
-            | (bytearray[start+2] & 0xff) << 16\
-            | (bytearray[start+3] & 0xff) << 24
-        return i
-
-    # 发送消息;
-    def sendKey(self, keyname, times=1, duration=1):
-        while times > 0:
-            times -= 1
-            self.__sendkey(keyname)
-            time.sleep(duration)
-    
-    # 发送多个按键;
-    def sendKeys(self, keyNames, duration):
-        for key in keyNames:
-            self.__sendkey(key)
-            time.sleep(duration)
-
-    '''
-    用于发送测试精灵指令,不区分子模块,也不获取发送结果,默认指令发送成功。
-    因为各子模块,有不返回结果,而返回结果的,结果格式也不一样。
-    '''
-    def __sendCmd(self, cmdStr):
-        '''是否连接'''
-        if self.constate is False:
-            if self.connect() is False:
-                return False
-
-        '''发送请求数据'''
-        try:
-            self.sock.settimeout(3)
-            cmdStr = "cmd>"+cmdStr
-            phead = np.array([(0xAC, cmdStr.__len__() + np.array([(0xAA, 0)], dtype=ProHead).itemsize)], dtype=ProHead)
-            '''一次性发送完整包'''
-            ecmd = bytearray(phead.tobytes() + bytes(cmdStr))
-            self.sock.sendall(ecmd)
-        except Exception, e:
-            print "__sendCmd=> send Error:", e
-            import traceback
-            traceback.print_exc()
-            self.disconnect()
-            self.connect()
-            return False
-
-        try:
-            self.sock.settimeout(10)
-            recvBytes = bytearray(self.sock.recv(1024))
-            data = self.parseRetData(recvBytes, phead.itemsize)
-            if int(data) == 1:
-                print "Info:__sendkey success!"
-                return True
-            print "Error:__sendkey fail!"
-        except Exception, e:
-            print "__sendCmd=> recv Error:", e
-            return False
-
-    def sendBLEKey(self, bleKeyName):
-        if TestWizardBLK.BLK_DIC.has_key(bleKeyName.lower()):
-            cmdStr = "*INPUT BLEKEY " + TestWizardBLK.BLK_DIC[bleKeyName.lower()]
-            ret = self.__sendCmd(cmdStr)
-            time.sleep(1)
-            return ret
-        else:
-            return False
-    '''
-    电源开关,所有电源同时控制,不区分LAN口
-    :param onOff: ON代表开,OFF代表关,区分大小写。
-    :return True代表成功,False达标失败
-    '''
-    def setAllPower(self, onOff):
-        if onOff <> "ON" and onOff <> "OFF":
-            return False
-        cmdStr = "*SET " + onOff
-        ret = self.__sendCmd(cmdStr)
-        time.sleep(1)
-        return ret
-
-    def loadSignalDataSet(self, file):
-        pass
-
-    def addSignalDataSet(self, file):
-        pass
-
-    def getDeviceName(self):
-        pass
-
-    def getScriptName(self):
-        pass
-
-    def getKeyName(self):
-        pass
-
-    def close(self):
-        self.disconnect()
-
-    def getCurrentDeviceName(self):
-        pass
-
-
-    '''断开连接'''
-
-    def disconnect(self):
-        try:
-            self.sock.shutdown(2)
-            self.sock.close()
-        except Exception, e:
-            print "TestWizardClient=> socket disconnect error:", e
-
-
-if __name__ == "__main__":
-    tw = TestWizardClient()
-    # tw.sendKey('POWER')
-    tw.sendKey('down')
-    # print 'sleep'
-    # time.sleep(3)
-    tw.sendKey('up')
-    # print 111111
-    # print tw.sendBLEKey("uparrow")
-    # time.sleep(3)
-    # print tw.sendBLEKey("downarrow")
-    # time.sleep(3)
-    # print 2222222
-    # print tw.setAllPower("OFF")
+# -*- coding:utf-8 -*-
+import os, sys, time
+import abc
+import socket
+import json
+import numpy as np
+import struct
+import TestWizardBLK
+import binascii
+
+# 创建ProHeader数据类型
+ProHead = np.dtype({'names': ['version', 'len'], 'formats': ['u1', 'u4']})
+
+
+class TestWizardClient():
+    def __init__(self):
+        '''设备通讯超时值:毫秒'''
+        self.device_timeout = 300
+
+        '''通信sock'''
+        self.sock = None
+        '''通信端口号'''
+        self.port = 5566
+        '''通信状态'''
+        self.constate = False
+        '''连接服务器'''
+        self.connect()
+
+    def __def__(self):
+        self.disconnect()
+
+    '''连接服务器'''
+
+    def connect(self):
+        try:
+            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            self.sock.connect(('127.0.0.1', self.port))  # 没有返回值;
+            self.sock.sendall('111')
+            self.constate = True
+        except Exception, e:
+            print "TestWizardClient=> socket connect error:", e, self.port
+            self.constate = False
+
+        return self.constate
+
+    # 发送消息;
+    def __sendCmd(self, cmdType, command):
+        '''是否连接'''
+        if self.constate is False:
+            if self.connect() is False:
+                return False
+        '''发送请求数据'''
+        try:
+            self.sock.settimeout(3)
+            command = cmdType + ">" + command
+            phead = np.array([(0xAC, command.__len__() + np.array([(0xAA, 0)], dtype=ProHead).itemsize)], dtype=ProHead)
+            '''一次性发送完整包'''
+            self.sock.sendall(bytearray(phead.tobytes() + bytes(command)))
+            self.sock.settimeout(None)
+        except Exception, e:
+            print "__send " + cmdType + "=> send Error:", e
+            import traceback
+            traceback.print_exc()
+            self.disconnect()
+            self.connect()
+            return False
+
+        try:
+            self.sock.settimeout(2)
+            recvBytes = bytearray(self.sock.recv(1024))
+            data = self.parseRetData(recvBytes, phead.itemsize)
+            if int(data) == 1:
+                print "Info:__send %s: %s success!"%(cmdType, command)
+                return True
+            print "Error:__send %s: %s fail!"%(cmdType, command)
+        except Exception, e:
+            print "__send " + cmdType + "=> recv Error:", e
+            return False
+
+    def parseRetData(self, recvByteArray, headSize):
+        version = recvByteArray[0]&0xff
+        recvLen = self.byte4ToInt(recvByteArray, 1)
+        if version == 0xAC and recvLen == recvByteArray.__len__():
+            byteArr = bytes(recvByteArray)
+            data = byteArr[headSize: recvByteArray.__len__()]
+            return data
+        return "0"
+
+    def byte4ToInt(self,bytearray, start):
+        i = (bytearray[start] & 0xff) \
+            | (bytearray[start+1] & 0xff) << 8 \
+            | (bytearray[start+2] & 0xff) << 16\
+            | (bytearray[start+3] & 0xff) << 24
+        return i
+
+    # 发送消息;
+    def sendKey(self, keyname, times=1, duration=1):
+        while times > 0:
+            times -= 1
+            self.__sendkey(keyname)
+            time.sleep(duration)
+    
+    # 发送多个按键;
+    def sendKeys(self, keyNames, duration):
+        for key in keyNames:
+            self.__sendCmd("ir", key)
+            time.sleep(duration)
+
+    def sendBLEKey(self, bleKeyName):
+        if TestWizardBLK.BLK_DIC.has_key(bleKeyName.lower()):
+            cmdStr = "*INPUT BLEKEY " + TestWizardBLK.BLK_DIC[bleKeyName.lower()]
+            ret = self.__sendCmd("cmd", cmdStr)
+            time.sleep(1)
+            return ret
+        else:
+            return False
+
+    '''
+    电源开关,所有电源同时控制,不区分LAN口
+    :param onOff: ON代表开,OFF代表关,区分大小写。
+    :return True代表成功,False达标失败
+    '''
+    def setAllPower(self, onOff):
+        if onOff <> "ON" and onOff <> "OFF":
+            return False
+        cmdStr = "*SET " + onOff
+        ret = self.__sendCmd("cmd", cmdStr)
+        time.sleep(1)
+        return ret
+
+    def sendUsbSwitch(self, index):
+        if index < 0 or index > 1:
+            print u"usb 索引只能0、1。当前=", index
+            return False
+        return self.__sendCmd('usb', str(index))
+
+    def loadSignalDataSet(self, file):
+        pass
+
+    def addSignalDataSet(self, file):
+        pass
+
+    def getDeviceName(self):
+        pass
+
+    def getScriptName(self):
+        pass
+
+    def getKeyName(self):
+        pass
+
+    def close(self):
+        self.disconnect()
+
+    def getCurrentDeviceName(self):
+        pass
+
+
+    '''断开连接'''
+
+    def disconnect(self):
+        try:
+            self.sock.shutdown(2)
+            self.sock.close()
+        except Exception, e:
+            print "TestWizardClient=> socket disconnect error:", e
+
+
+if __name__ == "__main__":
+    tw = TestWizardClient()
+    tw.sendUsbSwitch(0)
+    time.sleep(2)
+    tw.sendUsbSwitch(1)
+    # tw.sendKey('POWER')
+    # tw.sendKey('down')
+    # print 'sleep'
+    # time.sleep(3)
+    # tw.sendKey('up')
+    # print 111111
+    # print tw.sendBLEKey("uparrow")
+    # time.sleep(3)
+    # print tw.sendBLEKey("downarrow")
+    # time.sleep(3)
+    # print 2222222
+    # print tw.setAllPower("OFF")
     # print tw.setAllPower("ON")

+ 2395 - 2389
ssat_sdk/python_uiautomator.py

@@ -1,2389 +1,2395 @@
-# -*- coding:utf-8 -*-
-import time
-
-from ssat_sdk.ocr_convert import OCRConvert
-
-from ssat_sdk.device_manage.capturecard_manager import CCardManager
-
-from ssat_sdk.uiautomator2.ext import htmlreport
-# from uiautomator2.ext import htmlreport
-
-from ssat_sdk import uiautomator2 as u2, LoggingUtil, inspect, FeatureDetect, sat_environment, getSATTmpDIR, ImageCMP
-
-from ssat_sdk.UATree.UAT_tree import UATTree
-from ssat_sdk.tv_operator import TvOperator
-
-
-# import uiautomator2 as u2
-
-import os
-import re
-
-from ssat_sdk.utils import LoggingUtil
-from ssat_sdk.utils.string_util import getDigitFromString
-from ssat_sdk.sat_environment import getAdbDeviceSatatus
-
-pyFileName = os.path.split(__file__)[-1]
-
-
-def get_current_function_name():
-    return inspect.stack()[1][3]
-
-
-'''
-预置条件:保证执行PC和测试电视adb连接
-各个API说明的例子中到达self.pyUIAutomator 是PyUIAutomator类的实例
-self.pyUIAutomator = PyUIAutomator()
-
-'''
-
-
-class PyUIAutomator():
-    def __init__(self, serial=None):
-        try:
-            self.u = u2.connect_usb(serial) #得到UIAutomatorServer实例对象
-            self.serial = serial
-        except Exception,e:
-            print e
-        # hrp = htmlreport.HTMLReport(self.u, 'report')
-        # try:
-        #     hrp.patch_click()
-        # except Exception, e:
-        #     print e
-        #     pass
-        # self.u.app_stop_all()
-
-    def reConnect_usb(self):
-        self.u = u2.connect_usb(self.serial)
-        # hrp = htmlreport.HTMLReport(self.u, 'report')
-        # try:
-        #     hrp.patch_click()
-        # except Exception, e:
-        #     print e
-        #     pass
-        # self.u.app_stop_all()
-    '''
-    废弃,改用sat_environment.getAdbDeviceSatatus()
-    '''
-    def checkAdbConnected(self):
-        return u2.check_adb_connect()
-
-    def listenAdbConnected(self):
-        count = 5
-        while getAdbDeviceSatatus() is False and count >0:
-            count -= 1
-            LoggingUtil.printLog("PyUIAutomator","listenAdbConnected,Count %d adb not connected!"%count)
-            # self.reConnect_usb()
-            time.sleep(1)
-
-    def killAdb(self):
-        return u2.killAdb()
-
-    def getAdbType(self):
-        return u2.check_adb_connect_type()
-
-    '''
-    作用:获取UIAutomator 连接对象  后续可以使用该对象使用原生方法
-    '''
-
-    def getPyUIAutomator(self):
-        self.listenAdbConnected()
-        return self.u
-
-    '''
-    作用:Android times次按键  key:常用按键的字符串
-    主要的键值字段:home、back、left、right、up、down、center、menu、search、enter、delete ( or del)、
-    recent (recent apps)、volume_up、volume_down、volume_mute、camera、power
-    times :按键次数         默认为1次
-    duration:按键时间间隔  默认为1.0s
-
-    '''
-
-    def pressKeyTimes(self, key, times=1, duration=1.0):
-        self.listenAdbConnected()
-        self.u.pressKeyTimes(key, times, duration)
-
-    '''
-    用uiautomator view属性键作为paramDIct的键,传入的paramDict里的参数,必须有效。
-    参考session.Selector里的__fields字典
-    '''
-    def getUiObject2(self, paramDict):
-        # print "getUiObject2,paramDict:",paramDict
-        uiObject = None
-        if paramDict.__len__>0:
-            try:
-                uiObject = self.u(**paramDict)
-            except Exception,e:
-                print "UIAutomator未启动,请确认是否为开机状态"
-                uiObject = None
-            # print uiObject,uiObject.info
-        return uiObject
-
-    '''
-    作用:获取UiObject对象
-    参数:
-    className:控件类型(例如:android.widget.TextView)
-    resourceId:控件的Id(例如:lh.oslauncher:id/tv_title)
-    text:控件上的文件(例如:'主页')
-    description:控件描述
-    instance:实例参数
-
-    注意:className、resourceId、text 、description、instance是利用
-    UIautomatoview.bat工具查看控件属性看到的
-   其中 className对应 UIautomatoview.bat中的 class
-       resourceId对应 UIautomatoview.bat中的 resource-id
-      description对应 UIautomatoview.bat中的 content-desc
-         instance对应 UIautomatoview.bat中的 index
-
-
-    例子:uiObject =self.pyUIAutomator.getUiObject(resourceId="com.google.android.tvlauncher:id/banner_image", description=u"Play 电影", className = 'android.widget.ImageView')
-
-
-    '''
-
-    def getUiObject(self, className='', resourceId='', text='', description='', bounds = '',instance=-1):
-
-        if text != "":
-            if type(text) is not type([]):
-                obj = self.getUiObjectTest(className,resourceId,text,description,bounds,instance)
-                return obj
-            if type[text] is type([]):
-                count = 0
-                for t in text:
-                    try:
-                        obj = self.getUiObjectTest(className,resourceId,t,description,bounds,instance)
-                        objInfo = obj.info
-                        print "文本%s对应的option已找到"%t
-                        return obj
-                    except Exception,e:
-                        nextText = text[count+1]
-                        print"文本%s对应的option未找到,匹配下一个文本%s"%(t,nextText)
-                    count += 1
-                print "所有文本均已匹配完毕,未找到对应option"
-                return None
-
-        else:
-            obj = self.getUiObjectTest(className,resourceId,text,description,bounds,instance)
-        return obj
-
-
-
-
-
-    def getUiObjectTest(self, className='', resourceId='', text='', description='', bounds = '',instance=-1):
-        self.listenAdbConnected()
-        # print 'className:', className
-        # print 'resourceId:', resourceId
-        # print 'text:', text
-        # print 'description:', description
-        # print 'instance:', instance
-
-        uiObject = None
-        try:
-
-            if className == "" and resourceId == "" and text == "" and description == "" and\
-                    bounds == "" and instance == -1:
-                print "没有参数带入,找不到对象"
-                return uiObject
-
-            if className != "" and resourceId != "" and text != "" and description != "" and \
-                    bounds != "" and instance != -1:
-                uiObject = self.u(className=className, resourceId=resourceId, text=text, description=description,
-                                  bounds=bounds,instance=instance)
-            # 缺少一个元素的
-            #缺instance
-            if className != "" and resourceId != "" and text != "" and description != "" and \
-                    bounds != "" and instance == -1:
-                uiObject = self.u(className=className, resourceId=resourceId, text=text, bounds = bounds,
-                                  description=description)
-            #缺bounds
-            if className != "" and resourceId != "" and text != "" and description != "" and \
-                    bounds == "" and instance != -1:
-                uiObject = self.u(className=className, resourceId=resourceId, text=text,
-                                  description=description,instance = instance)
-            #缺description
-            if className != "" and resourceId != "" and text != "" and description == "" and\
-                    bounds != "" and instance != -1:
-                uiObject = self.u(className=className, resourceId=resourceId, text=text,
-                                  bounds=bounds,instance=instance)
-            #缺text
-            if className != "" and resourceId != "" and text == "" and description != "" and\
-                    bounds != "" and instance != -1:
-                uiObject = self.u(className=className, resourceId=resourceId, description=description,
-                                  bounds=bounds,instance=instance)
-            #缺resouceId
-            if className != "" and resourceId == "" and text != "" and description != "" and \
-                    bounds!="" and instance != -1:
-                uiObject = self.u(className=className, text=text, description=description,
-                                  bounds=bounds,instance=instance)
-            # lack of className
-            if className == "" and resourceId != "" and text != "" and description != "" and \
-                    bounds!="" and instance != -1:
-                uiObject = self.u(resourceId=resourceId, text=text, description=description,
-                                  bounds=bounds,instance=instance)
-
-            # 缺少两个元素的
-            # lack of className and resourceId
-            if className == "" and resourceId == "" and text != "" and description != "" and \
-                    bounds!="" and instance != -1:
-                uiObject = self.u(text=text, description=description,bounds=bounds,
-                                  instance=instance)
-            # lack of className and text
-            if className == "" and resourceId != "" and text == "" and description != "" and \
-                    bounds != "" and instance != -1:
-                uiObject = self.u(resourceId=resourceId, description=description, bounds=bounds,
-                                  instance=instance)
-            #lack of className and description
-            if className == "" and resourceId != "" and text != "" and description == "" and \
-                    bounds!="" and instance != -1:
-                uiObject = self.u(resourceId=resourceId, text=text,bounds=bounds,
-                                  instance=instance)
-            # lack of className and bounds
-            if className == "" and resourceId != "" and text != "" and description != "" and \
-                    bounds == "" and instance != -1:
-                uiObject = self.u(resourceId=resourceId, text=text, description=description,
-                                  instance=instance)
-            # lack of className and instance
-            if className == "" and resourceId != "" and text != "" and description != "" and \
-                    bounds != "" and instance == -1:
-                uiObject = self.u(resourceId=resourceId, text=text, description=description,
-                                  bounds=bounds)
-            # lack of resourceId and text
-            if className != "" and resourceId == "" and text == "" and description != "" and \
-                    bounds!="" and instance != -1:
-                uiObject = self.u(className=className, description=description,bounds=bounds,
-                                  instance=instance)
-            # lack of resourceId and description
-            if className != "" and resourceId == "" and text != "" and description == "" and \
-                    bounds!="" and instance != -1:
-                uiObject = self.u(className=className, text=text,bounds = bounds,
-                                  instance=instance)
-            # lack of resourceId and bounds
-            if className != "" and resourceId != "" and text == "" and description != "" and \
-                    bounds == "" and instance != -1:
-                uiObject = self.u(className=className, text=text, description=description,
-                                  instance=instance)
-            # lack of resourceId and instance
-            if className != "" and resourceId == "" and text != "" and description != "" and \
-                    bounds != "" and instance == -1:
-                uiObject = self.u(className=className, text=text, description=description,
-                                  bounds=bounds)
-            # lack of text and description
-            if className != "" and resourceId != "" and text == "" and description == "" and \
-                    bounds!="" and instance != -1:
-                uiObject = self.u(className=className, resourceId=resourceId, bounds=bounds,
-                                  instance=instance)
-            # lack of text and bounds
-            if className != "" and resourceId != "" and text == "" and description != "" and \
-                    bounds=="" and instance != -1:
-                uiObject = self.u(className=className, resourceId=resourceId,
-                                  description=description,instance=instance)
-            # lack of text and instance
-            if className != "" and resourceId != "" and text == "" and description != "" and \
-                    bounds!="" and instance == -1:
-                uiObject = self.u(className=className, resourceId=resourceId,
-                                  description=description,bounds=bounds)
-            # lack of description and bounds
-            if className != "" and resourceId != "" and text != "" and description == "" and \
-                    bounds=="" and instance != -1:
-                uiObject = self.u(className=className, resourceId=resourceId, text = text,
-                                  instance=instance)
-            # lack of description and instance
-            if className != "" and resourceId != "" and text != "" and description == "" and \
-                    bounds!="" and instance == -1:
-                uiObject = self.u(className=className, resourceId=resourceId, text = text,
-                                  bounds=bounds)
-            # lack of bounds and instance
-            if className != "" and resourceId != "" and text != "" and description != "" and \
-                    bounds=="" and instance == -1:
-                uiObject = self.u(className=className, resourceId=resourceId, text = text,
-                                  description=description)
-
-            # 缺少3个元素
-            # lack of className and resourceId and text
-            if className == "" and resourceId == "" and text == "" and description != "" and \
-                    bounds!="" and instance != -1:
-                uiObject = self.u(description=description, bounds=bounds,instance=instance)
-            # lack of className and resourceId and description
-            if className == "" and resourceId == "" and text != "" and description == "" and \
-                    bounds!="" and instance != -1:
-                uiObject = self.u(text = text,bounds=bounds,instance=instance)
-            # lack of className and resourceId and bounds
-            if className == "" and resourceId == "" and text != "" and description != "" and \
-                    bounds =="" and instance != -1:
-                uiObject = self.u(text = text,description=description,instance=instance)
-            # lack of className and resourceId and instance
-            if className == "" and resourceId == "" and text != "" and description != "" and \
-                    bounds!="" and instance == -1:
-                uiObject = self.u(text = text,description=description,bounds=bounds)
-            # lack of className and text and description
-            if className == "" and resourceId != "" and text == "" and description == "" and \
-                    bounds!="" and instance != -1:
-                uiObject = self.u(resourceId=resourceId,bounds=bounds,instance=instance)
-            # lack of className and text and bounds
-            if className == "" and resourceId != "" and text == "" and description != "" and \
-                    bounds=="" and instance != -1:
-                uiObject = self.u(resourceId=resourceId,description=description,instance=instance)
-             # lack of className and text and instance
-            if className == "" and resourceId != "" and text == "" and description != "" and \
-                    bounds!="" and instance == -1:
-                uiObject = self.u(resourceId=resourceId,description=description,bounds=bounds)
-            # lack of className and description and bounds
-            if className == "" and resourceId != "" and text != "" and description == "" and \
-                    bounds=="" and instance != -1:
-                uiObject = self.u(resourceId=resourceId, text = text,instance=instance)
-            # lack of className and description and instance
-            if className == "" and resourceId != "" and text != "" and description == "" and \
-                    bounds!="" and instance == -1:
-                uiObject = self.u(resourceId=resourceId, text = text,bounds=bounds)
-            # lack of resourceId and text and description
-            if className != "" and resourceId == "" and text == "" and description == "" and \
-                    bounds!="" and instance != -1:
-                uiObject = self.u(className=className,bounds=bounds,instance=instance)
-            # lack of resourceId and text and bounds
-            if className != "" and resourceId == "" and text == "" and description != "" and \
-                    bounds=="" and instance != -1:
-                uiObject = self.u(className=className,description=description,instance=instance)
-            # lack of resourceId and text and instance
-            if className != "" and resourceId == "" and text == "" and description != "" and \
-                    bounds!="" and instance == -1:
-                uiObject = self.u(className=className,description=description,bounds=bounds)
-            # lack of resourceId and description and bounds
-            if className != "" and resourceId == "" and text != "" and description == "" and \
-                    bounds =="" and instance != -1:
-                uiObject = self.u(className=className,text = text,instance=instance)
-            # lack of resourceId and description and instance
-            if className != "" and resourceId == "" and text != "" and description == "" and \
-                    bounds!="" and instance == -1:
-                uiObject = self.u(className=className, text = text,bounds=bounds)
-            # lack of resourceId and bounds and instance
-            if className != "" and resourceId == "" and text != "" and description != "" and \
-                    bounds =="" and instance == -1:
-                uiObject = self.u(className=className, text = text,description=description)
-            # lack of text and description and bounds
-            if className != "" and resourceId != "" and text == "" and description == "" and \
-                    bounds=="" and instance != -1:
-                uiObject = self.u(className=className, resourceId=resourceId,instance=instance)
-            # lack of text and description and instance
-            if className != "" and resourceId != "" and text == "" and description == "" and \
-                    bounds!="" and instance == -1:
-                uiObject = self.u(className=className, resourceId=resourceId,bounds=bounds)
-            # lack of description and bounds and instance
-            if className != "" and resourceId != "" and text != "" and description == "" and \
-                    bounds =="" and instance == -1:
-                uiObject = self.u(className=className, resourceId=resourceId, text = text)
-
-            # 缺少4个元素
-            # lack of className and resourceId and text and description
-            if className == "" and resourceId == "" and text == "" and description == "" and \
-                    bounds !="" and instance != -1:
-                uiObject = self.u(bounds=bounds,instance=instance)
-            # lack of className and resourceId and text and bounds
-            if className == "" and resourceId == "" and text == "" and description != "" and \
-                    bounds =="" and instance != -1:
-                uiObject = self.u(description=description,instance=instance)
-            # lack of className and resourceId and text and instance
-            if className == "" and resourceId == "" and text == "" and description != "" and \
-                    bounds !="" and instance == -1:
-                uiObject = self.u(description=description,bounds=bounds)
-            # lack of className and resourceId and description and bounds
-            if className == "" and resourceId == "" and text != "" and description == "" and \
-                    bounds =="" and instance != -1:
-                uiObject = self.u(text = text,instance=instance)
-            # lack of className and resourceId and description and instance
-            if className == "" and resourceId == "" and text != "" and description == "" and \
-                    bounds !="" and instance == -1:
-                uiObject = self.u(text = text,bounds=bounds)
-            # lack of resourceid and text and description and bounds
-            if className != "" and resourceId == "" and text == "" and description == "" and \
-                    bounds =="" and instance != -1:
-                uiObject = self.u(className=className,instance=instance)
-            # lack of resourceid and text and description and instance
-            if className != "" and resourceId == "" and text == "" and description == "" and \
-                    bounds !="" and instance == -1:
-                uiObject = self.u(className=className,bounds=bounds)
-            # lack of text and description and bounds and instance
-            if className != "" and resourceId != "" and text == "" and description == "" and \
-                    bounds =="" and instance == -1:
-                uiObject = self.u(className=className, resourceId=resourceId)
-
-            # 缺少5个元素的
-            # only className
-            if className != "" and resourceId == "" and text == "" and description == "" and \
-                    bounds =="" and instance == -1:
-                uiObject = self.u(className=className)
-            # only resourceid
-            if className == "" and resourceId != "" and text == "" and description == "" and \
-                    bounds =="" and instance == -1:
-                uiObject = self.u(resourceId=resourceId)
-            # only text
-            if className == "" and resourceId == "" and text != "" and description == "" and \
-                    bounds =="" and instance == -1:
-                uiObject = self.u(text = text)
-            # only description
-            if className == "" and resourceId == "" and text == "" and description != "" and \
-                    bounds =="" and instance == -1:
-                uiObject = self.u(description=description)
-            # only bounds
-            if className == "" and resourceId == "" and text == "" and description == "" and \
-                    bounds !="" and instance == -1:
-                uiObject = self.u(bounds=bounds)
-            # only instance
-            if className == "" and resourceId == "" and text == "" and description == "" and \
-                    bounds =="" and instance == -1:
-                uiObject = self.u(instance=instance)
-            return uiObject
-        except Exception, e:
-            print "异常:",e
-            return uiObject
-
-    ''' 
-    作用:判断当前界面中指定控件是否存在
-    参数:
-    className:控件类型(例如:android.widget.TextView)
-    resourceId:控件的Id(例如:lh.oslauncher:id/tv_title)
-    text:控件上的文件(例如:'主页')
-    description:控件描述
-    instance:实例参数
-
-    注意:className、resourceId、text 是利用WEditor工具查看控件属性看到的
-
-    例子:isExists = self.pyUIAutomator.isElementExist("android.widget.TextView",
-                                                     resourceId="com.apps.weather:id/today_tv_city",
-                                                     text='鞍山')
-    '''
-
-    def isElementExist(self, className='', resourceId='', text='', description='', instance=-1):
-        self.listenAdbConnected()
-        try:
-            uiObject = self.getUiObject(className, resourceId, text, description, instance)
-            print 'uiObject:', uiObject
-            if uiObject:
-                isExists = uiObject.exists()
-                print 'isExists:', isExists
-            else:
-                isExists = False
-        except Exception, e:
-            print e
-            isExists = False
-        return isExists
-
-    ''' 
-    作用:获取指定控件的文字属性
-    参数:
-    className:控件类型(例如:android.widget.TextView)
-    resourceId:控件的Id(例如:lh.oslauncher:id/tv_title)
-    注意:className、resourceId 是利用WEditor工具查看控件属性看到的
-    例子: str_play_time = self.pyUIAutomator.getTextById("android.widget.TextView",
-                                                       resourceId="com.apps.usbmediaplayer:id/playingTime")
-    '''
-
-    def getTextById(self, className, resourceId):
-        self.listenAdbConnected()
-        try:
-            text = self.u(className=className, resourceId=resourceId).get_text()
-        except Exception, e:
-            print e
-            text = ''
-        return text
-
-    '''
-    作用:执行adb命令
-    参数:adbCmd:要被执行的adb命令
-    注意:adbCmd不需要输入adb shell
-    例子:self.pyUIAutomator.adb_shell('am start -n com.apps.factory.ui/.designmenu.DesignMenuActivity')
-    '''
-
-    def adb_shell(self, adbCmd):
-        self.listenAdbConnected()
-        try:
-            self.u.adb_shell(adbCmd)
-        except Exception, e:
-            print e
-
-    ''' 
-    作用:设置android.widget.EditText 的文字内容
-    参数:text 要输入到EditText的文字内容
-    注意:保证焦点在该EditText,处于可以输入的状态
-    例子:self.pyUIAutomator.send_text(u'鞍山')
-    '''
-
-    def send_text(self, text):
-        self.listenAdbConnected()
-        try:
-            self.u.set_fastinput_ime(True)  # 切换成FastInputIME输入法
-            self.u.clear_text()
-            self.u.send_keys(unicode(text))  # adb广播输入
-            self.u.set_fastinput_ime(False)  # 切换成正常的输入法
-        except Exception, e:
-            print e
-
-    ''' 
-       作用:向电视机发送action动作
-       参数:actin 要执行的动作
-       注意:焦点在搜索界面输入框中,在搜索界面输入完搜索内容,执行该搜索动作
-       例子:小米谷歌商店搜索应用  搜索 keep应用
-           self.pyUIAutomator.send_text("kee")
-           self.pyUIAutomator.send_action("search")
-       '''
-
-    def send_action(self, action):
-        self.listenAdbConnected()
-        try:
-            self.u.set_fastinput_ime(True)  # 切换成FastInputIME输入法
-            self.u.send_action(action)
-            self.u.set_fastinput_ime(False)  # 切换成正常的输入法
-        except Exception, e:
-            print e
-
-    ''' 
-     作用:保存界面截图到指定地址
-     参数:pngPath 要保存图片的全路径名称
-     注意:保证该图片路径所在文件夹存在
-     例子:self.pyUIAutomator.screenshot(
-            r'E:\svn\SAT\SAT_Runner\btc_runner_se\runner\smoking\report\imgs\testUSB_Desk_Focused.png')
-     '''
-
-    def screenshot(self, pngPath):
-        self.listenAdbConnected()
-        try:
-            self.u.screenshot(pngPath)
-        except Exception, e:
-            print e
-
-    ''' 
-       作用:在Android 一个土司提示框  方便在电视机中看到跑到哪里
-       参数:
-       toastText:土司的内容
-       toastTime:土司存留时间
-       注意:
-       例子:self.pyUIAutomator.makeToast('测试开始', 3)
-       '''
-
-    def makeToast(self, toastText, toastTime=1):
-        self.listenAdbConnected()
-        try:
-            self.u.make_toast(toastText, toastTime)
-        except Exception, e:
-            print e
-
-    ''' 
-        作用:启动指定包名的应用的指定Activity
-        参数:
-        pkg_name:指定的包名
-        activity:指定的activity名
-        注意:
-        例子:self.pyUIAutomator.startApp('com.apps.usbmediaplayer')
-              self.pyUIAutomator.startApp('com.apps.usbmediaplayer','com.apps.usbmediaplayer.activity.MainActivity')
-        '''
-
-    def startApp(self, pkg_name, activity=None):
-        self.listenAdbConnected()
-        try:
-            if activity:
-                self.u.app_start(pkg_name, activity=activity)
-            else:
-                self.u.app_start(pkg_name)
-        except Exception, e:
-            print e
-
-    ''' 
-        作用:关闭某一指定包名的APP
-        参数:
-        pkg_name:指定的包名
-        注意:
-        例子:self.pyUIAutomator.stopApp('com.apps.usbmediaplayer')
-     '''
-
-    def stopApp(self, pkg_name):
-        self.listenAdbConnected()
-        try:
-            self.u.app_stop(pkg_name)
-        except Exception, e:
-            print e
-
-    ''' 
-        作用:关闭Android系统中所有的运行程序
-        参数:
-        注意:
-        例子:self.pyUIAutomator.stopAllApps()
-     '''
-
-    def stopAllApps(self):
-        self.listenAdbConnected()
-        try:
-            self.u.app_stop_all()
-        except Exception, e:
-            print e
-
-    ''' 
-        作用:停止uiautomator守护程序,允许其他测试框架如 appium 运行
-        参数:
-        注意:
-        例子:self.pyUIAutomator.stopUIAutomatorService()
-     '''
-
-    def stopUIAutomatorService(self):
-        self.listenAdbConnected()
-        try:
-            self.u.service("uiautomator").stop()
-        except Exception, e:
-            print e
-
-    '''
-      作用:根据传入的chooseType,获取当前界面聚焦的对象
-      参数:
-      注意:
-      例子:self.pyUIAutomator.getChoosedUIObject("focus")
-    '''
-    def getChoosedUIObject(self, chooseType, resourceId='', text=''):
-        if chooseType.lower() == "focus":
-            return self.getFocusedUIObject(resourceId=resourceId, text=text)
-        elif chooseType.lower() == "select":
-            return self.getSelectedUIObject(resourceId=resourceId, text=text)
-        else:
-            return None
-
-    ''' 
-          作用:获取当前界面的focused的UIObject对象
-          参数:
-          注意:
-          例子:self.pyUIAutomator.getFocusedUIObject()
-       '''
-
-    def getFocusedUIObject(self, resourceId='', text=''):
-        self.listenAdbConnected()
-        focusedUIObject = None
-        try:
-            if resourceId != "" and text != "":
-                focusedUIObject = self.u(resourceId=resourceId, text=text, focused=True)
-            elif resourceId == "" and text != "":
-                focusedUIObject = self.u(text=text, focused=True)
-            elif resourceId != "" and text == "":
-                focusedUIObject = self.u(resourceId=resourceId, focused=True)
-            else:
-                focusedUIObject = self.u(focused=True)
-        except Exception, e:
-            print "getFocusedUIObject Error:", e
-        return focusedUIObject
-
-    ''' 
-            作用:获取当前界面的selected的UIObject对象
-            参数:
-            注意:
-            例子:self.pyUIAutomator.getFocusedUIObject()
-         '''
-
-    def getSelectedUIObject(self, resourceId='', text=''):
-        self.listenAdbConnected()
-        try:
-            if resourceId != "" and text != "":
-                selectedUIObject = self.u(resourceId=resourceId, text=text, selected=True)
-            elif resourceId == "" and text != "":
-                selectedUIObject = self.u(text=text, selected=True)
-            elif resourceId != "" and text == "":
-                selectedUIObject = self.u(resourceId=resourceId, selected=True)
-            else:
-                selectedUIObject = self.u(selected=True)
-            return selectedUIObject
-        except Exception, e:
-            print "getSelectedUIObject Error:", e
-            return None
-
-    ''' 
-            作用:在脚本运行过程中,监听弹框的出现,如果出现则按按钮使其消失,使得脚本中获取元素控件时不被遮挡报错
-            参数:watcherName 监听器名称
-                 dialogText 弹框的text
-                 buttonText 使弹框消失的按钮text
-            注意: 1.这个函数只是创建监听器,这些监听器生效使用u.startWatchers(),监听器失效使用u.stopWatchers();
-                   2.一般使用在脚本运行实际执行界面前 声明监听器、调用 u.startWatchers(),在脚本 结束时候调用u.stopWatchers()
-            例子:self.pyUIAutomator.getFocusedUIObject()
-                u = PyUIAutomator()
-                u.createDilogWatcher("Normal Dialog Watcher", "我是一个普通Dialog", "确定")
-                u.startWatchers()
-                #这30S延时代表脚本执行
-                time.sleep(30)
-                 u.stopWatchers()
-         '''
-
-    def createDilogWatcher(self, watcherName, dialogText, buttonText):
-        self.listenAdbConnected()
-        self.u.watcher(watcherName).when(text=dialogText).click(text=buttonText)
-
-    ''' 
-             作用:使得创建的弹框监听器生效
-             参数:
-             注意:
-
-          '''
-
-    def startWatchers(self):
-        self.listenAdbConnected()
-        self.u.watchers.watched = True
-
-    ''' 
-             作用:使得创建的弹框监听器失效
-             参数:
-             注意:
-
-          '''
-
-    def stopWatchers(self):
-        self.listenAdbConnected()
-        self.u.watchers.watched = False
-
-    def dump_hierarchy(self):
-        self.listenAdbConnected()
-        return self.u.dump_hierarchy()
-
-
-class DirectionManageAndroid():
-    def __init__(self):
-
-        self.className = self.__class__.__name__
-
-        self.grid = None
-        self.remote = TvOperator()
-
-    def goOneStep(self, pyUIAutomator, direction, keyType = UATTree.Key_Event):
-        # keyDict = {'U': "up", "D": "down", "L": "left", "R": "right"}
-        print "goOneStep,direction:", direction
-        directionArr = direction.split("-")
-        for direct in directionArr:
-            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(),
-                                              u"script:sendKey 执行按键:%s" % (direct))
-            # self.custSendKey(keyDict[direct])
-            if keyType == UATTree.Key_Event:
-                pyUIAutomator.pressKeyTimes(direct)
-            elif keyType == UATTree.Key_IR:
-                self.remote.sendKey(direct)
-
-    # def custSendKey(self, keyEvent):
-    #     if type(keyEvent) == type('') or type(keyEvent) == type(u""):
-    #         self.operator.sendKey(keyEvent)
-    #     if type(keyEvent) == type([]):
-    #         try:
-    #             self.operator.sendKey(keyEvent[0], keyEvent[1], keyEvent[2])
-    #         except Exception:
-    #             LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(),
-    #                                               u'script:sendKey  ERROR 双击按键数组%s格式出错,请按照格式:[按键字符,按键次数,按键间隔]' % (
-    #                                                   keyEvent))
-
-    '''
-    计算目标区域在当前焦点区域的方位.
-            |               |
-    左上角  |      上       |   右上角
-            |               |
-    -----------------------------------
-            |               |
-            |               |
-     左     |    焦点区域   |    右
-            |               |
-    -----------------------------------
-            |               |
-     左下角 |      下       |   右下角
-            |               |
-
-    区域的方位,按上图,分成4面位方向和4角位方向。
-    4面位方向:上、下、左、右。只要目标区域,和4面位方向区域有交集,就属于面位方向。
-    4角位方向:左上角、右上角、左下角、右下角。目标区域全部在4角位方向区域,才是角位方向。
-    方位定义:up、down、left、right、left-up、left-down、right-up、right-down
-    :param areaL:当前焦点所在区域
-    :param areaT:目标区域
-    :return direction。字符串类型,方位:up、down、left、right、left-up、left-down、right-up、right-down
-    '''
-    ImgRectLimit = 10000
-
-    def getTargetDirection(self, focusedBounds, destBounds, moveKey=["down","up"]):
-        areaL = (focusedBounds['left'], focusedBounds['top'], focusedBounds['right'], focusedBounds['bottom'])
-        areaT = (destBounds['left'], destBounds['top'], destBounds['right'], destBounds['bottom'])
-        print "getTargetDirection:", areaL, areaT
-        LoggingUtil.getDebugLogger().info(pyFileName, self.className,
-                                          get_current_function_name(),
-                                          u'script:focusMatch 当前聚焦区域坐标:%s  目标聚焦区域坐标:%s' % (str(areaL), str(areaT)))
-        if self.hasSameArea(areaL,areaT):
-            focusCP = [(areaL[2] + areaL[0])/2,(areaL[3]+areaL[1])/2]
-            destCP = [(areaT[2] + areaT[0])/2,(areaT[3]+areaT[1])/2]
-            return self.getPointDirection(focusCP,destCP)
-        # 确定4个面位方位的区域的x,y轴范围
-        up = [areaL[0], 0, areaL[2], areaL[1]]
-        down = [areaL[0], areaL[3], areaL[2], DirectionManageAndroid.ImgRectLimit]
-        left = [0, areaL[1], areaL[0], areaL[3]]
-        right = [areaL[2], areaL[1], DirectionManageAndroid.ImgRectLimit, areaL[3]]
-        # 检测目标区域是否和4个面位方向区域有重叠部分
-        up_direct = self.hasSameArea(up, areaT)
-        if up_direct:
-            return "up"
-        down_direct = self.hasSameArea(down, areaT)
-        if down_direct:
-            return "down"
-        left_direct = self.hasSameArea(left, areaT)
-        if left_direct:
-            return "left"
-        right_direct = self.hasSameArea(right, areaT)
-        if right_direct:
-            return "right"
-
-        # 计算目标区域坐上角点的方位,x,y差值确定角位方向
-        dL_Ux = areaT[0] - areaL[0]
-        dL_Uy = areaT[1] - areaL[1]
-        if (dL_Ux > 0) and (dL_Uy > 0):
-            if "down" in moveKey:
-                return "down-right"
-            else:
-                return "right-down"
-        if (dL_Ux < 0) and (dL_Uy > 0):
-            if "down" in moveKey:
-                return "down-left"
-            else:
-                return "left-down"
-        if (dL_Ux > 0) and (dL_Uy < 0):
-            if "down" in moveKey:
-                return "up-right"
-            else:
-                return "right-up"
-        if (dL_Ux < 0) and (dL_Uy < 0):
-            if "down" in moveKey:
-                return "up-left"
-            else:
-                return "left-up"
-
-    '''
-    计算点p2相对p1的方位。按p1 45°斜线划分上下左右。
-    '''
-    def getPointDirection(self,p1,p2):
-        print "getPointDirection,p1,p2:",p1,p2
-        dx = p2[0] - p1[0]
-        dy = p2[1] - p1[1]
-        if dx >= 0 and abs(dy) < abs(dx):
-            return "right"
-        if dx <= 0 and abs(dy) < abs(dx):
-            return "left"
-        if dy >= 0 and abs(dy) >= abs(dx):
-            return "down"
-        if dy <= 0 and abs(dy) >= abs(dx):
-            return "up"
-        return "center"
-
-
-    '''
-    检测两个区域是否有重叠部分
-    '''
-
-    def hasSameArea(self, area1, area2):
-        # 计算各区域宽、高
-        area1_W = area1[2] - area1[0]
-        area1_H = area1[3] - area1[1]
-        area2_W = area2[2] - area2[0]
-        area2_H = area2[3] - area2[1]
-
-        # 计算目标区域坐上角点的方位
-        dL_Ux = area1[0] - area2[0]
-        dL_Uy = area1[1] - area2[1]
-        # 计算x坐标,判断是否有重叠可能。
-        x_enable = False
-        if dL_Ux > 0:
-            if (abs(dL_Ux) - area2_W) >= 0:
-                x_enable = False
-            else:
-                x_enable = True
-        else:
-            if (abs(dL_Ux) - area1_W) >= 0:
-                x_enable = False
-            else:
-                x_enable = True
-
-        # 计算y坐标,判断是否有重叠可能。
-        y_enable = False
-        if dL_Uy > 0:
-            if (abs(dL_Uy) - area2_H) > 0:
-                y_enable = False
-            else:
-                y_enable = True
-        else:
-            if (abs(dL_Uy) - area1_H) > 0:
-                y_enable = False
-            else:
-                y_enable = True
-
-        # 如果x坐标、y坐标都有可能重叠,则一定重叠。反之,没有重叠。
-        return x_enable and y_enable
-
-    def isSameBounds(self, BoundA, BoundB):
-        areaA = (BoundA['left'], BoundA['top'], BoundA['right'], BoundA['bottom'])
-        areaB = (BoundB['left'], BoundB['top'], BoundB['right'], BoundB['bottom'])
-        if areaA[0] == areaB[0] \
-                and areaA[1] == areaB[1] \
-                and areaA[2] == areaB[2] \
-                and areaA[3] == areaB[3]:
-            return True
-        else:
-            return False
-
-    def isHasAnotherBounds(self, BoundA, BoundB):
-        areaA = (BoundA['left'], BoundA['top'], BoundA['right'], BoundA['bottom'])
-        areaB = (BoundB['left'], BoundB['top'], BoundB['right'], BoundB['bottom'])
-
-        if areaA[0] <= areaB[0] and areaA[1] <= areaB[1] and areaA[2] >= areaB[2] and areaA[3] >= areaB[3]:
-            return True
-        else:
-            return False
-
-    def isRelativePositionBounds(self, initFocusedArea, initDestTextArea, BoundA, BoundB):
-        # initFocusedArea = (
-        #     initFocusedBound['left'], initFocusedBound['top'], initFocusedBound['right'], initFocusedBound['bottom'])
-        # initDestTextArea = (
-        #     initFocusedBound['left'], initDestTextBound['top'], initDestTextBound['right'], initDestTextBound['bottom'])
-
-        areaA = (BoundA['left'], BoundA['top'], BoundA['right'], BoundA['bottom'])
-        areaB = (BoundB['left'], BoundB['top'], BoundB['right'], BoundB['bottom'])
-
-        # if areaA[0] < areaB[0] and areaA[1] < areaB[1] and areaA[2] > areaB[2] and areaA[3] >areaB[3]:
-        #     return True
-        # else:
-        #     return False
-        if initFocusedArea[0] - initDestTextArea[0] == areaA[0] - areaB[0] and initFocusedArea[1] - initDestTextArea[
-            1] == areaA[1] - areaB[1] and initFocusedArea[2] - initDestTextArea[2] == areaA[2] - areaB[2] and \
-                initFocusedArea[3] - initDestTextArea[3] == areaA[3] - areaB[3]:
-            return True
-        else:
-            return False
-        # 比较两个Bound面积大小
-
-    def isBiggerBound(self, boundA, boundB):
-        # print 'boundA:',boundA
-        # print 'boundB:',boundB
-        if ((boundA["bottom"] - boundA["top"]) * (boundA["right"] - boundA["left"])) >= ((
-                                                                                                 boundB["bottom"] -
-                                                                                                 boundB["top"]) * (
-                                                                                                 boundB["right"] -
-                                                                                                 boundB["left"])):
-            return True
-        else:
-            return False
-
-    def getRelativeBounds(self, standBounds_view1, standBounds_view2, currentBound_view1):
-        currentBound_view2 = {}
-        currentBound_view2["top"] = standBounds_view2["top"] - standBounds_view1["top"] + currentBound_view1["top"]
-        currentBound_view2["left"] = standBounds_view2["left"] - standBounds_view1["left"] + currentBound_view1["left"]
-        currentBound_view2["right"] = standBounds_view2["right"] - standBounds_view1["right"] + currentBound_view1[
-            "right"]
-        currentBound_view2["bottom"] = standBounds_view2["bottom"] - standBounds_view1["bottom"] + currentBound_view1[
-            "bottom"]
-
-        return currentBound_view2
-
-
-class FocusManageAndroid():
-    # u是PyUIAutomator对象
-    # focusManage 是DirectionManageAndroid对象
-    def __init__(self, u, directionManageAndroid):
-        self.className = self.__class__.__name__
-        self.u = u
-        self.directionManageAndroid = directionManageAndroid
-        self.ReverseDirctionDict = {"up": "down", "down": "up", "left": "right", "right": "left"}
-        self.featureDetect = FeatureDetect()
-        self.ccard = CCardManager()
-        self.imgCMP = ImageCMP()
-        self.ocr = OCRConvert()
-        self.remote = TvOperator()
-
-    '''
-                     作用:在RecyclerView 中 focused控件包含文字控件  用于聚焦目标焦点
-                     参数:
-                     text:目标文字的文字内容
-                     className:目标文字的className
-                     findDirection: 目标文字控件被隐藏时寻找的方向
-                     Max_Try:最多查找次数
-                     注意:
-                     例子:
-                     小米信源设置界面  聚焦到Device Settings/Sound mode
-                     className = "android.widget.TextView"
-                     focusManageAndroid.toDestFocusByText_className_for_RecyclerView("News", className)
-                  '''
-
-    # 在RecyclerView 中 focused控件包含文字控件
-    def toDestFocusByText_className_for_RecyclerView(self, text, className, findDirection="down", Max_Try=10, keyType = UATTree.Key_Event):
-        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
-        count = 0
-        Max_Try = Max_Try
-        Reversecount = 0
-        Reverse_Max_Try = Max_Try * 2
-        while (True):
-            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
-                break
-            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
-            time.sleep(0.5)
-            focusedUIObject = self.u.getFocusedUIObject()
-            focusedBounds = focusedUIObject.info['bounds']
-            # print "focusedBounds:",focusedBounds
-            destUIObject = self.u.getUiObject(text=text, className=className)
-            if destUIObject:
-                try:
-                    destBounds = destUIObject.info['bounds']
-                    # print "destBounds:", destBounds
-                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
-                        print '成功聚焦到目标焦点:', text
-                        return True
-                    else:
-                        count = count + 1
-                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
-                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
-                except Exception:
-                    # 出现控件出现一半的时候,获取控件信息会报错
-                    if count < Max_Try:
-                        count = count + 1
-                        self.pressKeyByType(findDirection, keyType)
-                    else:
-                        Reversecount = Reversecount + 1
-                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
-            else:
-                if count < Max_Try:
-                    count = count + 1
-                    self.pressKeyByType(findDirection, keyType)
-                else:
-                    Reversecount = Reversecount + 1
-                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-
-        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
-        return False
-
-    ''' 
-            作用:在RecyclerView 中 focused控件包含文字控件  用于聚焦目标焦点
-            参数:
-            text:目标文字的文字内容
-            textResourceId:目标文字的resourceId
-            findDirection: 目标文字控件被隐藏时寻找的方向
-            Max_Try:最多查找次数
-            注意:
-            例子:
-            小米信源设置界面  聚焦到Device Settings
-            focusManageAndroid.toDestFocusByText_for_RecyclerView("Device Settings","com.android.tv:id/title")
-         '''
-
-    # 在RecyclerView 中 focused控件包含文字控件
-    def toDestFocusByText_for_RecyclerView(self, text, textResourceId="", findDirection="down", Max_Try=10, keyType = UATTree.Key_Event, hb_keyDict={}):
-        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
-        count = 0
-        Max_Try = Max_Try
-        Reversecount = 0
-        Reverse_Max_Try = Max_Try * 2
-        while (True):
-            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
-                break
-            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
-            time.sleep(0.5)
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            focusedUIObject = self.u.getFocusedUIObject()
-            try:
-                focusedBounds = focusedUIObject.info['bounds']
-            except Exception, e:
-                print "未能获取到当前聚焦焦点,聚焦框丢失,判断聚焦失败!!"
-                return False
-            print "focusedBounds:",focusedBounds
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            destUIObject = self.u.getUiObject(text=text, resourceId=textResourceId)
-
-            if destUIObject:
-                try:
-                    destBounds = destUIObject.info['bounds']
-                    print "destBounds:", destBounds
-                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
-                        print '成功聚焦到目标焦点:', text
-                        return True
-                    else:
-                        count = count + 1
-                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
-                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
-                except Exception:
-                    # 出现控件出现一半的时候,获取控件信息会报错
-                    if count < Max_Try:
-                        count = count + 1
-                        self.pressKeyByType(findDirection, keyType)
-                    else:
-                        Reversecount = Reversecount + 1
-                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
-            else:
-                if count < Max_Try:
-                    count = count + 1
-                    self.pressKeyByType(findDirection, keyType)
-                else:
-                    Reversecount = Reversecount + 1
-                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-
-        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
-        return False
-
-
-    '''
-            作用:在RecyclerView 中 focused控件包含文字控件  用于聚焦目标焦点,传入的resourceId为聚焦焦点的resId时的处理函数
-            参数:
-            text:目标文字的文字内容
-            focuseResId:整个聚焦焦点的resourceId
-            findDirection: 目标文字控件被隐藏时寻找的方向
-            Max_Try:最多查找次数
-            注意:
-            例子:
-            小米俄罗斯设置界面 存在复数个聚焦焦点时,使用此函数,可以规避复数焦点的问题
-    '''
-    def toDestFocusByText_with_FocuseResourceId(self, text, focuseResId, findDirection="down", Max_Try=10, keyType = UATTree.Key_Event, hb_keyDict = {}):
-        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
-        count = 0
-        Max_Try = Max_Try
-        Reversecount = 0
-        Reverse_Max_Try = Max_Try * 2
-        while (True):
-            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
-                break
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
-            time.sleep(0.5)
-            focusedUIObject = self.u.getFocusedUIObject(resourceId=focuseResId)
-            try:
-                focusedBounds = focusedUIObject.info['bounds']
-            except Exception, e:
-                print "未能获取到当前聚焦焦点,聚焦框丢失,判断聚焦失败!!"
-                return False
-            print "focusedBounds:",focusedBounds
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            destUIObject = self.u.getUiObject(text=text)
-            if destUIObject:
-                try:
-                    destBounds = destUIObject.info['bounds']
-                    print "destBounds:", destBounds
-                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
-                        print '成功聚焦到目标焦点:', text
-                        return True
-                    else:
-                        count = count + 1
-                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
-                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
-                except Exception:
-                    # 出现控件出现一半的时候,获取控件信息会报错
-                    if count < Max_Try:
-                        count = count + 1
-                        self.pressKeyByType(findDirection, keyType)
-                    else:
-                        Reversecount = Reversecount + 1
-                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
-            else:
-                if count < Max_Try:
-                    count = count + 1
-                    self.pressKeyByType(findDirection, keyType)
-                else:
-                    Reversecount = Reversecount + 1
-                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-
-        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
-        return False
-
-
-
-    ''' 
-             作用:在RecyclerVi或listView 中 selected控件包含文字控件  用于聚焦目标焦点
-             参数:
-             text:目标文字的文字内容
-             textResourceId:目标文字的resourceId
-             listViewResourceId:RecyclerVi或listView的resourceId
-             findDirection: 目标文字控件被隐藏时寻找的方向
-             Max_Try:最多查找次数
-             注意:
-             例子:
-             2841项目信源列表聚焦  聚焦到HDMI1
-             focusManageAndroid.toDestSelectByText_for_RecyclerView("HDMI1","com.android.tv:id/title","com.android.tv:id/scene_transition_common")
-          '''
-
-    def toDestSelectByText_for_RecyclerView(self, text, textResourceId='', optionViewResoucreId='', listViewResourceId='', findDirection="down",
-                                            Max_Try=10, keyType = UATTree.Key_Event, hb_keyDict={}):
-        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
-        count = 0
-        Max_Try = Max_Try
-        Reversecount = 0
-        Reverse_Max_Try = Max_Try * 2
-        while (True):
-            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
-                break
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            listViewUIObject = self.u.getUiObject(resourceId=listViewResourceId)
-            print "listViewUIObject.info:", listViewUIObject.info
-            if optionViewResoucreId != '':
-                selectedUIObject = listViewUIObject.child(selected=True, resourceId=optionViewResoucreId)
-            else:
-                selectedUIObject = listViewUIObject.child(selected=True)
-            print "selectedUIObject.info:", selectedUIObject.info
-            selectedBounds = selectedUIObject.info['bounds']
-            # print "focusedBounds:",focusedBounds
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            destUIObject = self.u.getUiObject(text=text, resourceId=textResourceId)
-            if destUIObject:
-                destBounds = destUIObject.info['bounds']
-                # print "destBounds:", destBounds
-                if self.directionManageAndroid.isHasAnotherBounds(selectedBounds, destBounds):
-                    print '成功聚焦到目标焦点:', text
-                    return True
-                else:
-                    count = count + 1
-                direction = self.directionManageAndroid.getTargetDirection(selectedBounds, destBounds)
-                self.directionManageAndroid.goOneStep(self.u, direction, keyType)
-            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
-            else:
-                if count < Max_Try:
-                    count = count + 1
-                    self.pressKeyByType(findDirection, keyType)
-                else:
-                    Reversecount = Reversecount + 1
-                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-
-        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
-        return False
-
-    ''' 
-                 作用:在界面中 中 focused控件包含content-des控件  用于聚焦目标焦点
-                 参数:
-                 description:目标描述的文字内容
-                 descriptionResourceId:目标描述的resourceId
-                 findDirection: 目标文字控件被隐藏时寻找的方向
-                 Max_Try:最多查找次数
-                 注意:
-                 例子:
-                 小米APP  聚焦到YouTube
-                 descriptionResourceId = "com.google.android.tvlauncher:id/banner_image"
-                 focusManageAndroid.toDestFocusByDescription_for_RecyclerView("YouTube",descriptionResourceId=descriptionResourceId)
-              '''
-
-    # 在RecyclerView 中 focused控件包含文字控件
-    def toDestFocusByDescription_for_RecyclerView(self, description, descriptionResourceId="", findDirection="down",
-                                                  Max_Try=10 ,keyType = UATTree.Key_Event, hb_keyDict = {}):
-        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
-        count = 0
-        Max_Try = Max_Try
-        Reversecount = 0
-        Reverse_Max_Try = Max_Try * 2
-        while (True):
-            print "count:", count
-            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
-                break
-            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
-            time.sleep(0.5)
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            focusedUIObject = self.u.getFocusedUIObject()
-            focusedBounds = focusedUIObject.info['bounds']
-            # print "focusedBounds:",focusedBounds
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            destUIObject = self.u.getUiObject(description=description, resourceId=descriptionResourceId)
-            if destUIObject:
-                try:
-                    destBounds = destUIObject.info['bounds']
-                    # print "destBounds:", destBounds
-                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
-                        print '成功聚焦到目标焦点:', description
-                        return True
-                    else:
-                        count = count + 1
-                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
-                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
-                except Exception, e:
-                    # 出现控件出现一半的时候,获取控件信息会报错
-                    if count < Max_Try:
-                        count = count + 1
-                        self.pressKeyByType(findDirection, keyType)
-                    else:
-                        Reversecount = Reversecount + 1
-                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
-            else:
-                if count < Max_Try:
-                    count = count + 1
-                    self.pressKeyByType(findDirection, keyType)
-                else:
-                    Reversecount = Reversecount + 1
-                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-
-        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
-        return False
-
-
-        # 在RecyclerView 中 focused控件包含bounds控件
-    def toDestFocusByBounds_for_RecyclerView(self, bounds, boundsResourceId="", findDirection="down",
-                                             Max_Try=10, keyType=UATTree.Key_Event, hb_keyDict={}):
-        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
-        count = 0
-        Max_Try = Max_Try
-        Reversecount = 0
-        Reverse_Max_Try = Max_Try * 2
-        while (True):
-            print "count:", count
-            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
-                break
-            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
-            time.sleep(0.5)
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            focusedUIObject = self.u.getFocusedUIObject()
-            focusedBounds = focusedUIObject.info['bounds']
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            try:
-                destBounds = bounds
-                if self.directionManageAndroid.isHasAnotherBounds(focusedBounds,destBounds):
-                    print "success to focus the target:",bounds
-                    return True
-                else:
-                    count = count + 1
-                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds,destBounds)
-                    self.directionManageAndroid.goOneStep(self.u,direction,keyType)
-            except Exception,e:
-                # 出现控件出现一半的时候,获取控件信息会报错
-                if count < Max_Try:
-                    count = count + 1
-                    self.pressKeyByType(findDirection, keyType)
-                else:
-                    Reversecount = Reversecount + 1
-                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-                # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
-            else:
-                if count < Max_Try:
-                    count = count + 1
-                    self.pressKeyByType(findDirection, keyType)
-                else:
-                    Reversecount = Reversecount + 1
-                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-
-            print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
-            return False
-
-    # class&Bounds
-    def toDestFocusByClassBounds_for_RecyclerView(self, className, bounds,classResourceId="", findDirection="down",
-                                             Max_Try=10, keyType=UATTree.Key_Event, hb_keyDict={}):
-        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
-        count = 0
-        Max_Try = Max_Try
-        Reversecount = 0
-        Reverse_Max_Try = Max_Try * 2
-        while (True):
-            print "count:", count
-            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
-                break
-            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
-            time.sleep(0.5)
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            focusedUIObject = self.u.getFocusedUIObject()
-            print "zhouyifocusedUIObject",focusedUIObject
-            print "zhouyifocusedUIObject.info", focusedUIObject.info
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            destUIObject = self.u.getUiObject(className=className, bounds = bounds,resourceId=classResourceId)
-            print "destUIObject",destUIObject
-            try:
-                destUIObjectInfo = destUIObject.info
-                print "destUIObjectInfo",destUIObjectInfo
-            except Exception,e:
-                print "异常:",e
-                # print "destUIObject.info",destUIObjectInfo
-            try:
-                # destBounds = destUIObject.info['bounds']
-                destBounds = bounds
-                print "destBounds:", destBounds
-                if self.directionManageAndroid.isHasAnotherBounds(focusedBounds,destBounds):
-                    print "c&b success to focus the target:",bounds
-                    return True
-                else:
-                    count = count + 1
-                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds,destBounds)
-                    self.directionManageAndroid.goOneStep(self.u,direction,keyType)
-            except Exception,e:
-                # 出现控件出现一半的时候,获取控件信息会报错
-                if count < Max_Try:
-                    count = count + 1
-                    self.pressKeyByType(findDirection, keyType)
-                else:
-                    Reversecount = Reversecount + 1
-                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-                # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
-            else:
-                if count < Max_Try:
-                    count = count + 1
-                    self.pressKeyByType(findDirection, keyType)
-                else:
-                    Reversecount = Reversecount + 1
-                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-
-            print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
-            return False
-
-        # class
-        def toDestFocusByClass_for_RecyclerView(self, className, classResourceId="", findDirection="down",
-                                                Max_Try=10, keyType=UATTree.Key_Event, hb_keyDict={}):
-            # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
-            count = 0
-            Max_Try = Max_Try
-            Reversecount = 0
-            Reverse_Max_Try = Max_Try * 2
-            while (True):
-                print "count:", count
-                if count >= Max_Try and Reversecount >= Reverse_Max_Try:
-                    break
-                # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
-                time.sleep(0.5)
-                if hb_keyDict != {}:
-                    # 执行心跳按键
-                    self.executeHeartBeatKey(hb_keyDict)
-                focusedUIObject = self.u.getFocusedUIObject()
-                print "zhouyifocusedUIObject", focusedUIObject
-                print "zhouyifocusedUIObject.info", focusedUIObject.info
-                if hb_keyDict != {}:
-                    # 执行心跳按键
-                    self.executeHeartBeatKey(hb_keyDict)
-                destUIObject = self.u.getUiObject(className=className, resourceId=classResourceId)
-                print "destUIObject", destUIObject
-                try:
-                    destUIObjectInfo = destUIObject.info
-                    print "destUIObjectInfo", destUIObjectInfo
-                except Exception, e:
-                    print "异常:", e
-                    # print "destUIObject.info",destUIObjectInfo
-                try:
-                    destBounds = destUIObject.info['bounds']
-                    print "destBounds:", destBounds
-                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
-                        print '成功聚焦到目标焦点:', bounds
-                        return True
-                    else:
-                        count = count + 1
-                        direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
-                        self.directionManageAndroid.goOneStep(self.u, direction, keyType)
-                except Exception, e:
-                    # 出现控件出现一半的时候,获取控件信息会报错
-                    if count < Max_Try:
-                        count = count + 1
-                        self.pressKeyByType(findDirection, keyType)
-                    else:
-                        Reversecount = Reversecount + 1
-                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-                    # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
-                else:
-                    if count < Max_Try:
-                        count = count + 1
-                        self.pressKeyByType(findDirection, keyType)
-                    else:
-                        Reversecount = Reversecount + 1
-                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-
-                print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
-                return False
-
-
-
-    # zhouyi
-    def toDestTargetByBounds_for_RecyclerView(self, bounds, chooseType, Max_Try=20,
-                                              keyType=UATTree.Key_Event, findFocusCount=0, hb_keyDict={}):
-        count = 0
-        while (True):
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            # targetUIObject = self.u.getUiObject(bounds=bounds)
-            # print "targetUIObject",targetUIObject
-            # print "zhouyibounds",bounds
-            # print "targetUIObject.info:", targetUIObject.info
-            # destBounds = targetUIObject.info['bounds']
-            destBounds = bounds
-            print "目标坐标:", destBounds
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            try:
-                if chooseType.lower() == "focus":
-                    choosingUIObject = self.u.getFocusedUIObject()
-                    objBounds = choosingUIObject.info['bounds']
-                elif chooseType.lower() == "select":
-                    choosingUIObject = self.u.getSelectedUIObject()
-                    objBounds = choosingUIObject.info['bounds']
-                print "当前坐标:", objBounds
-            except Exception, e:
-                print "获取焦点失败!Error:", e
-                if findFocusCount < 3:
-                    self.u.pressKeyTimes("down")
-                    return self.toDestTargetByBounds_for_RecyclerView(bounds, chooseType,
-                                                                      findFocusCount=findFocusCount + 1)
-                else:
-                    print "尝试%s次操作!!!仍未能找到聚焦点!!!无法聚焦到目标" % findFocusCount
-                    return False
-            if self.directionManageAndroid.isHasAnotherBounds(objBounds, destBounds):
-                print "已聚焦至目标组件!!!"
-                return True
-            else:
-                if count >= Max_Try:
-                    print "已尝试至最大次数%s次,仍未能聚焦至目标组件!!!" % Max_Try
-                    return False
-                direction = self.directionManageAndroid.getTargetDirection(objBounds, destBounds)
-                print "目标方位:", direction
-                self.directionManageAndroid.goOneStep(self.u, direction, keyType)
-                count += 1
-
-    ''' 
-                 作用:在界面中 中 focused控件中既没有包含content-des控件属性,也没有text控件属性,
-                       采用单张图片模板匹配  用于聚焦目标焦点
-                 参数:
-                 singleImagePath:目标聚焦的单张小图片的地址
-                 findDirection: 目标文字控件被隐藏时寻找的方向
-                 Max_Try:最多查找次数
-                 注意:
-                 例子:
-                 小米内销APP(既没有文字text属性也没有描述属性content-des)  聚焦到ATX APP
-                 singleImagePath = "D:\\ATX_APP.jpg"
-                 focusManageAndroid.toDestFocusBySingleImage_for_RecyclerView(singleImagePath)
-              '''
-
-    # 在RecyclerView 中 focused控件包含文字控件
-    def toDestFocusBySingleImage_for_RecyclerView(self, singleImagePath, findDirection="down",
-                                                  Max_Try=10 ,keyType = UATTree.Key_Event):
-        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
-        count = 0
-        Max_Try = Max_Try
-        Reversecount = 0
-        Reverse_Max_Try = Max_Try * 2
-        while (True):
-            print "count:", count
-            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
-                break
-            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
-            time.sleep(0.5)
-            focusedUIObject = self.u.getFocusedUIObject()
-            focusedBounds = focusedUIObject.info['bounds']
-
-            srceenImgPath = os.path.join(sat_environment.getSATTmpDIR(), "SingleImage_runpath.png")
-            self.ccard.takePicture(srceenImgPath)
-            resultDict = self.featureDetect.matchSingleImage(srceenImgPath, [0, 0, 1920, 1080], singleImagePath)
-
-            # destUIObject = self.u.getUiObject(description=description, resourceId=descriptionResourceId)
-            if resultDict:
-                try:
-                    destBounds_coordinate = resultDict['coordinate']
-                    # areaA = (BoundA['left'], BoundA['top'], BoundA['right'], BoundA['bottom'])
-                    destBounds = {"left": destBounds_coordinate[0], "top": destBounds_coordinate[1],
-                                  "right": destBounds_coordinate[2], "bottom": destBounds_coordinate[3]}
-                    # print "destBounds:", destBounds
-                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
-                        print '成功聚焦到目标焦点:', singleImagePath
-                        return True
-                    else:
-                        count = count + 1
-                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
-                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
-                except Exception, e:
-                    # 出现控件出现一半的时候,获取控件信息会报错
-                    if count < Max_Try:
-                        count = count + 1
-                        self.pressKeyByType(findDirection, keyType)
-                    else:
-                        Reversecount = Reversecount + 1
-                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
-            else:
-                if count < Max_Try:
-                    count = count + 1
-                    self.pressKeyByType(findDirection, keyType)
-                else:
-                    Reversecount = Reversecount + 1
-                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-
-        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
-        return False
-
-    ''' 
-                 作用:在界面中 中 focused控件中既没有包含content-des控件属性,也没有text控件属性,
-                       采用OCR识别文字  用于聚焦目标焦点
-                 参数:
-
-                 text:需要OCR识别的文字
-                 textResourceId:文字区域的resourceId
-                 recyclerViewResourceId:列表recyclerView的resourceId
-                 ocrTextBorder:为OCR识别更准确,文字识别坐标需要扩充的边界的值(默认为10)
-                 OCRDict:OCR识别文字的属性 默认为百度中英文识别
-                 findDirection: 目标文字控件被隐藏时寻找的方向
-                 Max_Try:最多查找次数
-                 注意:
-                 例子:
-
-              '''
-
-    # 在RecyclerView 中 focused控件包含文字控件
-    def toDestFocusByOCRText_for_RecyclerView(self, text, textResourceId, recyclerViewResourceId, ocrTextBorder=10,
-                                              OCRDict={"OCR_lan": "CHN_ENG", "OCR_type": 10000},
-                                              findDirection="down",
-                                              Max_Try=10,
-                                              keyType = UATTree.Key_Event):
-        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
-        count = 0
-        Max_Try = Max_Try
-        Reversecount = 0
-        Reverse_Max_Try = Max_Try * 2
-        while (True):
-            print "count:", count
-            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
-                break
-            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
-            time.sleep(0.5)
-            focusedUIObject = self.u.getFocusedUIObject()
-            focusedBounds = focusedUIObject.info['bounds']
-            # 寻找目标焦点坐标
-            childbound = None
-            recyclerViewUIObject = self.u.getUiObject(resourceId=recyclerViewResourceId)
-            for i in range(recyclerViewUIObject.info["childCount"]):
-                childUIObject = recyclerViewUIObject.child_by_instance(i, resourceId=textResourceId)
-                try:
-                    childbound = childUIObject.info["bounds"]
-                    # 对扩大后的图片坐标进行边界判断
-                    childbound_Area_left = childbound["left"] - 10
-                    if childbound_Area_left < 0: childbound_Area_left = 0
-
-                    childbound_Area_top = childbound["top"] - 10
-                    if childbound_Area_top < 0: childbound_Area_top = 0
-
-                    childbound_Area_right = childbound["right"] + 10
-                    if childbound_Area_right > 1920: childbound_Area_right = 1920
-
-                    childbound_Area_bottom = childbound["bottom"] - 10
-                    if childbound_Area_bottom > 1080: childbound_Area_bottom = 1080
-
-                    childbound_Area = (childbound_Area_left, childbound_Area_top,
-                                       childbound_Area_right, childbound_Area_bottom)
-                    # 小图保存地址
-                    tmpPic = os.path.join(getSATTmpDIR(), "uiautomator_text_ocr.png")
-                    # 整个界面图片保存地址
-                    srceenImgPath = os.path.join(sat_environment.getSATTmpDIR(), "uiautomator_OCR_runpath.png")
-                    self.u.screenshot(srceenImgPath)
-                    # self.ccard.takePicture(srceenImgPath)
-                    # 切割小图片
-                    self.imgCMP.saveCropPic(srceenImgPath, tmpPic, (
-                        childbound_Area[0], childbound_Area[1], childbound_Area[2], childbound_Area[3]))
-                    # ocr 识别
-                    textValueCUR = self.ocr.getStr(tmpPic, OCRDict["OCR_lan"], OCRDict["OCR_type"])
-                    print "textValueCUR:", textValueCUR
-                    print "destText:", text
-                    if self.ocr.cmpOcrStr(textValueCUR, text):
-                        break
-                    else:
-                        childbound = None
-                except Exception:
-                    childbound = None
-                    pass
-
-            if childbound:
-                try:
-                    destBounds = childbound
-                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
-                        print '成功聚焦到目标焦点:', text
-                        return True
-                    else:
-                        count = count + 1
-                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
-                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
-                except Exception, e:
-                    # 出现控件出现一半的时候,获取控件信息会报错
-                    if count < Max_Try:
-                        count = count + 1
-                        self.pressKeyByType(findDirection, keyType)
-                    else:
-                        Reversecount = Reversecount + 1
-                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
-            else:
-                if count < Max_Try:
-                    count = count + 1
-                    self.pressKeyByType(findDirection, keyType)
-                else:
-                    Reversecount = Reversecount + 1
-                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-
-        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
-        return False
-
-    ''' 
-                 作用:在界面中 中 focused控件中既没有包含content-des控件属性,也没有text控件属性,
-                       一般这种控件是自定义控件 class类型是android.view.View
-                       采用OCR识别文字  用于聚焦目标焦点
-                 参数:
-
-                 text:需要OCR识别的文字
-                 visableViewResourceId:可以通过UIAutomator框架获取到的控件的id
-                 initTextArea: 确定的一个区域下的文字坐标
-                 initVisableViewArea:确定的一个区域下的可获取控件的坐标
-                 ocrTextBorder:为OCR识别更准确,文字识别坐标需要扩充的边界的值(默认为10)
-                 OCRDict:OCR识别文字的属性 默认为百度中英文识别
-                 findDirection: 目标文字控件被隐藏时寻找的方向
-                 Max_Try:最多查找次数
-                 注意:
-                 例子:小米内销APP测试
-                 #文字内容
-                   text = "应用商店"
-                 #图标的resourceId
-                   visableViewResourceId = "com.mitv.tvhome:id/di_img"
-                 #非聚焦的app文字坐标
-                   initTextArea = [90, 392, 229, 424]
-                #非聚焦的app图片坐标
-                   initVisableViewArea = [90, 257, 227, 382]
-
-                   focusManageAndroid.toDestFocusRelativeOCRText__for_RecyclerView(text=text,
-                                                                    visableViewResourceId=visableViewResourceId,
-                                                                    initTextArea=initTextArea,
-                                                                    initVisableViewArea=initVisableViewArea)
-
-              '''
-
-    # 在RecyclerView 中 focused控件包含文字控件
-    def toDestFocusRelativeOCRText__for_RecyclerView(self, text, visableViewResourceId, initTextArea,
-                                                     initVisableViewArea, ocrTextBorder=5,
-                                                     OCRDict={"OCR_lan": "CHN_ENG", "OCR_type": 10000},
-                                                     findDirection="down",
-                                                     Max_Try=10,
-                                                     keyType = UATTree.Key_Event):
-        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
-        count = 0
-        Max_Try = Max_Try
-        Reversecount = 0
-        Reverse_Max_Try = Max_Try * 2
-
-        initTextBounds = {"left": initTextArea[0], "top": initTextArea[1], "right": initTextArea[2],
-                          "bottom": initTextArea[3]}
-        initvisableViewBounds = {"left": initVisableViewArea[0], "top": initVisableViewArea[1],
-                                 "right": initVisableViewArea[2], "bottom": initVisableViewArea[3]}
-        while (True):
-            print "count:", count
-            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
-                break
-            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
-            time.sleep(0.5)
-            focusedUIObject = self.u.getFocusedUIObject()
-            focusedBounds = focusedUIObject.info['bounds']
-            # 寻找目标焦点坐标
-            childbound = None
-            childCountUIObject = self.u.getUiObject(resourceId=visableViewResourceId)
-            print "childCountUIObject.info", childCountUIObject.info
-            childCount = childCountUIObject.count
-            print "列表包含的子控件数量:", childCount
-            for i in range(childCount):
-                print "第%s次查找目标bounds" % (str(i))
-                visableViewObject = self.u.getUiObject(instance=i, resourceId=visableViewResourceId)
-                try:
-                    visableViewBound = visableViewObject.info["bounds"]
-                    print "initvisableViewBounds:", initvisableViewBounds
-                    print "initTextBounds:", initTextBounds
-                    print "visableViewBound:", visableViewBound
-                    childbound = self.directionManageAndroid.getRelativeBounds(initvisableViewBounds, initTextBounds,
-                                                                               visableViewBound)
-                    print "childbound:", childbound
-                    # return False
-                    # childbound = childUIObject.info["bounds"]
-                    # 对扩大后的图片坐标进行边界判断
-                    childbound_Area_left = childbound["left"] - 10
-                    if childbound_Area_left < 0: childbound_Area_left = 0
-
-                    childbound_Area_top = childbound["top"] - 10
-                    if childbound_Area_top < 0: childbound_Area_top = 0
-
-                    childbound_Area_right = childbound["right"] + 10
-                    if childbound_Area_right > 1920: childbound_Area_right = 1920
-
-                    childbound_Area_bottom = childbound["bottom"] + 10
-                    if childbound_Area_bottom > 1080: childbound_Area_bottom = 1080
-
-                    childbound_Area = [childbound_Area_left, childbound_Area_top,
-                                       childbound_Area_right, childbound_Area_bottom]
-                    print "childbound_Area:", childbound_Area
-                    # 小图保存地址
-                    tmpPic = os.path.join(getSATTmpDIR(), "uiautomator_text_ocr.png")
-                    # 整个界面图片保存地址
-                    srceenImgPath = os.path.join(sat_environment.getSATTmpDIR(), "uiautomator_OCR_runpath.png")
-                    self.u.screenshot(srceenImgPath)
-                    # self.ccard.takePicture(srceenImgPath)
-                    # 切割小图片
-                    self.imgCMP.saveCropPic(srceenImgPath, tmpPic, (
-                        childbound_Area[0], childbound_Area[1], childbound_Area[2], childbound_Area[3]))
-                    # ocr 识别
-                    textValueCUR = self.ocr.getStr(tmpPic, OCRDict["OCR_lan"], OCRDict["OCR_type"])
-                    print "textValueCUR:", textValueCUR
-                    print "destText:", text
-                    if self.ocr.cmpOcrStr(textValueCUR, text):
-                        break
-                    else:
-                        childbound = None
-                except Exception, e:
-                    print e
-                    childbound = None
-                    pass
-            print "childbound:", childbound
-            # return False
-            if childbound:
-                try:
-                    destBounds = childbound
-                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
-                        print '成功聚焦到目标焦点:', text
-                        return True
-                    else:
-                        count = count + 1
-                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
-                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
-                except Exception, e:
-                    # 出现控件出现一半的时候,获取控件信息会报错
-                    if count < Max_Try:
-                        count = count + 1
-                        self.pressKeyByType(findDirection, keyType)
-                    else:
-                        Reversecount = Reversecount + 1
-                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
-            else:
-                if count < Max_Try:
-                    count = count + 1
-                    self.pressKeyByType(findDirection, keyType)
-                else:
-                    Reversecount = Reversecount + 1
-                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-
-        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
-        return False
-
-    ''' 
-            作用:在RecyclerView 中 focused控件和文字控件同级 相对位置固定  用于聚焦目标焦点
-            参数:
-            text:目标文字的文字内容
-            textResourceId:目标文字的resourceId
-            initFocusedArea:聚焦情况下的focused控件的坐标区域(x1,y1,x2,y2)
-            initDestTextArea:聚焦情况下的text控件的坐标区域(x1,y1,x2,y2)
-            findDirection: 目标文字控件被隐藏时寻找的方向
-            Max_Try:最多查找次数
-            注意:
-
-            例子:  
-            小米信源设置 的声音自定义界面 Device /Settings Sound mode /Custom/
-
-             initFocusedArea = (822, 180, 1259, 221)
-             initDestTextArea = (822, 116, 1259, 180)
-
-            focusManageAndroid.toDestFocusRelativeText_for_RecyclerView("3500Hz: 0dB", "com.android.tv:id/title",
-                                                                initFocusedArea, initDestTextArea)
-         '''
-
-    # 在RecyclerView 中 focused控件和文字控件同级
-    def toDestFocusRelativeText_for_RecyclerView(self, text, textResourceId, initFocusedArea, initDestTextArea,
-                                                 findDirection="down", Max_Try=10, keyType = UATTree.Key_Event):
-        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
-        count = 0
-        Max_Try = Max_Try
-        Reversecount = 0
-        Reverse_Max_Try = Max_Try * 2
-        while (True):
-            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
-                break
-            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
-            time.sleep(0.5)
-            focusedUIObject = self.u.getFocusedUIObject()
-            focusedBounds = focusedUIObject.info['bounds']
-            # print "focusedBounds:",focusedBounds
-
-            destUIObject = self.u.getUiObject(text=text, resourceId=textResourceId)
-            if destUIObject:
-                try:
-                    destBounds = destUIObject.info['bounds']
-                    # print "destBounds:", destBounds
-                    if self.directionManageAndroid.isRelativePositionBounds(initFocusedArea, initDestTextArea,
-                                                                            focusedBounds,
-                                                                            destBounds):
-                        print '成功聚焦到目标焦点:', text
-                        return True
-                    else:
-                        count = count + 1
-                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
-                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
-                except Exception:
-                    # 出现控件出现一半的时候,获取控件信息会报错
-                    if count < Max_Try:
-                        count = count + 1
-                        self.pressKeyByType(findDirection, keyType)
-                    else:
-                        Reversecount = Reversecount + 1
-                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
-            else:
-                if count < Max_Try:
-                    count = count + 1
-                    self.pressKeyByType(findDirection, keyType)
-                else:
-                    Reversecount = Reversecount + 1
-                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-
-        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
-        return False
-
-    ''' 
-            作用:聚焦效果View控件 包含 目标文本控件  用于聚焦目标焦点
-            参数:
-            text:目标文字的文字内容
-            textResourceId:目标文字的resourceId
-            FocusViewResourceId:聚焦效果View的resourceId
-            findDirection: 目标文字控件被隐藏时寻找的方向
-            Max_Try:最多查找次数
-            注意:
-            例子:
-            小米USB界面上方Tab聚焦方法
-            focusManageAndroid.toDestFocusByText_for_FocusView("Devices", "com.xiaomi.mitv.mediaexplorer:id/dev",
-                                                           "com.xiaomi.mitv.mediaexplorer:id/tab_cursor",
-                                                           findDirection="right",
-                                                           Max_Try=5)
-         '''
-
-    # 聚焦效果View控件 包含 目标文本控件
-    def toDestFocusByText_for_FocusView(self, text, FocusViewResourceId, findDirection="down",
-                                        Max_Try=10, keyType = UATTree.Key_Event, hb_keyDict={}):
-        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
-        count = 0
-        Max_Try = Max_Try
-        Reversecount = 0
-        Reverse_Max_Try = Max_Try * 2
-        while (True):
-            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
-                break
-            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
-            time.sleep(0.5)
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            focusedUIObject = self.u.getUiObject(resourceId=FocusViewResourceId)
-            focusedBounds = focusedUIObject.info['bounds']
-            print "focusedBounds:", focusedBounds
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            destUIObject = self.u.getUiObject(text=text)
-            if destUIObject:
-                try:
-                    destBounds = destUIObject.info['bounds']
-                    print "destBounds:", destBounds
-                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
-                        print '成功聚焦到目标焦点:', text
-                        return True
-                    else:
-                        count = count + 1
-                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
-                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
-                except Exception:
-                    # 出现控件出现一半的时候,获取控件信息会报错
-                    if count < Max_Try:
-                        count = count + 1
-                        self.pressKeyByType(findDirection, keyType)
-                    else:
-                        Reversecount = Reversecount + 1
-                        self.pressKeyByType(self.ReverseDirctionDict[findDirection] ,keyType)
-            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
-            else:
-                if count < Max_Try:
-                    count = count + 1
-                    self.pressKeyByType(findDirection, keyType)
-                else:
-                    Reversecount = Reversecount + 1
-                    self.pressKeyByType(self.ReverseDirctionDict[findDirection] ,keyType)
-
-        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
-        return False
-
-    '''
-                   作用:RecyclerView 中焦点没有focused属性  只是子项中的View有放大效果
-                   参数:
-                   text:目标文字的文字内容
-                   textResourceId:目标文字的resourceId
-                   zoomViewResourceId:f放大效果View的resourceId
-                   recyclerViewResourceId:列表recyclerView 的resourceId
-                   findDirection: 目标文字控件被隐藏时寻找的方向
-                   Max_Try:最多查找次数
-                   注意:
-                   例子:
-                   小米USB Images 浏览界面
-                   focusManageAndroid.toDestZoomByText_for_RecyclerView("Pattern", "com.xiaomi.mitv.mediaexplorer:id/album_item_name",
-                                                              "com.xiaomi.mitv.mediaexplorer:id/album_item_bg",
-                                                              "com.xiaomi.mitv.mediaexplorer:id/all_albums_view")
-                '''
-
-    # RecyclerView 中焦点没有focused属性  只是子项中的View有放大效果
-    def toDestZoomByText_for_RecyclerView(self, text, textResourceId, zoomViewResourceId, recyclerViewResourceId,
-                                          findDirection="down",
-                                          Max_Try=20,
-                                          keyType=UATTree.Key_Event):
-        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
-        count = 0
-        Max_Try = Max_Try
-        Reversecount = 0
-        Reverse_Max_Try = Max_Try * 2
-
-        while (True):
-            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
-                break
-            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
-            time.sleep(0.5)
-            # focusedUIObject = self.u.getUiObject(resourceId=FocusViewResourceId)
-            # focusedBounds = focusedUIObject.info['bounds']
-            # print "focusedBounds:", focusedBounds
-            destUIObject = self.u.getUiObject(text=text, resourceId=textResourceId)
-            if destUIObject:
-                try:
-                    # 获取目标文本的bounds
-                    destBounds = destUIObject.info['bounds']
-                    # print "destBounds:", destBounds
-
-                    zoomUiObject = self.u.getUiObject(resourceId=recyclerViewResourceId)
-                    # print "uiObject.info: ", uiObject.info
-                    childCount = zoomUiObject.info['childCount']
-
-                    zoomBound = {u'top': 0, u'left': 0, u'right': 0, u'bottom': 0}
-                    zoomIndex = -1
-
-                    for i in range(childCount):
-                        childObject = zoomUiObject.child_by_instance(i, resourceId=zoomViewResourceId)
-                        # print "childObject.info:", childObject.info
-                        childounds = childObject.info['bounds']
-                        # print "child:", str(i), "bounds:", childounds
-                        if self.directionManageAndroid.isBiggerBound(childounds, zoomBound):
-                            zoomBound = childounds
-                            zoomIndex = i
-                            zoomTextBound = zoomUiObject.child_by_instance(i, resourceId=textResourceId).info[
-                                "bounds"]
-                            zoomText = zoomUiObject.child_by_instance(i, resourceId=textResourceId).info["text"]
-
-                    # print zoomBound, zoomIndex, zoomTextBound, zoomText
-                    print "toDestZoomByText_for_RecyclerView 当前聚焦文字:", zoomText
-                    if str(zoomText) == str(text):
-                        print '成功聚焦到目标焦点:', text
-                        return True
-                    # if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
-                    #     print '成功聚焦到目标焦点:', text
-                    #     return True
-                    else:
-                        count = count + 1
-                    direction = self.directionManageAndroid.getTargetDirection(zoomTextBound, destBounds)
-                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
-                except Exception:
-                    # 出现控件出现一半的时候,获取控件信息会报错
-                    if count < Max_Try:
-                        count = count + 1
-                        self.pressKeyByType(findDirection, keyType)
-                    else:
-                        Reversecount = Reversecount + 1
-                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
-            else:
-                if count < Max_Try:
-                    count = count + 1
-                    self.pressKeyByType(findDirection, keyType)
-                else:
-                    Reversecount = Reversecount + 1
-                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-
-        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
-        return False
-
-    ''' 
-                   作用:获取RecyclerView 中所有文字名称的列表
-                   参数:
-
-                   recyclerViewResourceId:列表recyclerView 的resourceId
-                   textResourceId:item中文本区域的的resourceId
-                   findDirection: 目标文字控件被隐藏时寻找的方向
-                   Max_Try:最多查看次数,执行Max_Try 次findDirection 后 仍无新控件被刷出 则返回所有文件的列表
-                   name_list:最终返回的列表
-                   注意:
-                   例子:
-                   小米USB Music 浏览界面 获取所有的音乐文件
-                    recyclerViewResourceId = "com.xiaomi.mitv.mediaexplorer:id/all_music_view"
-                    textResourceId = "com.xiaomi.mitv.mediaexplorer:id/item_title"
-                    recyclerViewItemNameList = focusManageAndroid.getRecyclerViewItemNameList(recyclerViewResourceId,textResourceId)
-                    print "recyclerViewItemNameList:", recyclerViewItemNameList
-
-                '''
-
-    def getRecyclerViewItemNameList(self, recyclerViewResourceId, textResourceId, findDirection="down",
-                                    Max_Try=10, name_list=[], keyType = UATTree.Key_Event):
-        count = 0
-        isNOTChange = False
-        old_name_list = name_list
-
-        while (True):
-            if count > Max_Try and isNOTChange:
-                return name_list
-                break
-            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
-            time.sleep(0.5)
-            recyclerViewUIObject = self.u.getUiObject(resourceId=recyclerViewResourceId)
-            for i in range(recyclerViewUIObject.info["childCount"]):
-                childUIObject = recyclerViewUIObject.child_by_instance(i, resourceId=textResourceId)
-                try:
-                    childText = childUIObject.info["text"]
-                    if childText not in name_list:
-                        name_list.append(childUIObject.info["text"])
-                except Exception:
-                    pass
-            if old_name_list == name_list:
-                isNOTChange = True
-                count = count + 1
-                self.pressKeyByType(findDirection, keyType)
-
-            else:
-                isNOTChange = False
-                self.pressKeyByType(findDirection, keyType)
-                self.getRecyclerViewItemNameList(recyclerViewResourceId, textResourceId, findDirection,
-                                                 Max_Try, name_list)
-
-    '''
-                      作用:获取RecyclerView 中所有文字名称的列表
-                      参数:
-                      keyText:寻找目标Text的key的Text
-                      keyResourceId:寻找目标Text的key的resourceId
-                      recyclerViewResourceId:列表recyclerView 的resourceId
-                      textResourceId:目标text的中文本区域的的resourceId
-                      findDirection: 目标文字控件被隐藏时寻找的方向
-                      Max_Try:最多查看次数,执行Max_Try 次findDirection 后 仍无新控件被刷出 则返回""
-                      注意:
-                      例子:
-                      小米USB Music 音乐播放界面 通过音乐名称获取音乐时长
-                       keyResourceId = "com.xiaomi.mimusic2:id/item_textview"
-                       recyclerViewResourceId = "com.xiaomi.mimusic2:id/music_list_view"
-                       textResourceId = "com.xiaomi.mimusic2:id/music_list_item_duration"
-                       findText = focusManageAndroid.getRecyclerViewItemTextByKeyText("鸭子", keyResourceId,
-                                                                                   recyclerViewResourceId,
-                                                                                   textResourceId)
-                       print "findText:", findText
-
-                   '''
-
-    def getRecyclerViewItemTextByKeyText(self, keyText, keyResourceId, recyclerViewResourceId, textResourceId,
-                                         findDirection="down",
-                                         Max_Try=20,
-                                         keyType = UATTree.Key_Event):
-        count = 0
-        Reversecount = 0
-        Reverse_Max_Try = Max_Try * 2
-
-        while (True):
-            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
-                break
-            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
-            time.sleep(0.5)
-            recyclerViewUIObject = self.u.getUiObject(resourceId=recyclerViewResourceId)
-            iCounnt = -1
-            findText = ""
-            print "recyclerViewUIObject.info:", recyclerViewUIObject.info
-            for i in range(recyclerViewUIObject.info["childCount"]):
-                print "i:", i
-                try:
-                    childUIObject = recyclerViewUIObject.child_by_instance(i, resourceId=keyResourceId)
-                    if str(childUIObject.info["text"]) == str(keyText):
-                        iCounnt = i
-                        findUIObject = recyclerViewUIObject.child_by_instance(i, resourceId=textResourceId)
-                        findText = findUIObject.info["text"]
-                        return str(findText)
-                except Exception:
-                    iCounnt = -1
-                    pass
-            if iCounnt == -1:
-                if count < Max_Try:
-                    count = count + 1
-                    self.pressKeyByType(findDirection, keyType)
-                else:
-                    Reversecount = Reversecount + 1
-                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
-        print "执行%s 次查找,仍未找到keyText:%s!!" % (str(Max_Try), str(keyText))
-        return ""
-
-
-    def focusFirstItemFromRecyclerView(self, recyclerView_resId, child_class, Max_Try=20, keyType = UATTree.Key_Event):
-        return self.focusItemByIndexFromRecyclerView(recyclerView_resId, child_class, 0, Max_Try, keyType)
-
-    def focusItemByIndexFromRecyclerView(self, recyclerView_resId, child_class, target_index, Max_Try=20,
-                                         keyType = UATTree.Key_Event, hb_keyDict={}):
-        count = 0
-        recyclerView = self.u.getUiObject(resourceId=recyclerView_resId)
-        while(True):
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            firstItemUIObject = recyclerView.child_by_instance(target_index, className = child_class)
-            print "firstItemUIObject.info:", firstItemUIObject.info
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            destBounds = firstItemUIObject.info['bounds']
-            print "目标坐标:", destBounds
-            focusedUIObject = self.u.getFocusedUIObject()
-            focusedBounds = focusedUIObject.info['bounds']
-            print "当前坐标:",focusedBounds
-            if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
-                print "已将焦点归位至第一位!!!"
-                return True
-            else:
-                if count >= Max_Try:
-                    print "已尝试至最大次数%s次,仍未能使焦点归位至第一位!!!"%Max_Try
-                    return False
-                direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
-                print "目标方位:",direction
-                self.directionManageAndroid.goOneStep(self.u, direction, keyType)
-                count += 1
-
-    def focusTargetSeekBar(self, target_resId, chooseType, Max_Try=20, keyType = UATTree.Key_Event, findFocusCount = 0):
-        count = 0
-        while(True):
-            targetUIObject = self.u.getUiObject(resourceId=target_resId)
-            print "targetUIObject.info:", targetUIObject.info
-            destBounds = targetUIObject.info['bounds']
-            print "目标坐标:", destBounds
-            try:
-                if chooseType.lower() == "focus":
-                    choosingUIObject = self.u.getFocusedUIObject()
-                    objBounds = choosingUIObject.info['bounds']
-                elif chooseType.lower() == "select":
-                    choosingUIObject = self.u.getSelectedUIObject()
-                    objBounds = choosingUIObject.info['bounds']
-                print "当前坐标:",objBounds
-            except Exception,e:
-                print "获取焦点失败!Error:",e
-                if findFocusCount < 3:
-                    self.u.pressKeyTimes("down")
-                    return self.focusTargetSeekBar(target_resId, chooseType, findFocusCount = findFocusCount + 1)
-                else:
-                    print "尝试%s次操作!!!仍未能找到聚焦点!!!无法聚焦到目标"%findFocusCount
-                    return False
-            if self.directionManageAndroid.isHasAnotherBounds(objBounds, destBounds):
-                print "已聚焦至目标seekbar!!!"
-                return True
-            else:
-                if count >= Max_Try:
-                    print "已尝试至最大次数%s次,仍未能聚焦至目标seekbar!!!"%Max_Try
-                    return False
-                direction = self.directionManageAndroid.getTargetDirection(objBounds, destBounds)
-                print "目标方位:",direction
-                self.directionManageAndroid.goOneStep(self.u, direction, keyType)
-                count += 1
-
-    def toDestTargetByResourceId_for_RecyclerView(self, resourceId, chooseType, Max_Try=20,
-                                                  keyType = UATTree.Key_Event, findFocusCount = 0, hb_keyDict={}):
-        count = 0
-        while(True):
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            targetUIObject = self.u.getUiObject(resourceId=resourceId)
-            print "targetUIObject.info:", targetUIObject.info
-            destBounds = targetUIObject.info['bounds']
-            print "目标坐标:", destBounds
-            if hb_keyDict != {}:
-                # 执行心跳按键
-                self.executeHeartBeatKey(hb_keyDict)
-            try:
-                if chooseType.lower() == "focus":
-                    choosingUIObject = self.u.getFocusedUIObject()
-                    objBounds = choosingUIObject.info['bounds']
-                elif chooseType.lower() == "select":
-                    choosingUIObject = self.u.getSelectedUIObject()
-                    objBounds = choosingUIObject.info['bounds']
-                print "当前坐标:",objBounds
-            except Exception,e:
-                print "获取焦点失败!Error:",e
-                if findFocusCount < 3:
-                    self.u.pressKeyTimes("down")
-                    return self.toDestTargetByResourceId_for_RecyclerView(resourceId, chooseType, findFocusCount = findFocusCount + 1)
-                else:
-                    print "尝试%s次操作!!!仍未能找到聚焦点!!!无法聚焦到目标"%findFocusCount
-                    return False
-            if self.directionManageAndroid.isHasAnotherBounds(objBounds, destBounds):
-                print "已聚焦至目标组件!!!"
-                return True
-            else:
-                if count >= Max_Try:
-                    print "已尝试至最大次数%s次,仍未能聚焦至目标组件!!!"%Max_Try
-                    return False
-                direction = self.directionManageAndroid.getTargetDirection(objBounds, destBounds)
-                print "目标方位:",direction
-                self.directionManageAndroid.goOneStep(self.u, direction, keyType)
-                count += 1
-
-
-    def pressKeyByType(self, keyName, keyType = UATTree.Key_Event, times = 1, duration = 1.0):
-        print "pressKeyByType:",keyName,keyType
-        if keyType == UATTree.Key_Event:
-            return self.u.pressKeyTimes(keyName, times, duration)
-        elif keyType == UATTree.Key_IR:
-            return self.remote.sendKey(keyName, times, duration)
-        else:
-            return False
-
-    '''
-    执行心跳按键的函数。传入UATree里parent['others']['heartbeat_key']中的参数,会根据参数的配置执行心跳按键
-    '''
-    def executeHeartBeatKey(self, hb_keyDict):
-        try:
-            eventList = hb_keyDict["event"]
-            irList = hb_keyDict["ir"]
-            if eventList != "":
-                LoggingUtil.printLog("Executing heartbeat_key by EventKey!!!KeyList:%s" % eventList)
-                for key in eventList:
-                    self.u.pressKeyTimes(key)
-            elif irList != "":
-                LoggingUtil.printLog("Executing heartbeat_key by irKey!!!KeyList:%s" % irList)
-                for key in irList:
-                    self.remote.sendKey(key)
-        except Exception,e:
-            LoggingUtil.printLog(u"[executeHeartBeatKey]: 心跳按键配置异常,无法正常执行心跳按键。\n %s"%e)
-
-
-if __name__ == "__main__":
-    u = PyUIAutomator()
-    dm = DirectionManageAndroid()
-    fm = FocusManageAndroid(u, dm)
-    fm.toDestFocusByText_with_FocuseResourceId(text="Picture", focuseResId="com.mediatek.wwtv.tvcenter:id/menu_item_frame")
-    # directionMange = DirectionManageAndroid()
-    # focusManage = FocusManageAndroid(u, directionMange)
-    # focusObj = focusManage.u.getFocusedUIObject()
-    # print "focusObj.info:",focusObj.info
-    # childCount = focusObj.info["childCount"]
-    # for i in range(childCount):
-    #     childUI = focusObj.child_by_instance(i, className = "android.widget.TextView")
-    #     print childUI.info["text"]
-
-        # focusManage.toDestFocusByText_for_RecyclerView(text="Picture mode",textResourceId="",
-    #                                                findDirection="down")
-    # focusManage.focusTargetSeekBar("com.android.tv.settings:id/seekbar_freq_300")
-    # recyclerViewUIObject = u.getUiObject(resourceId="com.xiaomi.mitv.mediaexplorer:id/photo_list")
-    # print "recyclerViewUIObject.info", recyclerViewUIObject.info
-    # for i in range(recyclerViewUIObject.info["childCount"]):
-    #     childUI = recyclerViewUIObject.child_by_instance(i)
-    #     print "cound %s child.info:"%i, childUI.info
-
-    # focusManage.focusFirstItemFromRecyclerView(recyclerViewUIObject, child_class="android.widget.LinearLayout")
-
-    # focusedUIObject = pyui.getFocusedUIObject()
-    # print 'focusedUIObject:', focusedUIObject
-    # if focusedUIObject:
-    #     print focusedUIObject.info
+# -*- coding:utf-8 -*-
+import time
+
+from ssat_sdk.ocr_convert import OCRConvert
+
+from ssat_sdk.device_manage.capturecard_manager import CCardManager
+
+from ssat_sdk.uiautomator2.ext import htmlreport
+# from uiautomator2.ext import htmlreport
+
+from ssat_sdk import uiautomator2 as u2, LoggingUtil, inspect, FeatureDetect, sat_environment, getSATTmpDIR, ImageCMP
+
+from ssat_sdk.UATree.UAT_tree import UATTree
+from ssat_sdk.tv_operator import TvOperator
+
+
+# import uiautomator2 as u2
+
+import os
+import re
+
+from ssat_sdk.utils import LoggingUtil
+from ssat_sdk.utils.string_util import getDigitFromString
+from ssat_sdk.sat_environment import getAdbDeviceSatatus
+
+pyFileName = os.path.split(__file__)[-1]
+
+
+def get_current_function_name():
+    return inspect.stack()[1][3]
+
+
+'''
+预置条件:保证执行PC和测试电视adb连接
+各个API说明的例子中到达self.pyUIAutomator 是PyUIAutomator类的实例
+self.pyUIAutomator = PyUIAutomator()
+
+'''
+
+
+class PyUIAutomator():
+    def __init__(self, serial=None):
+        try:
+            self.u = u2.connect_usb(serial) #得到UIAutomatorServer实例对象
+            self.serial = serial
+        except Exception,e:
+            print e
+        # hrp = htmlreport.HTMLReport(self.u, 'report')
+        # try:
+        #     hrp.patch_click()
+        # except Exception, e:
+        #     print e
+        #     pass
+        # self.u.app_stop_all()
+
+    def reConnect_usb(self):
+        self.u = u2.connect_usb(self.serial)
+        # hrp = htmlreport.HTMLReport(self.u, 'report')
+        # try:
+        #     hrp.patch_click()
+        # except Exception, e:
+        #     print e
+        #     pass
+        # self.u.app_stop_all()
+    '''
+    废弃,改用sat_environment.getAdbDeviceSatatus()
+    '''
+    def checkAdbConnected(self):
+        return u2.check_adb_connect()
+
+    def listenAdbConnected(self):
+        count = 5
+        while getAdbDeviceSatatus() is False and count >0:
+            count -= 1
+            LoggingUtil.printLog("PyUIAutomator","listenAdbConnected,Count %d adb not connected!"%count)
+            # self.reConnect_usb()
+            time.sleep(1)
+
+    def killAdb(self):
+        return u2.killAdb()
+
+    def getAdbType(self):
+        return u2.check_adb_connect_type()
+
+    '''
+    作用:获取UIAutomator 连接对象  后续可以使用该对象使用原生方法
+    '''
+
+    def getPyUIAutomator(self):
+        self.listenAdbConnected()
+        return self.u
+
+    '''
+    作用:Android times次按键  key:常用按键的字符串
+    主要的键值字段:home、back、left、right、up、down、center、menu、search、enter、delete ( or del)、
+    recent (recent apps)、volume_up、volume_down、volume_mute、camera、power
+    times :按键次数         默认为1次
+    duration:按键时间间隔  默认为1.0s
+
+    '''
+
+    def pressKeyTimes(self, key, times=1, duration=1.0):
+        self.listenAdbConnected()
+        self.u.pressKeyTimes(key, times, duration)
+
+    '''
+    用uiautomator view属性键作为paramDIct的键,传入的paramDict里的参数,必须有效。
+    参考session.Selector里的__fields字典
+    '''
+    def getUiObject2(self, paramDict):
+        # print "getUiObject2,paramDict:",paramDict
+        uiObject = None
+        if paramDict.__len__>0:
+            try:
+                uiObject = self.u(**paramDict)
+            except Exception,e:
+                print "UIAutomator未启动,请确认是否为开机状态"
+                uiObject = None
+            # print uiObject,uiObject.info
+        return uiObject
+
+    '''
+    作用:获取UiObject对象
+    参数:
+    className:控件类型(例如:android.widget.TextView)
+    resourceId:控件的Id(例如:lh.oslauncher:id/tv_title)
+    text:控件上的文件(例如:'主页')
+    description:控件描述
+    instance:实例参数
+
+    注意:className、resourceId、text 、description、instance是利用
+    UIautomatoview.bat工具查看控件属性看到的
+   其中 className对应 UIautomatoview.bat中的 class
+       resourceId对应 UIautomatoview.bat中的 resource-id
+      description对应 UIautomatoview.bat中的 content-desc
+         instance对应 UIautomatoview.bat中的 index
+
+
+    例子:uiObject =self.pyUIAutomator.getUiObject(resourceId="com.google.android.tvlauncher:id/banner_image", description=u"Play 电影", className = 'android.widget.ImageView')
+
+
+    '''
+
+    def getUiObject(self, className='', resourceId='', text='', description='', bounds = '',instance=-1):
+
+        if text != "":
+            if type(text) is not type([]):
+                obj = self.getUiObjectTest(className,resourceId,text,description,bounds,instance)
+                return obj
+            if type(text) is type([]):
+                count = 0
+                for t in text:
+                    try:
+                        obj = self.getUiObjectTest(className,resourceId,t,description,bounds,instance)
+                        objInfo = obj.info
+                        print "文本%s对应的option已找到"%t
+                        return obj
+                    except Exception,e:
+                        nextText = text[count+1]
+                        print"文本%s对应的option未找到,匹配下一个文本%s"%(t,nextText)
+                    count += 1
+                print "所有文本均已匹配完毕,未找到对应option"
+                class object():
+                    exists = False
+                obj = object()
+                return obj
+        else:
+            obj = self.getUiObjectTest(className,resourceId,text,description,bounds,instance)
+            return obj
+
+
+
+
+
+    def getUiObjectTest(self, className='', resourceId='', text='', description='', bounds = '',instance=-1):
+        self.listenAdbConnected()
+        # print 'className:', className
+        # print 'resourceId:', resourceId
+        # print 'text:', text
+        # print 'description:', description
+        # print 'instance:', instance
+
+        uiObject = None
+        try:
+
+            if className == "" and resourceId == "" and text == "" and description == "" and\
+                    bounds == "" and instance == -1:
+                print "没有参数带入,找不到对象"
+                return uiObject
+
+            if className != "" and resourceId != "" and text != "" and description != "" and \
+                    bounds != "" and instance != -1:
+                uiObject = self.u(className=className, resourceId=resourceId, text=text, description=description,
+                                  bounds=bounds,instance=instance)
+            # 缺少一个元素的
+            #缺instance
+            if className != "" and resourceId != "" and text != "" and description != "" and \
+                    bounds != "" and instance == -1:
+                uiObject = self.u(className=className, resourceId=resourceId, text=text, bounds = bounds,
+                                  description=description)
+            #缺bounds
+            if className != "" and resourceId != "" and text != "" and description != "" and \
+                    bounds == "" and instance != -1:
+                uiObject = self.u(className=className, resourceId=resourceId, text=text,
+                                  description=description,instance = instance)
+            #缺description
+            if className != "" and resourceId != "" and text != "" and description == "" and\
+                    bounds != "" and instance != -1:
+                uiObject = self.u(className=className, resourceId=resourceId, text=text,
+                                  bounds=bounds,instance=instance)
+            #缺text
+            if className != "" and resourceId != "" and text == "" and description != "" and\
+                    bounds != "" and instance != -1:
+                uiObject = self.u(className=className, resourceId=resourceId, description=description,
+                                  bounds=bounds,instance=instance)
+            #缺resouceId
+            if className != "" and resourceId == "" and text != "" and description != "" and \
+                    bounds!="" and instance != -1:
+                uiObject = self.u(className=className, text=text, description=description,
+                                  bounds=bounds,instance=instance)
+            # lack of className
+            if className == "" and resourceId != "" and text != "" and description != "" and \
+                    bounds!="" and instance != -1:
+                uiObject = self.u(resourceId=resourceId, text=text, description=description,
+                                  bounds=bounds,instance=instance)
+
+            # 缺少两个元素的
+            # lack of className and resourceId
+            if className == "" and resourceId == "" and text != "" and description != "" and \
+                    bounds!="" and instance != -1:
+                uiObject = self.u(text=text, description=description,bounds=bounds,
+                                  instance=instance)
+            # lack of className and text
+            if className == "" and resourceId != "" and text == "" and description != "" and \
+                    bounds != "" and instance != -1:
+                uiObject = self.u(resourceId=resourceId, description=description, bounds=bounds,
+                                  instance=instance)
+            #lack of className and description
+            if className == "" and resourceId != "" and text != "" and description == "" and \
+                    bounds!="" and instance != -1:
+                uiObject = self.u(resourceId=resourceId, text=text,bounds=bounds,
+                                  instance=instance)
+            # lack of className and bounds
+            if className == "" and resourceId != "" and text != "" and description != "" and \
+                    bounds == "" and instance != -1:
+                uiObject = self.u(resourceId=resourceId, text=text, description=description,
+                                  instance=instance)
+            # lack of className and instance
+            if className == "" and resourceId != "" and text != "" and description != "" and \
+                    bounds != "" and instance == -1:
+                uiObject = self.u(resourceId=resourceId, text=text, description=description,
+                                  bounds=bounds)
+            # lack of resourceId and text
+            if className != "" and resourceId == "" and text == "" and description != "" and \
+                    bounds!="" and instance != -1:
+                uiObject = self.u(className=className, description=description,bounds=bounds,
+                                  instance=instance)
+            # lack of resourceId and description
+            if className != "" and resourceId == "" and text != "" and description == "" and \
+                    bounds!="" and instance != -1:
+                uiObject = self.u(className=className, text=text,bounds = bounds,
+                                  instance=instance)
+            # lack of resourceId and bounds
+            if className != "" and resourceId != "" and text == "" and description != "" and \
+                    bounds == "" and instance != -1:
+                uiObject = self.u(className=className, text=text, description=description,
+                                  instance=instance)
+            # lack of resourceId and instance
+            if className != "" and resourceId == "" and text != "" and description != "" and \
+                    bounds != "" and instance == -1:
+                uiObject = self.u(className=className, text=text, description=description,
+                                  bounds=bounds)
+            # lack of text and description
+            if className != "" and resourceId != "" and text == "" and description == "" and \
+                    bounds!="" and instance != -1:
+                uiObject = self.u(className=className, resourceId=resourceId, bounds=bounds,
+                                  instance=instance)
+            # lack of text and bounds
+            if className != "" and resourceId != "" and text == "" and description != "" and \
+                    bounds=="" and instance != -1:
+                uiObject = self.u(className=className, resourceId=resourceId,
+                                  description=description,instance=instance)
+            # lack of text and instance
+            if className != "" and resourceId != "" and text == "" and description != "" and \
+                    bounds!="" and instance == -1:
+                uiObject = self.u(className=className, resourceId=resourceId,
+                                  description=description,bounds=bounds)
+            # lack of description and bounds
+            if className != "" and resourceId != "" and text != "" and description == "" and \
+                    bounds=="" and instance != -1:
+                uiObject = self.u(className=className, resourceId=resourceId, text = text,
+                                  instance=instance)
+            # lack of description and instance
+            if className != "" and resourceId != "" and text != "" and description == "" and \
+                    bounds!="" and instance == -1:
+                uiObject = self.u(className=className, resourceId=resourceId, text = text,
+                                  bounds=bounds)
+            # lack of bounds and instance
+            if className != "" and resourceId != "" and text != "" and description != "" and \
+                    bounds=="" and instance == -1:
+                uiObject = self.u(className=className, resourceId=resourceId, text = text,
+                                  description=description)
+
+            # 缺少3个元素
+            # lack of className and resourceId and text
+            if className == "" and resourceId == "" and text == "" and description != "" and \
+                    bounds!="" and instance != -1:
+                uiObject = self.u(description=description, bounds=bounds,instance=instance)
+            # lack of className and resourceId and description
+            if className == "" and resourceId == "" and text != "" and description == "" and \
+                    bounds!="" and instance != -1:
+                uiObject = self.u(text = text,bounds=bounds,instance=instance)
+            # lack of className and resourceId and bounds
+            if className == "" and resourceId == "" and text != "" and description != "" and \
+                    bounds =="" and instance != -1:
+                uiObject = self.u(text = text,description=description,instance=instance)
+            # lack of className and resourceId and instance
+            if className == "" and resourceId == "" and text != "" and description != "" and \
+                    bounds!="" and instance == -1:
+                uiObject = self.u(text = text,description=description,bounds=bounds)
+            # lack of className and text and description
+            if className == "" and resourceId != "" and text == "" and description == "" and \
+                    bounds!="" and instance != -1:
+                uiObject = self.u(resourceId=resourceId,bounds=bounds,instance=instance)
+            # lack of className and text and bounds
+            if className == "" and resourceId != "" and text == "" and description != "" and \
+                    bounds=="" and instance != -1:
+                uiObject = self.u(resourceId=resourceId,description=description,instance=instance)
+             # lack of className and text and instance
+            if className == "" and resourceId != "" and text == "" and description != "" and \
+                    bounds!="" and instance == -1:
+                uiObject = self.u(resourceId=resourceId,description=description,bounds=bounds)
+            # lack of className and description and bounds
+            if className == "" and resourceId != "" and text != "" and description == "" and \
+                    bounds=="" and instance != -1:
+                uiObject = self.u(resourceId=resourceId, text = text,instance=instance)
+            # lack of className and description and instance
+            if className == "" and resourceId != "" and text != "" and description == "" and \
+                    bounds!="" and instance == -1:
+                uiObject = self.u(resourceId=resourceId, text = text,bounds=bounds)
+            # lack of resourceId and text and description
+            if className != "" and resourceId == "" and text == "" and description == "" and \
+                    bounds!="" and instance != -1:
+                uiObject = self.u(className=className,bounds=bounds,instance=instance)
+            # lack of resourceId and text and bounds
+            if className != "" and resourceId == "" and text == "" and description != "" and \
+                    bounds=="" and instance != -1:
+                uiObject = self.u(className=className,description=description,instance=instance)
+            # lack of resourceId and text and instance
+            if className != "" and resourceId == "" and text == "" and description != "" and \
+                    bounds!="" and instance == -1:
+                uiObject = self.u(className=className,description=description,bounds=bounds)
+            # lack of resourceId and description and bounds
+            if className != "" and resourceId == "" and text != "" and description == "" and \
+                    bounds =="" and instance != -1:
+                uiObject = self.u(className=className,text = text,instance=instance)
+            # lack of resourceId and description and instance
+            if className != "" and resourceId == "" and text != "" and description == "" and \
+                    bounds!="" and instance == -1:
+                uiObject = self.u(className=className, text = text,bounds=bounds)
+            # lack of resourceId and bounds and instance
+            if className != "" and resourceId == "" and text != "" and description != "" and \
+                    bounds =="" and instance == -1:
+                uiObject = self.u(className=className, text = text,description=description)
+            # lack of text and description and bounds
+            if className != "" and resourceId != "" and text == "" and description == "" and \
+                    bounds=="" and instance != -1:
+                uiObject = self.u(className=className, resourceId=resourceId,instance=instance)
+            # lack of text and description and instance
+            if className != "" and resourceId != "" and text == "" and description == "" and \
+                    bounds!="" and instance == -1:
+                uiObject = self.u(className=className, resourceId=resourceId,bounds=bounds)
+            # lack of description and bounds and instance
+            if className != "" and resourceId != "" and text != "" and description == "" and \
+                    bounds =="" and instance == -1:
+                uiObject = self.u(className=className, resourceId=resourceId, text = text)
+
+            # 缺少4个元素
+            # lack of className and resourceId and text and description
+            if className == "" and resourceId == "" and text == "" and description == "" and \
+                    bounds !="" and instance != -1:
+                uiObject = self.u(bounds=bounds,instance=instance)
+            # lack of className and resourceId and text and bounds
+            if className == "" and resourceId == "" and text == "" and description != "" and \
+                    bounds =="" and instance != -1:
+                uiObject = self.u(description=description,instance=instance)
+            # lack of className and resourceId and text and instance
+            if className == "" and resourceId == "" and text == "" and description != "" and \
+                    bounds !="" and instance == -1:
+                uiObject = self.u(description=description,bounds=bounds)
+            # lack of className and resourceId and description and bounds
+            if className == "" and resourceId == "" and text != "" and description == "" and \
+                    bounds =="" and instance != -1:
+                uiObject = self.u(text = text,instance=instance)
+            # lack of className and resourceId and description and instance
+            if className == "" and resourceId == "" and text != "" and description == "" and \
+                    bounds !="" and instance == -1:
+                uiObject = self.u(text = text,bounds=bounds)
+            # lack of resourceid and text and description and bounds
+            if className != "" and resourceId == "" and text == "" and description == "" and \
+                    bounds =="" and instance != -1:
+                uiObject = self.u(className=className,instance=instance)
+            # lack of resourceid and text and description and instance
+            if className != "" and resourceId == "" and text == "" and description == "" and \
+                    bounds !="" and instance == -1:
+                uiObject = self.u(className=className,bounds=bounds)
+            # lack of text and description and bounds and instance
+            if className != "" and resourceId != "" and text == "" and description == "" and \
+                    bounds =="" and instance == -1:
+                uiObject = self.u(className=className, resourceId=resourceId)
+
+            # 缺少5个元素的
+            # only className
+            if className != "" and resourceId == "" and text == "" and description == "" and \
+                    bounds =="" and instance == -1:
+                uiObject = self.u(className=className)
+            # only resourceid
+            if className == "" and resourceId != "" and text == "" and description == "" and \
+                    bounds =="" and instance == -1:
+                uiObject = self.u(resourceId=resourceId)
+            # only text
+            if className == "" and resourceId == "" and text != "" and description == "" and \
+                    bounds =="" and instance == -1:
+                uiObject = self.u(text = text)
+            # only description
+            if className == "" and resourceId == "" and text == "" and description != "" and \
+                    bounds =="" and instance == -1:
+                uiObject = self.u(description=description)
+            # only bounds
+            if className == "" and resourceId == "" and text == "" and description == "" and \
+                    bounds !="" and instance == -1:
+                uiObject = self.u(bounds=bounds)
+            # only instance
+            if className == "" and resourceId == "" and text == "" and description == "" and \
+                    bounds =="" and instance == -1:
+                uiObject = self.u(instance=instance)
+            return uiObject
+        except Exception, e:
+            print "异常:",e
+            return uiObject
+
+    ''' 
+    作用:判断当前界面中指定控件是否存在
+    参数:
+    className:控件类型(例如:android.widget.TextView)
+    resourceId:控件的Id(例如:lh.oslauncher:id/tv_title)
+    text:控件上的文件(例如:'主页')
+    description:控件描述
+    instance:实例参数
+
+    注意:className、resourceId、text 是利用WEditor工具查看控件属性看到的
+
+    例子:isExists = self.pyUIAutomator.isElementExist("android.widget.TextView",
+                                                     resourceId="com.apps.weather:id/today_tv_city",
+                                                     text='鞍山')
+    '''
+
+    def isElementExist(self, className='', resourceId='', text='', description='', instance=-1):
+        self.listenAdbConnected()
+        try:
+            uiObject = self.getUiObject(className, resourceId, text, description, instance)
+            print 'uiObject:', uiObject
+            if uiObject:
+                isExists = uiObject.exists()
+                print 'isExists:', isExists
+            else:
+                isExists = False
+        except Exception, e:
+            print e
+            isExists = False
+        return isExists
+
+    ''' 
+    作用:获取指定控件的文字属性
+    参数:
+    className:控件类型(例如:android.widget.TextView)
+    resourceId:控件的Id(例如:lh.oslauncher:id/tv_title)
+    注意:className、resourceId 是利用WEditor工具查看控件属性看到的
+    例子: str_play_time = self.pyUIAutomator.getTextById("android.widget.TextView",
+                                                       resourceId="com.apps.usbmediaplayer:id/playingTime")
+    '''
+
+    def getTextById(self, className, resourceId):
+        self.listenAdbConnected()
+        try:
+            text = self.u(className=className, resourceId=resourceId).get_text()
+        except Exception, e:
+            print e
+            text = ''
+        return text
+
+    '''
+    作用:执行adb命令
+    参数:adbCmd:要被执行的adb命令
+    注意:adbCmd不需要输入adb shell
+    例子:self.pyUIAutomator.adb_shell('am start -n com.apps.factory.ui/.designmenu.DesignMenuActivity')
+    '''
+
+    def adb_shell(self, adbCmd):
+        self.listenAdbConnected()
+        try:
+            self.u.adb_shell(adbCmd)
+        except Exception, e:
+            print e
+
+    ''' 
+    作用:设置android.widget.EditText 的文字内容
+    参数:text 要输入到EditText的文字内容
+    注意:保证焦点在该EditText,处于可以输入的状态
+    例子:self.pyUIAutomator.send_text(u'鞍山')
+    '''
+
+    def send_text(self, text):
+        self.listenAdbConnected()
+        try:
+            self.u.set_fastinput_ime(True)  # 切换成FastInputIME输入法
+            self.u.clear_text()
+            self.u.send_keys(unicode(text))  # adb广播输入
+            self.u.set_fastinput_ime(False)  # 切换成正常的输入法
+        except Exception, e:
+            print e
+
+    ''' 
+       作用:向电视机发送action动作
+       参数:actin 要执行的动作
+       注意:焦点在搜索界面输入框中,在搜索界面输入完搜索内容,执行该搜索动作
+       例子:小米谷歌商店搜索应用  搜索 keep应用
+           self.pyUIAutomator.send_text("kee")
+           self.pyUIAutomator.send_action("search")
+       '''
+
+    def send_action(self, action):
+        self.listenAdbConnected()
+        try:
+            self.u.set_fastinput_ime(True)  # 切换成FastInputIME输入法
+            self.u.send_action(action)
+            self.u.set_fastinput_ime(False)  # 切换成正常的输入法
+        except Exception, e:
+            print e
+
+    ''' 
+     作用:保存界面截图到指定地址
+     参数:pngPath 要保存图片的全路径名称
+     注意:保证该图片路径所在文件夹存在
+     例子:self.pyUIAutomator.screenshot(
+            r'E:\svn\SAT\SAT_Runner\btc_runner_se\runner\smoking\report\imgs\testUSB_Desk_Focused.png')
+     '''
+
+    def screenshot(self, pngPath):
+        self.listenAdbConnected()
+        try:
+            self.u.screenshot(pngPath)
+        except Exception, e:
+            print e
+
+    ''' 
+       作用:在Android 一个土司提示框  方便在电视机中看到跑到哪里
+       参数:
+       toastText:土司的内容
+       toastTime:土司存留时间
+       注意:
+       例子:self.pyUIAutomator.makeToast('测试开始', 3)
+       '''
+
+    def makeToast(self, toastText, toastTime=1):
+        self.listenAdbConnected()
+        try:
+            self.u.make_toast(toastText, toastTime)
+        except Exception, e:
+            print e
+
+    ''' 
+        作用:启动指定包名的应用的指定Activity
+        参数:
+        pkg_name:指定的包名
+        activity:指定的activity名
+        注意:
+        例子:self.pyUIAutomator.startApp('com.apps.usbmediaplayer')
+              self.pyUIAutomator.startApp('com.apps.usbmediaplayer','com.apps.usbmediaplayer.activity.MainActivity')
+        '''
+
+    def startApp(self, pkg_name, activity=None):
+        self.listenAdbConnected()
+        try:
+            if activity:
+                self.u.app_start(pkg_name, activity=activity)
+            else:
+                self.u.app_start(pkg_name)
+        except Exception, e:
+            print e
+
+    ''' 
+        作用:关闭某一指定包名的APP
+        参数:
+        pkg_name:指定的包名
+        注意:
+        例子:self.pyUIAutomator.stopApp('com.apps.usbmediaplayer')
+     '''
+
+    def stopApp(self, pkg_name):
+        self.listenAdbConnected()
+        try:
+            self.u.app_stop(pkg_name)
+        except Exception, e:
+            print e
+
+    ''' 
+        作用:关闭Android系统中所有的运行程序
+        参数:
+        注意:
+        例子:self.pyUIAutomator.stopAllApps()
+     '''
+
+    def stopAllApps(self):
+        self.listenAdbConnected()
+        try:
+            self.u.app_stop_all()
+        except Exception, e:
+            print e
+
+    ''' 
+        作用:停止uiautomator守护程序,允许其他测试框架如 appium 运行
+        参数:
+        注意:
+        例子:self.pyUIAutomator.stopUIAutomatorService()
+     '''
+
+    def stopUIAutomatorService(self):
+        self.listenAdbConnected()
+        try:
+            self.u.service("uiautomator").stop()
+        except Exception, e:
+            print e
+
+    '''
+      作用:根据传入的chooseType,获取当前界面聚焦的对象
+      参数:
+      注意:
+      例子:self.pyUIAutomator.getChoosedUIObject("focus")
+    '''
+    def getChoosedUIObject(self, chooseType, resourceId='', text=''):
+        if chooseType.lower() == "focus":
+            return self.getFocusedUIObject(resourceId=resourceId, text=text)
+        elif chooseType.lower() == "select":
+            return self.getSelectedUIObject(resourceId=resourceId, text=text)
+        else:
+            return None
+
+    ''' 
+          作用:获取当前界面的focused的UIObject对象
+          参数:
+          注意:
+          例子:self.pyUIAutomator.getFocusedUIObject()
+       '''
+
+    def getFocusedUIObject(self, resourceId='', text=''):
+        self.listenAdbConnected()
+        focusedUIObject = None
+        try:
+            if resourceId != "" and text != "":
+                focusedUIObject = self.u(resourceId=resourceId, text=text, focused=True)
+            elif resourceId == "" and text != "":
+                focusedUIObject = self.u(text=text, focused=True)
+            elif resourceId != "" and text == "":
+                focusedUIObject = self.u(resourceId=resourceId, focused=True)
+            else:
+                focusedUIObject = self.u(focused=True)
+        except Exception, e:
+            print "getFocusedUIObject Error:", e
+        return focusedUIObject
+
+    ''' 
+            作用:获取当前界面的selected的UIObject对象
+            参数:
+            注意:
+            例子:self.pyUIAutomator.getFocusedUIObject()
+         '''
+
+    def getSelectedUIObject(self, resourceId='', text=''):
+        self.listenAdbConnected()
+        try:
+            if resourceId != "" and text != "":
+                selectedUIObject = self.u(resourceId=resourceId, text=text, selected=True)
+            elif resourceId == "" and text != "":
+                selectedUIObject = self.u(text=text, selected=True)
+            elif resourceId != "" and text == "":
+                selectedUIObject = self.u(resourceId=resourceId, selected=True)
+            else:
+                selectedUIObject = self.u(selected=True)
+            return selectedUIObject
+        except Exception, e:
+            print "getSelectedUIObject Error:", e
+            return None
+
+    ''' 
+            作用:在脚本运行过程中,监听弹框的出现,如果出现则按按钮使其消失,使得脚本中获取元素控件时不被遮挡报错
+            参数:watcherName 监听器名称
+                 dialogText 弹框的text
+                 buttonText 使弹框消失的按钮text
+            注意: 1.这个函数只是创建监听器,这些监听器生效使用u.startWatchers(),监听器失效使用u.stopWatchers();
+                   2.一般使用在脚本运行实际执行界面前 声明监听器、调用 u.startWatchers(),在脚本 结束时候调用u.stopWatchers()
+            例子:self.pyUIAutomator.getFocusedUIObject()
+                u = PyUIAutomator()
+                u.createDilogWatcher("Normal Dialog Watcher", "我是一个普通Dialog", "确定")
+                u.startWatchers()
+                #这30S延时代表脚本执行
+                time.sleep(30)
+                 u.stopWatchers()
+         '''
+
+    def createDilogWatcher(self, watcherName, dialogText, buttonText):
+        self.listenAdbConnected()
+        self.u.watcher(watcherName).when(text=dialogText).click(text=buttonText)
+
+    ''' 
+             作用:使得创建的弹框监听器生效
+             参数:
+             注意:
+
+          '''
+
+    def startWatchers(self):
+        self.listenAdbConnected()
+        self.u.watchers.watched = True
+
+    ''' 
+             作用:使得创建的弹框监听器失效
+             参数:
+             注意:
+
+          '''
+
+    def stopWatchers(self):
+        self.listenAdbConnected()
+        self.u.watchers.watched = False
+
+    def dump_hierarchy(self):
+        self.listenAdbConnected()
+        return self.u.dump_hierarchy()
+
+
+class DirectionManageAndroid():
+    def __init__(self):
+
+        self.className = self.__class__.__name__
+
+        self.grid = None
+        self.remote = TvOperator()
+
+    def goOneStep(self, pyUIAutomator, direction, keyType = UATTree.Key_Event):
+        # keyDict = {'U': "up", "D": "down", "L": "left", "R": "right"}
+        print "goOneStep,direction:", direction
+        directionArr = direction.split("-")
+        for direct in directionArr:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(),
+                                              u"script:sendKey 执行按键:%s" % (direct))
+            # self.custSendKey(keyDict[direct])
+            if keyType == UATTree.Key_Event:
+                pyUIAutomator.pressKeyTimes(direct)
+            elif keyType == UATTree.Key_IR:
+                self.remote.sendKey(direct)
+
+    # def custSendKey(self, keyEvent):
+    #     if type(keyEvent) == type('') or type(keyEvent) == type(u""):
+    #         self.operator.sendKey(keyEvent)
+    #     if type(keyEvent) == type([]):
+    #         try:
+    #             self.operator.sendKey(keyEvent[0], keyEvent[1], keyEvent[2])
+    #         except Exception:
+    #             LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(),
+    #                                               u'script:sendKey  ERROR 双击按键数组%s格式出错,请按照格式:[按键字符,按键次数,按键间隔]' % (
+    #                                                   keyEvent))
+
+    '''
+    计算目标区域在当前焦点区域的方位.
+            |               |
+    左上角  |      上       |   右上角
+            |               |
+    -----------------------------------
+            |               |
+            |               |
+     左     |    焦点区域   |    右
+            |               |
+    -----------------------------------
+            |               |
+     左下角 |      下       |   右下角
+            |               |
+
+    区域的方位,按上图,分成4面位方向和4角位方向。
+    4面位方向:上、下、左、右。只要目标区域,和4面位方向区域有交集,就属于面位方向。
+    4角位方向:左上角、右上角、左下角、右下角。目标区域全部在4角位方向区域,才是角位方向。
+    方位定义:up、down、left、right、left-up、left-down、right-up、right-down
+    :param areaL:当前焦点所在区域
+    :param areaT:目标区域
+    :return direction。字符串类型,方位:up、down、left、right、left-up、left-down、right-up、right-down
+    '''
+    ImgRectLimit = 10000
+
+    def getTargetDirection(self, focusedBounds, destBounds, moveKey=["down","up"]):
+        areaL = (focusedBounds['left'], focusedBounds['top'], focusedBounds['right'], focusedBounds['bottom'])
+        areaT = (destBounds['left'], destBounds['top'], destBounds['right'], destBounds['bottom'])
+        print "getTargetDirection:", areaL, areaT
+        LoggingUtil.getDebugLogger().info(pyFileName, self.className,
+                                          get_current_function_name(),
+                                          u'script:focusMatch 当前聚焦区域坐标:%s  目标聚焦区域坐标:%s' % (str(areaL), str(areaT)))
+        if self.hasSameArea(areaL,areaT):
+            focusCP = [(areaL[2] + areaL[0])/2,(areaL[3]+areaL[1])/2]
+            destCP = [(areaT[2] + areaT[0])/2,(areaT[3]+areaT[1])/2]
+            return self.getPointDirection(focusCP,destCP)
+        # 确定4个面位方位的区域的x,y轴范围
+        up = [areaL[0], 0, areaL[2], areaL[1]]
+        down = [areaL[0], areaL[3], areaL[2], DirectionManageAndroid.ImgRectLimit]
+        left = [0, areaL[1], areaL[0], areaL[3]]
+        right = [areaL[2], areaL[1], DirectionManageAndroid.ImgRectLimit, areaL[3]]
+        # 检测目标区域是否和4个面位方向区域有重叠部分
+        up_direct = self.hasSameArea(up, areaT)
+        if up_direct:
+            return "up"
+        down_direct = self.hasSameArea(down, areaT)
+        if down_direct:
+            return "down"
+        left_direct = self.hasSameArea(left, areaT)
+        if left_direct:
+            return "left"
+        right_direct = self.hasSameArea(right, areaT)
+        if right_direct:
+            return "right"
+
+        # 计算目标区域坐上角点的方位,x,y差值确定角位方向
+        dL_Ux = areaT[0] - areaL[0]
+        dL_Uy = areaT[1] - areaL[1]
+        if (dL_Ux > 0) and (dL_Uy > 0):
+            if "down" in moveKey:
+                return "down-right"
+            else:
+                return "right-down"
+        if (dL_Ux < 0) and (dL_Uy > 0):
+            if "down" in moveKey:
+                return "down-left"
+            else:
+                return "left-down"
+        if (dL_Ux > 0) and (dL_Uy < 0):
+            if "down" in moveKey:
+                return "up-right"
+            else:
+                return "right-up"
+        if (dL_Ux < 0) and (dL_Uy < 0):
+            if "down" in moveKey:
+                return "up-left"
+            else:
+                return "left-up"
+
+    '''
+    计算点p2相对p1的方位。按p1 45°斜线划分上下左右。
+    '''
+    def getPointDirection(self,p1,p2):
+        print "getPointDirection,p1,p2:",p1,p2
+        dx = p2[0] - p1[0]
+        dy = p2[1] - p1[1]
+        if dx >= 0 and abs(dy) < abs(dx):
+            return "right"
+        if dx <= 0 and abs(dy) < abs(dx):
+            return "left"
+        if dy >= 0 and abs(dy) >= abs(dx):
+            return "down"
+        if dy <= 0 and abs(dy) >= abs(dx):
+            return "up"
+        return "center"
+
+
+    '''
+    检测两个区域是否有重叠部分
+    '''
+
+    def hasSameArea(self, area1, area2):
+        # 计算各区域宽、高
+        area1_W = area1[2] - area1[0]
+        area1_H = area1[3] - area1[1]
+        area2_W = area2[2] - area2[0]
+        area2_H = area2[3] - area2[1]
+
+        # 计算目标区域坐上角点的方位
+        dL_Ux = area1[0] - area2[0]
+        dL_Uy = area1[1] - area2[1]
+        # 计算x坐标,判断是否有重叠可能。
+        x_enable = False
+        if dL_Ux > 0:
+            if (abs(dL_Ux) - area2_W) >= 0:
+                x_enable = False
+            else:
+                x_enable = True
+        else:
+            if (abs(dL_Ux) - area1_W) >= 0:
+                x_enable = False
+            else:
+                x_enable = True
+
+        # 计算y坐标,判断是否有重叠可能。
+        y_enable = False
+        if dL_Uy > 0:
+            if (abs(dL_Uy) - area2_H) >= 0:
+                y_enable = False
+            else:
+                y_enable = True
+        else:
+            if (abs(dL_Uy) - area1_H) >= 0:
+                y_enable = False
+            else:
+                y_enable = True
+
+        # 如果x坐标、y坐标都有可能重叠,则一定重叠。反之,没有重叠。
+        return x_enable and y_enable
+
+    def isSameBounds(self, BoundA, BoundB):
+        areaA = (BoundA['left'], BoundA['top'], BoundA['right'], BoundA['bottom'])
+        areaB = (BoundB['left'], BoundB['top'], BoundB['right'], BoundB['bottom'])
+        if areaA[0] == areaB[0] \
+                and areaA[1] == areaB[1] \
+                and areaA[2] == areaB[2] \
+                and areaA[3] == areaB[3]:
+            return True
+        else:
+            return False
+
+    def isHasAnotherBounds(self, BoundA, BoundB):
+        areaA = (BoundA['left'], BoundA['top'], BoundA['right'], BoundA['bottom'])
+        areaB = (BoundB['left'], BoundB['top'], BoundB['right'], BoundB['bottom'])
+
+        if areaA[0] <= areaB[0] and areaA[1] <= areaB[1] and areaA[2] >= areaB[2] and areaA[3] >= areaB[3]:
+            return True
+        else:
+            return False
+
+    def isRelativePositionBounds(self, initFocusedArea, initDestTextArea, BoundA, BoundB):
+        # initFocusedArea = (
+        #     initFocusedBound['left'], initFocusedBound['top'], initFocusedBound['right'], initFocusedBound['bottom'])
+        # initDestTextArea = (
+        #     initFocusedBound['left'], initDestTextBound['top'], initDestTextBound['right'], initDestTextBound['bottom'])
+
+        areaA = (BoundA['left'], BoundA['top'], BoundA['right'], BoundA['bottom'])
+        areaB = (BoundB['left'], BoundB['top'], BoundB['right'], BoundB['bottom'])
+
+        # if areaA[0] < areaB[0] and areaA[1] < areaB[1] and areaA[2] > areaB[2] and areaA[3] >areaB[3]:
+        #     return True
+        # else:
+        #     return False
+        if initFocusedArea[0] - initDestTextArea[0] == areaA[0] - areaB[0] and initFocusedArea[1] - initDestTextArea[
+            1] == areaA[1] - areaB[1] and initFocusedArea[2] - initDestTextArea[2] == areaA[2] - areaB[2] and \
+                initFocusedArea[3] - initDestTextArea[3] == areaA[3] - areaB[3]:
+            return True
+        else:
+            return False
+        # 比较两个Bound面积大小
+
+    def isBiggerBound(self, boundA, boundB):
+        # print 'boundA:',boundA
+        # print 'boundB:',boundB
+        if ((boundA["bottom"] - boundA["top"]) * (boundA["right"] - boundA["left"])) >= ((
+                                                                                                 boundB["bottom"] -
+                                                                                                 boundB["top"]) * (
+                                                                                                 boundB["right"] -
+                                                                                                 boundB["left"])):
+            return True
+        else:
+            return False
+
+    def getRelativeBounds(self, standBounds_view1, standBounds_view2, currentBound_view1):
+        currentBound_view2 = {}
+        currentBound_view2["top"] = standBounds_view2["top"] - standBounds_view1["top"] + currentBound_view1["top"]
+        currentBound_view2["left"] = standBounds_view2["left"] - standBounds_view1["left"] + currentBound_view1["left"]
+        currentBound_view2["right"] = standBounds_view2["right"] - standBounds_view1["right"] + currentBound_view1[
+            "right"]
+        currentBound_view2["bottom"] = standBounds_view2["bottom"] - standBounds_view1["bottom"] + currentBound_view1[
+            "bottom"]
+
+        return currentBound_view2
+
+
+class FocusManageAndroid():
+    # u是PyUIAutomator对象
+    # focusManage 是DirectionManageAndroid对象
+    def __init__(self, u, directionManageAndroid):
+        self.className = self.__class__.__name__
+        self.u = u
+        self.directionManageAndroid = directionManageAndroid
+        self.ReverseDirctionDict = {"up": "down", "down": "up", "left": "right", "right": "left"}
+        self.featureDetect = FeatureDetect()
+        self.ccard = CCardManager()
+        self.imgCMP = ImageCMP()
+        self.ocr = OCRConvert()
+        self.remote = TvOperator()
+
+    '''
+                     作用:在RecyclerView 中 focused控件包含文字控件  用于聚焦目标焦点
+                     参数:
+                     text:目标文字的文字内容
+                     className:目标文字的className
+                     findDirection: 目标文字控件被隐藏时寻找的方向
+                     Max_Try:最多查找次数
+                     注意:
+                     例子:
+                     小米信源设置界面  聚焦到Device Settings/Sound mode
+                     className = "android.widget.TextView"
+                     focusManageAndroid.toDestFocusByText_className_for_RecyclerView("News", className)
+                  '''
+
+    # 在RecyclerView 中 focused控件包含文字控件
+    def toDestFocusByText_className_for_RecyclerView(self, text, className, findDirection="down", Max_Try=10, keyType = UATTree.Key_Event):
+        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
+        count = 0
+        Max_Try = Max_Try
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try * 2
+        while (True):
+            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                break
+            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
+            time.sleep(0.5)
+            focusedUIObject = self.u.getFocusedUIObject()
+            focusedBounds = focusedUIObject.info['bounds']
+            # print "focusedBounds:",focusedBounds
+            destUIObject = self.u.getUiObject(text=text, className=className)
+            if destUIObject:
+                try:
+                    destBounds = destUIObject.info['bounds']
+                    # print "destBounds:", destBounds
+                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
+                        print '成功聚焦到目标焦点:', text
+                        return True
+                    else:
+                        count = count + 1
+                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
+                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
+                except Exception:
+                    # 出现控件出现一半的时候,获取控件信息会报错
+                    if count < Max_Try:
+                        count = count + 1
+                        self.pressKeyByType(findDirection, keyType)
+                    else:
+                        Reversecount = Reversecount + 1
+                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
+            else:
+                if count < Max_Try:
+                    count = count + 1
+                    self.pressKeyByType(findDirection, keyType)
+                else:
+                    Reversecount = Reversecount + 1
+                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+
+        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
+        return False
+
+    ''' 
+            作用:在RecyclerView 中 focused控件包含文字控件  用于聚焦目标焦点
+            参数:
+            text:目标文字的文字内容
+            textResourceId:目标文字的resourceId
+            findDirection: 目标文字控件被隐藏时寻找的方向
+            Max_Try:最多查找次数
+            注意:
+            例子:
+            小米信源设置界面  聚焦到Device Settings
+            focusManageAndroid.toDestFocusByText_for_RecyclerView("Device Settings","com.android.tv:id/title")
+         '''
+
+    # 在RecyclerView 中 focused控件包含文字控件
+    def toDestFocusByText_for_RecyclerView(self, text, textResourceId="", findDirection="down", Max_Try=10, keyType = UATTree.Key_Event, hb_keyDict={}):
+        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
+        count = 0
+        Max_Try = Max_Try
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try * 2
+        while (True):
+            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                break
+            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
+            time.sleep(0.5)
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            focusedUIObject = self.u.getFocusedUIObject()
+            try:
+                focusedBounds = focusedUIObject.info['bounds']
+            except Exception, e:
+                print "未能获取到当前聚焦焦点,聚焦框丢失,判断聚焦失败!!"
+                return False
+            print "focusedBounds:",focusedBounds
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            destUIObject = self.u.getUiObject(text=text, resourceId=textResourceId)
+
+            if destUIObject:
+                try:
+                    destBounds = destUIObject.info['bounds']
+                    print "destBounds:", destBounds
+                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
+                        print '成功聚焦到目标焦点:', text
+                        return True
+                    else:
+                        count = count + 1
+                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
+                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
+                except Exception:
+                    # 出现控件出现一半的时候,获取控件信息会报错
+                    if count < Max_Try:
+                        count = count + 1
+                        self.pressKeyByType(findDirection, keyType)
+                    else:
+                        Reversecount = Reversecount + 1
+                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
+            else:
+                if count < Max_Try:
+                    count = count + 1
+                    self.pressKeyByType(findDirection, keyType)
+                else:
+                    Reversecount = Reversecount + 1
+                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+
+        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
+        return False
+
+
+    '''
+            作用:在RecyclerView 中 focused控件包含文字控件  用于聚焦目标焦点,传入的resourceId为聚焦焦点的resId时的处理函数
+            参数:
+            text:目标文字的文字内容
+            focuseResId:整个聚焦焦点的resourceId
+            findDirection: 目标文字控件被隐藏时寻找的方向
+            Max_Try:最多查找次数
+            注意:
+            例子:
+            小米俄罗斯设置界面 存在复数个聚焦焦点时,使用此函数,可以规避复数焦点的问题
+    '''
+    def toDestFocusByText_with_FocuseResourceId(self, text, focuseResId, findDirection="down", Max_Try=10, keyType = UATTree.Key_Event, hb_keyDict = {}):
+        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
+        count = 0
+        Max_Try = Max_Try
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try * 2
+        while (True):
+            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                break
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
+            time.sleep(0.5)
+            focusedUIObject = self.u.getFocusedUIObject(resourceId=focuseResId)
+            try:
+                focusedBounds = focusedUIObject.info['bounds']
+            except Exception, e:
+                print "未能获取到当前聚焦焦点,聚焦框丢失,判断聚焦失败!!"
+                return False
+            print "focusedBounds:",focusedBounds
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            destUIObject = self.u.getUiObject(text=text)
+            if destUIObject:
+                try:
+                    destBounds = destUIObject.info['bounds']
+                    print "destBounds:", destBounds
+                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
+                        print '成功聚焦到目标焦点:', text
+                        return True
+                    else:
+                        count = count + 1
+                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
+                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
+                except Exception:
+                    # 出现控件出现一半的时候,获取控件信息会报错
+                    if count < Max_Try:
+                        count = count + 1
+                        self.pressKeyByType(findDirection, keyType)
+                    else:
+                        Reversecount = Reversecount + 1
+                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
+            else:
+                if count < Max_Try:
+                    count = count + 1
+                    self.pressKeyByType(findDirection, keyType)
+                else:
+                    Reversecount = Reversecount + 1
+                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+
+        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
+        return False
+
+
+
+    ''' 
+             作用:在RecyclerVi或listView 中 selected控件包含文字控件  用于聚焦目标焦点
+             参数:
+             text:目标文字的文字内容
+             textResourceId:目标文字的resourceId
+             listViewResourceId:RecyclerVi或listView的resourceId
+             findDirection: 目标文字控件被隐藏时寻找的方向
+             Max_Try:最多查找次数
+             注意:
+             例子:
+             2841项目信源列表聚焦  聚焦到HDMI1
+             focusManageAndroid.toDestSelectByText_for_RecyclerView("HDMI1","com.android.tv:id/title","com.android.tv:id/scene_transition_common")
+          '''
+
+    def toDestSelectByText_for_RecyclerView(self, text, textResourceId='', optionViewResoucreId='', listViewResourceId='', findDirection="down",
+                                            Max_Try=10, keyType = UATTree.Key_Event, hb_keyDict={}):
+        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
+        count = 0
+        Max_Try = Max_Try
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try * 2
+        while (True):
+            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                break
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            listViewUIObject = self.u.getUiObject(resourceId=listViewResourceId)
+            print "listViewUIObject.info:", listViewUIObject.info
+            if optionViewResoucreId != '':
+                selectedUIObject = listViewUIObject.child(selected=True, resourceId=optionViewResoucreId)
+            else:
+                selectedUIObject = listViewUIObject.child(selected=True)
+            print "selectedUIObject.info:", selectedUIObject.info
+            selectedBounds = selectedUIObject.info['bounds']
+            # print "focusedBounds:",focusedBounds
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            destUIObject = self.u.getUiObject(text=text, resourceId=textResourceId)
+            if destUIObject:
+                destBounds = destUIObject.info['bounds']
+                # print "destBounds:", destBounds
+                if self.directionManageAndroid.isHasAnotherBounds(selectedBounds, destBounds):
+                    print '成功聚焦到目标焦点:', text
+                    return True
+                else:
+                    count = count + 1
+                direction = self.directionManageAndroid.getTargetDirection(selectedBounds, destBounds)
+                self.directionManageAndroid.goOneStep(self.u, direction, keyType)
+            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
+            else:
+                if count < Max_Try:
+                    count = count + 1
+                    self.pressKeyByType(findDirection, keyType)
+                else:
+                    Reversecount = Reversecount + 1
+                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+
+        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
+        return False
+
+    ''' 
+                 作用:在界面中 中 focused控件包含content-des控件  用于聚焦目标焦点
+                 参数:
+                 description:目标描述的文字内容
+                 descriptionResourceId:目标描述的resourceId
+                 findDirection: 目标文字控件被隐藏时寻找的方向
+                 Max_Try:最多查找次数
+                 注意:
+                 例子:
+                 小米APP  聚焦到YouTube
+                 descriptionResourceId = "com.google.android.tvlauncher:id/banner_image"
+                 focusManageAndroid.toDestFocusByDescription_for_RecyclerView("YouTube",descriptionResourceId=descriptionResourceId)
+              '''
+
+    # 在RecyclerView 中 focused控件包含文字控件
+    def toDestFocusByDescription_for_RecyclerView(self, description, descriptionResourceId="", findDirection="down",
+                                                  Max_Try=10 ,keyType = UATTree.Key_Event, hb_keyDict = {}):
+        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
+        count = 0
+        Max_Try = Max_Try
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try * 2
+        while (True):
+            print "count:", count
+            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                break
+            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
+            time.sleep(0.5)
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            focusedUIObject = self.u.getFocusedUIObject()
+            focusedBounds = focusedUIObject.info['bounds']
+            # print "focusedBounds:",focusedBounds
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            destUIObject = self.u.getUiObject(description=description, resourceId=descriptionResourceId)
+            if destUIObject:
+                try:
+                    destBounds = destUIObject.info['bounds']
+                    # print "destBounds:", destBounds
+                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
+                        print '成功聚焦到目标焦点:', description
+                        return True
+                    else:
+                        count = count + 1
+                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
+                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
+                except Exception, e:
+                    # 出现控件出现一半的时候,获取控件信息会报错
+                    if count < Max_Try:
+                        count = count + 1
+                        self.pressKeyByType(findDirection, keyType)
+                    else:
+                        Reversecount = Reversecount + 1
+                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
+            else:
+                if count < Max_Try:
+                    count = count + 1
+                    self.pressKeyByType(findDirection, keyType)
+                else:
+                    Reversecount = Reversecount + 1
+                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+
+        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
+        return False
+
+
+        # 在RecyclerView 中 focused控件包含bounds控件
+    def toDestFocusByBounds_for_RecyclerView(self, bounds, boundsResourceId="", findDirection="down",
+                                             Max_Try=10, keyType=UATTree.Key_Event, hb_keyDict={}):
+        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
+        count = 0
+        Max_Try = Max_Try
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try * 2
+        while (True):
+            print "count:", count
+            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                break
+            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
+            time.sleep(0.5)
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            focusedUIObject = self.u.getFocusedUIObject()
+            focusedBounds = focusedUIObject.info['bounds']
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            try:
+                destBounds = bounds
+                if self.directionManageAndroid.isHasAnotherBounds(focusedBounds,destBounds):
+                    print "success to focus the target:",bounds
+                    return True
+                else:
+                    count = count + 1
+                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds,destBounds)
+                    self.directionManageAndroid.goOneStep(self.u,direction,keyType)
+            except Exception,e:
+                # 出现控件出现一半的时候,获取控件信息会报错
+                if count < Max_Try:
+                    count = count + 1
+                    self.pressKeyByType(findDirection, keyType)
+                else:
+                    Reversecount = Reversecount + 1
+                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+                # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
+            else:
+                if count < Max_Try:
+                    count = count + 1
+                    self.pressKeyByType(findDirection, keyType)
+                else:
+                    Reversecount = Reversecount + 1
+                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+
+            print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
+            return False
+
+    # class&Bounds
+    def toDestFocusByClassBounds_for_RecyclerView(self, className, bounds,classResourceId="", findDirection="down",
+                                             Max_Try=10, keyType=UATTree.Key_Event, hb_keyDict={}):
+        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
+        count = 0
+        Max_Try = Max_Try
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try * 2
+        while (True):
+            print "count:", count
+            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                break
+            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
+            time.sleep(0.5)
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            focusedUIObject = self.u.getFocusedUIObject()
+            print "zhouyifocusedUIObject",focusedUIObject
+            print "zhouyifocusedUIObject.info", focusedUIObject.info
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            destUIObject = self.u.getUiObject(className=className, bounds = bounds,resourceId=classResourceId)
+            print "destUIObject",destUIObject
+            try:
+                destUIObjectInfo = destUIObject.info
+                print "destUIObjectInfo",destUIObjectInfo
+            except Exception,e:
+                print "异常:",e
+                # print "destUIObject.info",destUIObjectInfo
+            try:
+                # destBounds = destUIObject.info['bounds']
+                destBounds = bounds
+                print "destBounds:", destBounds
+                if self.directionManageAndroid.isHasAnotherBounds(focusedBounds,destBounds):
+                    print "c&b success to focus the target:",bounds
+                    return True
+                else:
+                    count = count + 1
+                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds,destBounds)
+                    self.directionManageAndroid.goOneStep(self.u,direction,keyType)
+            except Exception,e:
+                # 出现控件出现一半的时候,获取控件信息会报错
+                if count < Max_Try:
+                    count = count + 1
+                    self.pressKeyByType(findDirection, keyType)
+                else:
+                    Reversecount = Reversecount + 1
+                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+                # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
+            else:
+                if count < Max_Try:
+                    count = count + 1
+                    self.pressKeyByType(findDirection, keyType)
+                else:
+                    Reversecount = Reversecount + 1
+                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+
+            print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
+            return False
+
+        # class
+        def toDestFocusByClass_for_RecyclerView(self, className, classResourceId="", findDirection="down",
+                                                Max_Try=10, keyType=UATTree.Key_Event, hb_keyDict={}):
+            # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
+            count = 0
+            Max_Try = Max_Try
+            Reversecount = 0
+            Reverse_Max_Try = Max_Try * 2
+            while (True):
+                print "count:", count
+                if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                    break
+                # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
+                time.sleep(0.5)
+                if hb_keyDict != {}:
+                    # 执行心跳按键
+                    self.executeHeartBeatKey(hb_keyDict)
+                focusedUIObject = self.u.getFocusedUIObject()
+                print "zhouyifocusedUIObject", focusedUIObject
+                print "zhouyifocusedUIObject.info", focusedUIObject.info
+                if hb_keyDict != {}:
+                    # 执行心跳按键
+                    self.executeHeartBeatKey(hb_keyDict)
+                destUIObject = self.u.getUiObject(className=className, resourceId=classResourceId)
+                print "destUIObject", destUIObject
+                try:
+                    destUIObjectInfo = destUIObject.info
+                    print "destUIObjectInfo", destUIObjectInfo
+                except Exception, e:
+                    print "异常:", e
+                    # print "destUIObject.info",destUIObjectInfo
+                try:
+                    destBounds = destUIObject.info['bounds']
+                    print "destBounds:", destBounds
+                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
+                        print '成功聚焦到目标焦点:', bounds
+                        return True
+                    else:
+                        count = count + 1
+                        direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
+                        self.directionManageAndroid.goOneStep(self.u, direction, keyType)
+                except Exception, e:
+                    # 出现控件出现一半的时候,获取控件信息会报错
+                    if count < Max_Try:
+                        count = count + 1
+                        self.pressKeyByType(findDirection, keyType)
+                    else:
+                        Reversecount = Reversecount + 1
+                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+                    # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
+                else:
+                    if count < Max_Try:
+                        count = count + 1
+                        self.pressKeyByType(findDirection, keyType)
+                    else:
+                        Reversecount = Reversecount + 1
+                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+
+                print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
+                return False
+
+
+
+    # zhouyi
+    def toDestTargetByBounds_for_RecyclerView(self, bounds, chooseType, Max_Try=20,
+                                              keyType=UATTree.Key_Event, findFocusCount=0, hb_keyDict={}):
+        count = 0
+        while (True):
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            # targetUIObject = self.u.getUiObject(bounds=bounds)
+            # print "targetUIObject",targetUIObject
+            # print "zhouyibounds",bounds
+            # print "targetUIObject.info:", targetUIObject.info
+            # destBounds = targetUIObject.info['bounds']
+            destBounds = bounds
+            print "目标坐标:", destBounds
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            try:
+                if chooseType.lower() == "focus":
+                    choosingUIObject = self.u.getFocusedUIObject()
+                    objBounds = choosingUIObject.info['bounds']
+                elif chooseType.lower() == "select":
+                    choosingUIObject = self.u.getSelectedUIObject()
+                    objBounds = choosingUIObject.info['bounds']
+                print "当前坐标:", objBounds
+            except Exception, e:
+                print "获取焦点失败!Error:", e
+                if findFocusCount < 3:
+                    self.u.pressKeyTimes("down")
+                    return self.toDestTargetByBounds_for_RecyclerView(bounds, chooseType,
+                                                                      findFocusCount=findFocusCount + 1)
+                else:
+                    print "尝试%s次操作!!!仍未能找到聚焦点!!!无法聚焦到目标" % findFocusCount
+                    return False
+            if self.directionManageAndroid.isHasAnotherBounds(objBounds, destBounds):
+                print "已聚焦至目标组件!!!"
+                return True
+            else:
+                if count >= Max_Try:
+                    print "已尝试至最大次数%s次,仍未能聚焦至目标组件!!!" % Max_Try
+                    return False
+                direction = self.directionManageAndroid.getTargetDirection(objBounds, destBounds)
+                print "目标方位:", direction
+                self.directionManageAndroid.goOneStep(self.u, direction, keyType)
+                count += 1
+
+    ''' 
+                 作用:在界面中 中 focused控件中既没有包含content-des控件属性,也没有text控件属性,
+                       采用单张图片模板匹配  用于聚焦目标焦点
+                 参数:
+                 singleImagePath:目标聚焦的单张小图片的地址
+                 findDirection: 目标文字控件被隐藏时寻找的方向
+                 Max_Try:最多查找次数
+                 注意:
+                 例子:
+                 小米内销APP(既没有文字text属性也没有描述属性content-des)  聚焦到ATX APP
+                 singleImagePath = "D:\\ATX_APP.jpg"
+                 focusManageAndroid.toDestFocusBySingleImage_for_RecyclerView(singleImagePath)
+              '''
+
+    # 在RecyclerView 中 focused控件包含文字控件
+    def toDestFocusBySingleImage_for_RecyclerView(self, singleImagePath, findDirection="down",
+                                                  Max_Try=10 ,keyType = UATTree.Key_Event):
+        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
+        count = 0
+        Max_Try = Max_Try
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try * 2
+        while (True):
+            print "count:", count
+            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                break
+            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
+            time.sleep(0.5)
+            focusedUIObject = self.u.getFocusedUIObject()
+            focusedBounds = focusedUIObject.info['bounds']
+
+            srceenImgPath = os.path.join(sat_environment.getSATTmpDIR(), "SingleImage_runpath.png")
+            self.ccard.takePicture(srceenImgPath)
+            resultDict = self.featureDetect.matchSingleImage(srceenImgPath, [0, 0, 1920, 1080], singleImagePath)
+
+            # destUIObject = self.u.getUiObject(description=description, resourceId=descriptionResourceId)
+            if resultDict:
+                try:
+                    destBounds_coordinate = resultDict['coordinate']
+                    # areaA = (BoundA['left'], BoundA['top'], BoundA['right'], BoundA['bottom'])
+                    destBounds = {"left": destBounds_coordinate[0], "top": destBounds_coordinate[1],
+                                  "right": destBounds_coordinate[2], "bottom": destBounds_coordinate[3]}
+                    # print "destBounds:", destBounds
+                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
+                        print '成功聚焦到目标焦点:', singleImagePath
+                        return True
+                    else:
+                        count = count + 1
+                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
+                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
+                except Exception, e:
+                    # 出现控件出现一半的时候,获取控件信息会报错
+                    if count < Max_Try:
+                        count = count + 1
+                        self.pressKeyByType(findDirection, keyType)
+                    else:
+                        Reversecount = Reversecount + 1
+                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
+            else:
+                if count < Max_Try:
+                    count = count + 1
+                    self.pressKeyByType(findDirection, keyType)
+                else:
+                    Reversecount = Reversecount + 1
+                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+
+        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
+        return False
+
+    ''' 
+                 作用:在界面中 中 focused控件中既没有包含content-des控件属性,也没有text控件属性,
+                       采用OCR识别文字  用于聚焦目标焦点
+                 参数:
+
+                 text:需要OCR识别的文字
+                 textResourceId:文字区域的resourceId
+                 recyclerViewResourceId:列表recyclerView的resourceId
+                 ocrTextBorder:为OCR识别更准确,文字识别坐标需要扩充的边界的值(默认为10)
+                 OCRDict:OCR识别文字的属性 默认为百度中英文识别
+                 findDirection: 目标文字控件被隐藏时寻找的方向
+                 Max_Try:最多查找次数
+                 注意:
+                 例子:
+
+              '''
+
+    # 在RecyclerView 中 focused控件包含文字控件
+    def toDestFocusByOCRText_for_RecyclerView(self, text, textResourceId, recyclerViewResourceId, ocrTextBorder=10,
+                                              OCRDict={"OCR_lan": "CHN_ENG", "OCR_type": 10000},
+                                              findDirection="down",
+                                              Max_Try=10,
+                                              keyType = UATTree.Key_Event):
+        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
+        count = 0
+        Max_Try = Max_Try
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try * 2
+        while (True):
+            print "count:", count
+            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                break
+            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
+            time.sleep(0.5)
+            focusedUIObject = self.u.getFocusedUIObject()
+            focusedBounds = focusedUIObject.info['bounds']
+            # 寻找目标焦点坐标
+            childbound = None
+            recyclerViewUIObject = self.u.getUiObject(resourceId=recyclerViewResourceId)
+            for i in range(recyclerViewUIObject.info["childCount"]):
+                childUIObject = recyclerViewUIObject.child_by_instance(i, resourceId=textResourceId)
+                try:
+                    childbound = childUIObject.info["bounds"]
+                    # 对扩大后的图片坐标进行边界判断
+                    childbound_Area_left = childbound["left"] - 10
+                    if childbound_Area_left < 0: childbound_Area_left = 0
+
+                    childbound_Area_top = childbound["top"] - 10
+                    if childbound_Area_top < 0: childbound_Area_top = 0
+
+                    childbound_Area_right = childbound["right"] + 10
+                    if childbound_Area_right > 1920: childbound_Area_right = 1920
+
+                    childbound_Area_bottom = childbound["bottom"] - 10
+                    if childbound_Area_bottom > 1080: childbound_Area_bottom = 1080
+
+                    childbound_Area = (childbound_Area_left, childbound_Area_top,
+                                       childbound_Area_right, childbound_Area_bottom)
+                    # 小图保存地址
+                    tmpPic = os.path.join(getSATTmpDIR(), "uiautomator_text_ocr.png")
+                    # 整个界面图片保存地址
+                    srceenImgPath = os.path.join(sat_environment.getSATTmpDIR(), "uiautomator_OCR_runpath.png")
+                    self.u.screenshot(srceenImgPath)
+                    # self.ccard.takePicture(srceenImgPath)
+                    # 切割小图片
+                    self.imgCMP.saveCropPic(srceenImgPath, tmpPic, (
+                        childbound_Area[0], childbound_Area[1], childbound_Area[2], childbound_Area[3]))
+                    # ocr 识别
+                    textValueCUR = self.ocr.getStr(tmpPic, OCRDict["OCR_lan"], OCRDict["OCR_type"])
+                    print "textValueCUR:", textValueCUR
+                    print "destText:", text
+                    if self.ocr.cmpOcrStr(textValueCUR, text):
+                        break
+                    else:
+                        childbound = None
+                except Exception:
+                    childbound = None
+                    pass
+
+            if childbound:
+                try:
+                    destBounds = childbound
+                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
+                        print '成功聚焦到目标焦点:', text
+                        return True
+                    else:
+                        count = count + 1
+                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
+                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
+                except Exception, e:
+                    # 出现控件出现一半的时候,获取控件信息会报错
+                    if count < Max_Try:
+                        count = count + 1
+                        self.pressKeyByType(findDirection, keyType)
+                    else:
+                        Reversecount = Reversecount + 1
+                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
+            else:
+                if count < Max_Try:
+                    count = count + 1
+                    self.pressKeyByType(findDirection, keyType)
+                else:
+                    Reversecount = Reversecount + 1
+                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+
+        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
+        return False
+
+    ''' 
+                 作用:在界面中 中 focused控件中既没有包含content-des控件属性,也没有text控件属性,
+                       一般这种控件是自定义控件 class类型是android.view.View
+                       采用OCR识别文字  用于聚焦目标焦点
+                 参数:
+
+                 text:需要OCR识别的文字
+                 visableViewResourceId:可以通过UIAutomator框架获取到的控件的id
+                 initTextArea: 确定的一个区域下的文字坐标
+                 initVisableViewArea:确定的一个区域下的可获取控件的坐标
+                 ocrTextBorder:为OCR识别更准确,文字识别坐标需要扩充的边界的值(默认为10)
+                 OCRDict:OCR识别文字的属性 默认为百度中英文识别
+                 findDirection: 目标文字控件被隐藏时寻找的方向
+                 Max_Try:最多查找次数
+                 注意:
+                 例子:小米内销APP测试
+                 #文字内容
+                   text = "应用商店"
+                 #图标的resourceId
+                   visableViewResourceId = "com.mitv.tvhome:id/di_img"
+                 #非聚焦的app文字坐标
+                   initTextArea = [90, 392, 229, 424]
+                #非聚焦的app图片坐标
+                   initVisableViewArea = [90, 257, 227, 382]
+
+                   focusManageAndroid.toDestFocusRelativeOCRText__for_RecyclerView(text=text,
+                                                                    visableViewResourceId=visableViewResourceId,
+                                                                    initTextArea=initTextArea,
+                                                                    initVisableViewArea=initVisableViewArea)
+
+              '''
+
+    # 在RecyclerView 中 focused控件包含文字控件
+    def toDestFocusRelativeOCRText__for_RecyclerView(self, text, visableViewResourceId, initTextArea,
+                                                     initVisableViewArea, ocrTextBorder=5,
+                                                     OCRDict={"OCR_lan": "CHN_ENG", "OCR_type": 10000},
+                                                     findDirection="down",
+                                                     Max_Try=10,
+                                                     keyType = UATTree.Key_Event):
+        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
+        count = 0
+        Max_Try = Max_Try
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try * 2
+
+        initTextBounds = {"left": initTextArea[0], "top": initTextArea[1], "right": initTextArea[2],
+                          "bottom": initTextArea[3]}
+        initvisableViewBounds = {"left": initVisableViewArea[0], "top": initVisableViewArea[1],
+                                 "right": initVisableViewArea[2], "bottom": initVisableViewArea[3]}
+        while (True):
+            print "count:", count
+            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                break
+            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
+            time.sleep(0.5)
+            focusedUIObject = self.u.getFocusedUIObject()
+            focusedBounds = focusedUIObject.info['bounds']
+            # 寻找目标焦点坐标
+            childbound = None
+            childCountUIObject = self.u.getUiObject(resourceId=visableViewResourceId)
+            print "childCountUIObject.info", childCountUIObject.info
+            childCount = childCountUIObject.count
+            print "列表包含的子控件数量:", childCount
+            for i in range(childCount):
+                print "第%s次查找目标bounds" % (str(i))
+                visableViewObject = self.u.getUiObject(instance=i, resourceId=visableViewResourceId)
+                try:
+                    visableViewBound = visableViewObject.info["bounds"]
+                    print "initvisableViewBounds:", initvisableViewBounds
+                    print "initTextBounds:", initTextBounds
+                    print "visableViewBound:", visableViewBound
+                    childbound = self.directionManageAndroid.getRelativeBounds(initvisableViewBounds, initTextBounds,
+                                                                               visableViewBound)
+                    print "childbound:", childbound
+                    # return False
+                    # childbound = childUIObject.info["bounds"]
+                    # 对扩大后的图片坐标进行边界判断
+                    childbound_Area_left = childbound["left"] - 10
+                    if childbound_Area_left < 0: childbound_Area_left = 0
+
+                    childbound_Area_top = childbound["top"] - 10
+                    if childbound_Area_top < 0: childbound_Area_top = 0
+
+                    childbound_Area_right = childbound["right"] + 10
+                    if childbound_Area_right > 1920: childbound_Area_right = 1920
+
+                    childbound_Area_bottom = childbound["bottom"] + 10
+                    if childbound_Area_bottom > 1080: childbound_Area_bottom = 1080
+
+                    childbound_Area = [childbound_Area_left, childbound_Area_top,
+                                       childbound_Area_right, childbound_Area_bottom]
+                    print "childbound_Area:", childbound_Area
+                    # 小图保存地址
+                    tmpPic = os.path.join(getSATTmpDIR(), "uiautomator_text_ocr.png")
+                    # 整个界面图片保存地址
+                    srceenImgPath = os.path.join(sat_environment.getSATTmpDIR(), "uiautomator_OCR_runpath.png")
+                    self.u.screenshot(srceenImgPath)
+                    # self.ccard.takePicture(srceenImgPath)
+                    # 切割小图片
+                    self.imgCMP.saveCropPic(srceenImgPath, tmpPic, (
+                        childbound_Area[0], childbound_Area[1], childbound_Area[2], childbound_Area[3]))
+                    # ocr 识别
+                    textValueCUR = self.ocr.getStr(tmpPic, OCRDict["OCR_lan"], OCRDict["OCR_type"])
+                    print "textValueCUR:", textValueCUR
+                    print "destText:", text
+                    if self.ocr.cmpOcrStr(textValueCUR, text):
+                        break
+                    else:
+                        childbound = None
+                except Exception, e:
+                    print e
+                    childbound = None
+                    pass
+            print "childbound:", childbound
+            # return False
+            if childbound:
+                try:
+                    destBounds = childbound
+                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
+                        print '成功聚焦到目标焦点:', text
+                        return True
+                    else:
+                        count = count + 1
+                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
+                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
+                except Exception, e:
+                    # 出现控件出现一半的时候,获取控件信息会报错
+                    if count < Max_Try:
+                        count = count + 1
+                        self.pressKeyByType(findDirection, keyType)
+                    else:
+                        Reversecount = Reversecount + 1
+                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
+            else:
+                if count < Max_Try:
+                    count = count + 1
+                    self.pressKeyByType(findDirection, keyType)
+                else:
+                    Reversecount = Reversecount + 1
+                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+
+        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
+        return False
+
+    ''' 
+            作用:在RecyclerView 中 focused控件和文字控件同级 相对位置固定  用于聚焦目标焦点
+            参数:
+            text:目标文字的文字内容
+            textResourceId:目标文字的resourceId
+            initFocusedArea:聚焦情况下的focused控件的坐标区域(x1,y1,x2,y2)
+            initDestTextArea:聚焦情况下的text控件的坐标区域(x1,y1,x2,y2)
+            findDirection: 目标文字控件被隐藏时寻找的方向
+            Max_Try:最多查找次数
+            注意:
+
+            例子:  
+            小米信源设置 的声音自定义界面 Device /Settings Sound mode /Custom/
+
+             initFocusedArea = (822, 180, 1259, 221)
+             initDestTextArea = (822, 116, 1259, 180)
+
+            focusManageAndroid.toDestFocusRelativeText_for_RecyclerView("3500Hz: 0dB", "com.android.tv:id/title",
+                                                                initFocusedArea, initDestTextArea)
+         '''
+
+    # 在RecyclerView 中 focused控件和文字控件同级
+    def toDestFocusRelativeText_for_RecyclerView(self, text, textResourceId, initFocusedArea, initDestTextArea,
+                                                 findDirection="down", Max_Try=10, keyType = UATTree.Key_Event):
+        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
+        count = 0
+        Max_Try = Max_Try
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try * 2
+        while (True):
+            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                break
+            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
+            time.sleep(0.5)
+            focusedUIObject = self.u.getFocusedUIObject()
+            focusedBounds = focusedUIObject.info['bounds']
+            # print "focusedBounds:",focusedBounds
+
+            destUIObject = self.u.getUiObject(text=text, resourceId=textResourceId)
+            if destUIObject:
+                try:
+                    destBounds = destUIObject.info['bounds']
+                    # print "destBounds:", destBounds
+                    if self.directionManageAndroid.isRelativePositionBounds(initFocusedArea, initDestTextArea,
+                                                                            focusedBounds,
+                                                                            destBounds):
+                        print '成功聚焦到目标焦点:', text
+                        return True
+                    else:
+                        count = count + 1
+                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
+                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
+                except Exception:
+                    # 出现控件出现一半的时候,获取控件信息会报错
+                    if count < Max_Try:
+                        count = count + 1
+                        self.pressKeyByType(findDirection, keyType)
+                    else:
+                        Reversecount = Reversecount + 1
+                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
+            else:
+                if count < Max_Try:
+                    count = count + 1
+                    self.pressKeyByType(findDirection, keyType)
+                else:
+                    Reversecount = Reversecount + 1
+                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+
+        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
+        return False
+
+    ''' 
+            作用:聚焦效果View控件 包含 目标文本控件  用于聚焦目标焦点
+            参数:
+            text:目标文字的文字内容
+            textResourceId:目标文字的resourceId
+            FocusViewResourceId:聚焦效果View的resourceId
+            findDirection: 目标文字控件被隐藏时寻找的方向
+            Max_Try:最多查找次数
+            注意:
+            例子:
+            小米USB界面上方Tab聚焦方法
+            focusManageAndroid.toDestFocusByText_for_FocusView("Devices", "com.xiaomi.mitv.mediaexplorer:id/dev",
+                                                           "com.xiaomi.mitv.mediaexplorer:id/tab_cursor",
+                                                           findDirection="right",
+                                                           Max_Try=5)
+         '''
+
+    # 聚焦效果View控件 包含 目标文本控件
+    def toDestFocusByText_for_FocusView(self, text, FocusViewResourceId='', FocusViewClass='',FocusViewDesc='',findDirection="down",
+                                        Max_Try=10, keyType = UATTree.Key_Event, hb_keyDict={}):
+        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
+        count = 0
+        Max_Try = Max_Try
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try * 2
+        while (True):
+            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                break
+            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
+            time.sleep(0.5)
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            if FocusViewResourceId is not '':
+                focusedUIObject = self.u.getUiObject(resourceId=FocusViewResourceId)
+            else:
+                focusedUIObject = self.u.getUiObject(description=FocusViewDesc,className=FocusViewClass)
+
+            focusedBounds = focusedUIObject.info['bounds']
+            print "focusedBounds:", focusedBounds
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            destUIObject = self.u.getUiObject(text=text)
+            if destUIObject:
+                try:
+                    destBounds = destUIObject.info['bounds']
+                    print "destBounds:", destBounds
+                    if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
+                        print '成功聚焦到目标焦点:', text
+                        return True
+                    else:
+                        count = count + 1
+                    direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
+                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
+                except Exception:
+                    # 出现控件出现一半的时候,获取控件信息会报错
+                    if count < Max_Try:
+                        count = count + 1
+                        self.pressKeyByType(findDirection, keyType)
+                    else:
+                        Reversecount = Reversecount + 1
+                        self.pressKeyByType(self.ReverseDirctionDict[findDirection] ,keyType)
+            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
+            else:
+                if count < Max_Try:
+                    count = count + 1
+                    self.pressKeyByType(findDirection, keyType)
+                else:
+                    Reversecount = Reversecount + 1
+                    self.pressKeyByType(self.ReverseDirctionDict[findDirection] ,keyType)
+
+        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
+        return False
+
+    '''
+                   作用:RecyclerView 中焦点没有focused属性  只是子项中的View有放大效果
+                   参数:
+                   text:目标文字的文字内容
+                   textResourceId:目标文字的resourceId
+                   zoomViewResourceId:f放大效果View的resourceId
+                   recyclerViewResourceId:列表recyclerView 的resourceId
+                   findDirection: 目标文字控件被隐藏时寻找的方向
+                   Max_Try:最多查找次数
+                   注意:
+                   例子:
+                   小米USB Images 浏览界面
+                   focusManageAndroid.toDestZoomByText_for_RecyclerView("Pattern", "com.xiaomi.mitv.mediaexplorer:id/album_item_name",
+                                                              "com.xiaomi.mitv.mediaexplorer:id/album_item_bg",
+                                                              "com.xiaomi.mitv.mediaexplorer:id/all_albums_view")
+                '''
+
+    # RecyclerView 中焦点没有focused属性  只是子项中的View有放大效果
+    def toDestZoomByText_for_RecyclerView(self, text, textResourceId, zoomViewResourceId, recyclerViewResourceId,
+                                          findDirection="down",
+                                          Max_Try=20,
+                                          keyType=UATTree.Key_Event):
+        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次
+        count = 0
+        Max_Try = Max_Try
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try * 2
+
+        while (True):
+            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                break
+            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
+            time.sleep(0.5)
+            # focusedUIObject = self.u.getUiObject(resourceId=FocusViewResourceId)
+            # focusedBounds = focusedUIObject.info['bounds']
+            # print "focusedBounds:", focusedBounds
+            destUIObject = self.u.getUiObject(text=text, resourceId=textResourceId)
+            if destUIObject:
+                try:
+                    # 获取目标文本的bounds
+                    destBounds = destUIObject.info['bounds']
+                    # print "destBounds:", destBounds
+
+                    zoomUiObject = self.u.getUiObject(resourceId=recyclerViewResourceId)
+                    # print "uiObject.info: ", uiObject.info
+                    childCount = zoomUiObject.info['childCount']
+
+                    zoomBound = {u'top': 0, u'left': 0, u'right': 0, u'bottom': 0}
+                    zoomIndex = -1
+
+                    for i in range(childCount):
+                        childObject = zoomUiObject.child_by_instance(i, resourceId=zoomViewResourceId)
+                        # print "childObject.info:", childObject.info
+                        childounds = childObject.info['bounds']
+                        # print "child:", str(i), "bounds:", childounds
+                        if self.directionManageAndroid.isBiggerBound(childounds, zoomBound):
+                            zoomBound = childounds
+                            zoomIndex = i
+                            zoomTextBound = zoomUiObject.child_by_instance(i, resourceId=textResourceId).info[
+                                "bounds"]
+                            zoomText = zoomUiObject.child_by_instance(i, resourceId=textResourceId).info["text"]
+
+                    # print zoomBound, zoomIndex, zoomTextBound, zoomText
+                    print "toDestZoomByText_for_RecyclerView 当前聚焦文字:", zoomText
+                    if str(zoomText) == str(text):
+                        print '成功聚焦到目标焦点:', text
+                        return True
+                    # if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
+                    #     print '成功聚焦到目标焦点:', text
+                    #     return True
+                    else:
+                        count = count + 1
+                    direction = self.directionManageAndroid.getTargetDirection(zoomTextBound, destBounds)
+                    self.directionManageAndroid.goOneStep(self.u, direction, keyType)
+                except Exception:
+                    # 出现控件出现一半的时候,获取控件信息会报错
+                    if count < Max_Try:
+                        count = count + 1
+                        self.pressKeyByType(findDirection, keyType)
+                    else:
+                        Reversecount = Reversecount + 1
+                        self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+            # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次
+            else:
+                if count < Max_Try:
+                    count = count + 1
+                    self.pressKeyByType(findDirection, keyType)
+                else:
+                    Reversecount = Reversecount + 1
+                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+
+        print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try))
+        return False
+
+    ''' 
+                   作用:获取RecyclerView 中所有文字名称的列表
+                   参数:
+
+                   recyclerViewResourceId:列表recyclerView 的resourceId
+                   textResourceId:item中文本区域的的resourceId
+                   findDirection: 目标文字控件被隐藏时寻找的方向
+                   Max_Try:最多查看次数,执行Max_Try 次findDirection 后 仍无新控件被刷出 则返回所有文件的列表
+                   name_list:最终返回的列表
+                   注意:
+                   例子:
+                   小米USB Music 浏览界面 获取所有的音乐文件
+                    recyclerViewResourceId = "com.xiaomi.mitv.mediaexplorer:id/all_music_view"
+                    textResourceId = "com.xiaomi.mitv.mediaexplorer:id/item_title"
+                    recyclerViewItemNameList = focusManageAndroid.getRecyclerViewItemNameList(recyclerViewResourceId,textResourceId)
+                    print "recyclerViewItemNameList:", recyclerViewItemNameList
+
+                '''
+
+    def getRecyclerViewItemNameList(self, recyclerViewResourceId, textResourceId, findDirection="down",
+                                    Max_Try=10, name_list=[], keyType = UATTree.Key_Event):
+        count = 0
+        isNOTChange = False
+        old_name_list = name_list
+
+        while (True):
+            if count > Max_Try and isNOTChange:
+                return name_list
+                break
+            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
+            time.sleep(0.5)
+            recyclerViewUIObject = self.u.getUiObject(resourceId=recyclerViewResourceId)
+            for i in range(recyclerViewUIObject.info["childCount"]):
+                childUIObject = recyclerViewUIObject.child_by_instance(i, resourceId=textResourceId)
+                try:
+                    childText = childUIObject.info["text"]
+                    if childText not in name_list:
+                        name_list.append(childUIObject.info["text"])
+                except Exception:
+                    pass
+            if old_name_list == name_list:
+                isNOTChange = True
+                count = count + 1
+                self.pressKeyByType(findDirection, keyType)
+
+            else:
+                isNOTChange = False
+                self.pressKeyByType(findDirection, keyType)
+                self.getRecyclerViewItemNameList(recyclerViewResourceId, textResourceId, findDirection,
+                                                 Max_Try, name_list)
+
+    '''
+                      作用:获取RecyclerView 中所有文字名称的列表
+                      参数:
+                      keyText:寻找目标Text的key的Text
+                      keyResourceId:寻找目标Text的key的resourceId
+                      recyclerViewResourceId:列表recyclerView 的resourceId
+                      textResourceId:目标text的中文本区域的的resourceId
+                      findDirection: 目标文字控件被隐藏时寻找的方向
+                      Max_Try:最多查看次数,执行Max_Try 次findDirection 后 仍无新控件被刷出 则返回""
+                      注意:
+                      例子:
+                      小米USB Music 音乐播放界面 通过音乐名称获取音乐时长
+                       keyResourceId = "com.xiaomi.mimusic2:id/item_textview"
+                       recyclerViewResourceId = "com.xiaomi.mimusic2:id/music_list_view"
+                       textResourceId = "com.xiaomi.mimusic2:id/music_list_item_duration"
+                       findText = focusManageAndroid.getRecyclerViewItemTextByKeyText("鸭子", keyResourceId,
+                                                                                   recyclerViewResourceId,
+                                                                                   textResourceId)
+                       print "findText:", findText
+
+                   '''
+
+    def getRecyclerViewItemTextByKeyText(self, keyText, keyResourceId, recyclerViewResourceId, textResourceId,
+                                         findDirection="down",
+                                         Max_Try=20,
+                                         keyType = UATTree.Key_Event):
+        count = 0
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try * 2
+
+        while (True):
+            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                break
+            # 等待聚焦效果刷新完成,稳定之后再获取相关的属性
+            time.sleep(0.5)
+            recyclerViewUIObject = self.u.getUiObject(resourceId=recyclerViewResourceId)
+            iCounnt = -1
+            findText = ""
+            print "recyclerViewUIObject.info:", recyclerViewUIObject.info
+            for i in range(recyclerViewUIObject.info["childCount"]):
+                print "i:", i
+                try:
+                    childUIObject = recyclerViewUIObject.child_by_instance(i, resourceId=keyResourceId)
+                    if str(childUIObject.info["text"]) == str(keyText):
+                        iCounnt = i
+                        findUIObject = recyclerViewUIObject.child_by_instance(i, resourceId=textResourceId)
+                        findText = findUIObject.info["text"]
+                        return str(findText)
+                except Exception:
+                    iCounnt = -1
+                    pass
+            if iCounnt == -1:
+                if count < Max_Try:
+                    count = count + 1
+                    self.pressKeyByType(findDirection, keyType)
+                else:
+                    Reversecount = Reversecount + 1
+                    self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType)
+        print "执行%s 次查找,仍未找到keyText:%s!!" % (str(Max_Try), str(keyText))
+        return ""
+
+
+    def focusFirstItemFromRecyclerView(self, recyclerView_resId, child_class, Max_Try=20, keyType = UATTree.Key_Event):
+        return self.focusItemByIndexFromRecyclerView(recyclerView_resId, child_class, 0, Max_Try, keyType)
+
+    def focusItemByIndexFromRecyclerView(self, recyclerView_resId, child_class, target_index, Max_Try=20,
+                                         keyType = UATTree.Key_Event, hb_keyDict={}):
+        count = 0
+        recyclerView = self.u.getUiObject(resourceId=recyclerView_resId)
+        while(True):
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            firstItemUIObject = recyclerView.child_by_instance(target_index, className = child_class)
+            print "firstItemUIObject.info:", firstItemUIObject.info
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            destBounds = firstItemUIObject.info['bounds']
+            print "目标坐标:", destBounds
+            focusedUIObject = self.u.getFocusedUIObject()
+            focusedBounds = focusedUIObject.info['bounds']
+            print "当前坐标:",focusedBounds
+            if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds):
+                print "已将焦点归位至第一位!!!"
+                return True
+            else:
+                if count >= Max_Try:
+                    print "已尝试至最大次数%s次,仍未能使焦点归位至第一位!!!"%Max_Try
+                    return False
+                direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds)
+                print "目标方位:",direction
+                self.directionManageAndroid.goOneStep(self.u, direction, keyType)
+                count += 1
+
+    def focusTargetSeekBar(self, target_resId, chooseType, Max_Try=20, keyType = UATTree.Key_Event, findFocusCount = 0):
+        count = 0
+        while(True):
+            targetUIObject = self.u.getUiObject(resourceId=target_resId)
+            print "targetUIObject.info:", targetUIObject.info
+            destBounds = targetUIObject.info['bounds']
+            print "目标坐标:", destBounds
+            try:
+                if chooseType.lower() == "focus":
+                    choosingUIObject = self.u.getFocusedUIObject()
+                    objBounds = choosingUIObject.info['bounds']
+                elif chooseType.lower() == "select":
+                    choosingUIObject = self.u.getSelectedUIObject()
+                    objBounds = choosingUIObject.info['bounds']
+                print "当前坐标:",objBounds
+            except Exception,e:
+                print "获取焦点失败!Error:",e
+                if findFocusCount < 3:
+                    self.u.pressKeyTimes("down")
+                    return self.focusTargetSeekBar(target_resId, chooseType, findFocusCount = findFocusCount + 1)
+                else:
+                    print "尝试%s次操作!!!仍未能找到聚焦点!!!无法聚焦到目标"%findFocusCount
+                    return False
+            if self.directionManageAndroid.isHasAnotherBounds(objBounds, destBounds):
+                print "已聚焦至目标seekbar!!!"
+                return True
+            else:
+                if count >= Max_Try:
+                    print "已尝试至最大次数%s次,仍未能聚焦至目标seekbar!!!"%Max_Try
+                    return False
+                direction = self.directionManageAndroid.getTargetDirection(objBounds, destBounds)
+                print "目标方位:",direction
+                self.directionManageAndroid.goOneStep(self.u, direction, keyType)
+                count += 1
+
+    def toDestTargetByResourceId_for_RecyclerView(self, resourceId, chooseType, Max_Try=20,
+                                                  keyType = UATTree.Key_Event, findFocusCount = 0, hb_keyDict={}):
+        count = 0
+        while(True):
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            targetUIObject = self.u.getUiObject(resourceId=resourceId)
+            print "targetUIObject.info:", targetUIObject.info
+            destBounds = targetUIObject.info['bounds']
+            print "目标坐标:", destBounds
+            if hb_keyDict != {}:
+                # 执行心跳按键
+                self.executeHeartBeatKey(hb_keyDict)
+            try:
+                if chooseType.lower() == "focus":
+                    choosingUIObject = self.u.getFocusedUIObject()
+                    objBounds = choosingUIObject.info['bounds']
+                elif chooseType.lower() == "select":
+                    choosingUIObject = self.u.getSelectedUIObject()
+                    objBounds = choosingUIObject.info['bounds']
+                print "当前坐标:",objBounds
+            except Exception,e:
+                print "获取焦点失败!Error:",e
+                if findFocusCount < 3:
+                    self.u.pressKeyTimes("down")
+                    return self.toDestTargetByResourceId_for_RecyclerView(resourceId, chooseType, findFocusCount = findFocusCount + 1)
+                else:
+                    print "尝试%s次操作!!!仍未能找到聚焦点!!!无法聚焦到目标"%findFocusCount
+                    return False
+            if self.directionManageAndroid.isHasAnotherBounds(objBounds, destBounds):
+                print "已聚焦至目标组件!!!"
+                return True
+            else:
+                if count >= Max_Try:
+                    print "已尝试至最大次数%s次,仍未能聚焦至目标组件!!!"%Max_Try
+                    return False
+                direction = self.directionManageAndroid.getTargetDirection(objBounds, destBounds)
+                print "目标方位:",direction
+                self.directionManageAndroid.goOneStep(self.u, direction, keyType)
+                count += 1
+
+
+    def pressKeyByType(self, keyName, keyType = UATTree.Key_Event, times = 1, duration = 1.0):
+        print "pressKeyByType:",keyName,keyType
+        if keyType == UATTree.Key_Event:
+            return self.u.pressKeyTimes(keyName, times, duration)
+        elif keyType == UATTree.Key_IR:
+            return self.remote.sendKey(keyName, times, duration)
+        else:
+            return False
+
+    '''
+    执行心跳按键的函数。传入UATree里parent['others']['heartbeat_key']中的参数,会根据参数的配置执行心跳按键
+    '''
+    def executeHeartBeatKey(self, hb_keyDict):
+        try:
+            eventList = hb_keyDict["event"]
+            irList = hb_keyDict["ir"]
+            if eventList != "":
+                LoggingUtil.printLog("Executing heartbeat_key by EventKey!!!KeyList:%s" % eventList)
+                for key in eventList:
+                    self.u.pressKeyTimes(key)
+            elif irList != "":
+                LoggingUtil.printLog("Executing heartbeat_key by irKey!!!KeyList:%s" % irList)
+                for key in irList:
+                    self.remote.sendKey(key)
+        except Exception,e:
+            LoggingUtil.printLog(u"[executeHeartBeatKey]: 心跳按键配置异常,无法正常执行心跳按键。\n %s"%e)
+
+
+if __name__ == "__main__":
+    u = PyUIAutomator()
+    dm = DirectionManageAndroid()
+    fm = FocusManageAndroid(u, dm)
+    fm.toDestFocusByText_with_FocuseResourceId(text="Picture", focuseResId="com.mediatek.wwtv.tvcenter:id/menu_item_frame")
+    # directionMange = DirectionManageAndroid()
+    # focusManage = FocusManageAndroid(u, directionMange)
+    # focusObj = focusManage.u.getFocusedUIObject()
+    # print "focusObj.info:",focusObj.info
+    # childCount = focusObj.info["childCount"]
+    # for i in range(childCount):
+    #     childUI = focusObj.child_by_instance(i, className = "android.widget.TextView")
+    #     print childUI.info["text"]
+
+        # focusManage.toDestFocusByText_for_RecyclerView(text="Picture mode",textResourceId="",
+    #                                                findDirection="down")
+    # focusManage.focusTargetSeekBar("com.android.tv.settings:id/seekbar_freq_300")
+    # recyclerViewUIObject = u.getUiObject(resourceId="com.xiaomi.mitv.mediaexplorer:id/photo_list")
+    # print "recyclerViewUIObject.info", recyclerViewUIObject.info
+    # for i in range(recyclerViewUIObject.info["childCount"]):
+    #     childUI = recyclerViewUIObject.child_by_instance(i)
+    #     print "cound %s child.info:"%i, childUI.info
+
+    # focusManage.focusFirstItemFromRecyclerView(recyclerViewUIObject, child_class="android.widget.LinearLayout")
+
+    # focusedUIObject = pyui.getFocusedUIObject()
+    # print 'focusedUIObject:', focusedUIObject
+    # if focusedUIObject:
+    #     print focusedUIObject.info

+ 180 - 0
ssat_sdk/somking/SATClient.py

@@ -0,0 +1,180 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+import abc
+import socket
+import json
+import numpy as np
+import struct
+from ssat_sdk.MenuTree3.TConfig import TConfig
+
+# 创建协议头数据类型
+'''
+// 数据头;
+typedef struct _DATAHEADER_ {
+    // 协议标识符;
+    byte			protocol;
+    // 当前通信包的长度=header长度+buf长度;
+    unsigned int	len;
+    // 命令类型;
+    byte			cmd;
+}DataHeader, *pDataHeader;
+
+// 请求包
+typedef struct _PACKAGE_ {
+    DataHeader	header;
+    byte		buf[4]; // 具体内容(指针地址);
+}Package;
+'''
+ProHead = np.dtype({'names': ['version', 'len', 'cmd'], 'formats': ['u1', 'u4', 'u1']})
+
+
+class SATClient():
+    def __init__(self):
+        self.config = TConfig(r"D:\SAT\SATService\SATService.ini")
+        '''通信sock'''
+        self.sock = None
+        '''通信端口号'''
+        self.port = self.config.get_int('SATService', 'TCPSvrPort')
+        print u'通信端口号',self.port
+        '''通信状态'''
+        self.constate = False
+
+    def __def__(self):
+        self.disconnect()
+
+    '''连接服务器'''
+    def connect(self):
+        try:
+            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            self.sock.connect(('127.0.0.1', self.port))  # 没有返回值;
+            self.sock.sendall('111')
+            self.constate = True
+        except Exception, e:
+            print "BaseClient=> socket connect error:", e, self.port
+            self.constate = False
+
+        return self.constate
+
+    # 发送消息;
+    def sendmsg(self):
+        '''是否连接'''
+        if self.constate is False:
+            if self.connect() is False:
+                return None
+
+        '''拼装要发送的数据包'''
+        msg_json = ""
+        msg_dict = {'device_id': self.device_id, 'device_name': self.device_name, "device_cmd": self.device_cmd, "device_timeout": self.device_timeout}
+        try:
+            msg_json = json.dumps(msg_dict)
+        except Exception:
+            print "BaseClient=> to json error:", msg_dict
+            return None
+
+        '''发送请求数据'''
+        try:
+            self.sock.settimeout(5)
+            phead = np.array([(0xAA, msg_json.__len__() + np.array([(0xAA, 0)], dtype=ProHead).itemsize)], dtype=ProHead)
+            '''一次性发送完整包'''
+            self.sock.sendall(bytearray(phead.tobytes() + bytes(msg_json)))
+            '''分包发送:头、体'''
+            # self.sock.sendall(phead.tobytes())
+            # self.sock.sendall(bytes(msg_json))
+            self.sock.settimeout(None)
+        except Exception, e:
+            print "BaseClient=> send Error:", e
+            self.disconnect()
+            self.connect()
+            return None
+        
+        '''接收返回数据'''
+        try:
+            # 等待数据返回;(串口若请求失败,超时值应该设置在3秒以上)
+            self.sock.settimeout(10)
+            data = bytes(self.sock.recv(1024 * 8))
+            self.sock.settimeout(None)
+
+            phead = data[0: np.array([(0xAA, 0)], dtype=ProHead).itemsize]
+            '''小端<I, 大端>I'''
+            plen = struct.unpack('<I', phead[1:])[0]
+            '''返回头是否有效'''
+            if ord(phead[0]) != 0xAB:
+                return None
+            
+            msg_json = data[np.array([(0xAA, 0)], dtype=ProHead).itemsize:]
+            if plen - 5 != msg_json.__len__():
+                return None
+            
+            # print msg_json
+            msg_json = msg_json.decode('gb2312')
+            # 解析成json;
+            msg_dict = json.loads(msg_json)
+            return msg_dict
+        except Exception, e:
+            print "BaseClient=> recv Error:", e
+            return None
+
+
+    def send_add_device(self, device_name):
+        '''是否连接'''
+        if self.constate is False:
+            if self.connect() is False:
+                return False
+
+        '''发送请求数据'''
+        try:
+            self.sock.settimeout(3)
+            phead = np.array(
+                [
+                    (
+                        # 协议头;
+                        0xAA,   
+                        # 包长度;
+                        device_name.__len__() + np.array([(0xAA, 0, 1)], dtype=ProHead).itemsize,
+                        # 命令类型;
+                        2
+                    )
+                ], 
+            dtype=ProHead)
+            '''一次性发送完整包'''
+            self.sock.sendall(bytearray(phead.tobytes() + bytes(device_name)))
+            self.sock.settimeout(None)
+        except Exception, e:
+            print "__send => send Error:", e
+            import traceback
+            traceback.print_exc()
+            self.disconnect()
+            self.connect()
+            return False
+
+        '''接收返回数据'''
+        try:
+            # 等待数据返回;(串口若请求失败,超时值应该设置在3秒以上)
+            self.sock.settimeout(10)
+            data = bytes(self.sock.recv(1024 * 8))
+            self.sock.settimeout(None)
+
+            phead = data[0: np.array([(0xAA, 0, 1)], dtype=ProHead).itemsize]
+            '''小端<I, 大端>I'''
+            plen = struct.unpack('<I', phead[1:5])[0]   # len长度占4位字节;
+            '''返回头是否有效'''
+            if ord(phead[0]) != 0xAA and plen != 6:
+                return False
+            
+            # 添加设备,不返回其他数据;
+            return True
+        except Exception, e:
+            print "BaseClient=> recv Error:", e
+            return False
+
+    '''断开连接'''
+    def disconnect(self):
+        try:
+            self.sock.shutdown(2)
+            self.sock.close()
+        except Exception, e:
+            print "BaseClient=> socket disconnect error:", e
+
+if __name__ == "__main__":
+    client = SATClient()
+    client.send_add_device('192.168.1.102')

+ 444 - 0
ssat_sdk/somking/SomkePretreatment.py

@@ -0,0 +1,444 @@
+# -*- coding:utf-8 -*-
+import os
+import sys
+import time
+import re
+# import getopt
+import json
+import chardet
+import datetime
+import hashlib  # md5验证;
+import subprocess
+import urllib2
+import threading
+from ssat_sdk.basessh2 import baseSSH2
+from ssat_sdk.tv_operator import TvOperator
+# 导入测试精灵;
+from ssat_sdk.device_manage.testWizardClient import TestWizardClient
+from SATClient import SATClient
+# 导入http相关库:python2.x中2个一起用,python3.x合并成一个urllib
+import urllib
+import urllib2
+
+
+class SomkePretreatment:
+    def __init__(self, taskId=None):
+        self.taskId = taskId
+        if self.taskId is None:
+            self.__parseCommandLine()
+        # 任务信息;
+        self.taskInfo = {}
+        self.ota_local_name = 'usb_ota.zip'
+        # 上传升级包的位置;
+        # self.updateZip = ' /storage/0000-0000/usb_ota.zip'
+        self.updateZip = ' /storage/905A-30AB/usb_ota.zip'
+        # ota路径;
+        self.ota_server_path = "/home/RT2841_2851_dailybuild/DailyBuild_RT2841_%s/" % str(time.strftime("%m%d"))
+        # ota名称;
+        self.ota_server_name = 'signed-ota_rt2841_update.zip'
+        # apk安装ota的命令;
+        self.installCmd = ' shell am start -n com.apps.ota/.ui.MainActivity'
+        # ssh2;
+        self.ssh2 = baseSSH2()
+        # 遥控器;
+        self.redhat3 = TvOperator()
+        # 获取任务信息;
+        self.getTaskInfo()
+
+    '''
+    函数:解析命令行参数
+    参数:
+    返回:
+    注意:argv[0]属于程序本身名称
+    '''
+
+    def __parseCommandLine(self):
+        if sys.argv.__len__() > 1:
+            try:
+                args = json.loads(sys.argv[1].replace("'", '"'))
+                # 解析出taskId;
+                if "TaskId" in args:
+                    self.taskId = args["TaskId"]
+                print "TaskId=", self.taskId
+            except Exception, e:
+                print u"解析json失败:%s" % sys.argv[1]
+
+    def __cmdExecute(self, cmd):
+        # 定义文件名称;
+        file_name = str(time.time())
+        # 输出到文件中;
+        cmd = cmd + ' >> ' + file_name
+        print u'执行cmd:', cmd
+        proc = subprocess.Popen(cmd, shell=True)
+        # 等待完成;
+        proc.wait()
+        # 读取文件内容;
+        data = ''
+        with open(file_name, mode="rb") as f:
+            data = f.read()
+
+        os.remove(file_name)
+        print u"cmd结果:", data
+        return data
+
+    def __adbConnect(self):
+        try:
+            # 启用adb的root权限;
+            cmd = "adb connect " + self.taskInfo["deviceSerial"]
+            proc = self.__cmdExecute(cmd)
+            if proc.find("connected to " + self.taskInfo["deviceSerial"] + ":5555") < 0 and proc.find(
+                    "already connected to " + self.taskInfo["deviceSerial"] + ":5555") < 0:
+                return False
+
+            print "adb connect success"
+            return True
+        except Exception, e:
+            print 'adb connect error:', e
+            return False
+
+    def adbCheck(self):
+        try:
+            cmd = "adb devices"
+            proc = self.__cmdExecute(cmd)
+            if proc.find(self.taskInfo["deviceSerial"] + ":5555\tdevice\r\n") > -1:
+                print "adb connected"
+                return True
+
+            print "adb didn't connected"
+            return False
+        except Exception,e:
+            return False
+
+    def __adbDisconnect(self):
+        # 启用adb的root权限;
+        cmd = "adb disconnect " + self.taskInfo["deviceSerial"]
+        proc = self.__cmdExecute(cmd)
+        if proc.find("disconnected " + self.taskInfo["deviceSerial"]) < 0 and proc.find(
+                "error: no such device '" + self.taskInfo["deviceSerial"] + ":5555'") < 0:
+            return False
+
+        print "adb disConnect success"
+        return True
+
+    def __adbRoot(self):
+        try:
+            # 启用adb的root权限;
+            cmd = "adb -s " + self.taskInfo["deviceSerial"] + " root"
+            proc = self.__cmdExecute(cmd)
+            if proc.find("adbd is already running as root") < 0 and proc.find(
+                    "restarting adbd as root") < 0 and proc != '':
+                return False
+
+            print "adb root success"
+            return True
+        except Exception, e:
+            print 'adb root error:', e
+            return False
+
+    def __adbRemount(self):
+        # 启用adb的root权限;
+        cmd = "adb -s " + self.taskInfo["deviceSerial"] + " remount /data"
+        proc = self.__cmdExecute(cmd)
+        if proc.find("adbd is already running as root") < 0 and proc.find("restarting adbd as root") < 0 and proc != '':
+            return False
+
+        print "adb root success"
+        return True
+
+    def __adbInstallApk(self, apk_path):
+        if os.path.exists(apk_path):
+            cmd = 'adb -s ' + self.taskInfo['deviceSerial'] + ' install -r -t ' + apk_path
+            proc = self.__cmdExecute(cmd)
+            if (proc.find('Success') >= 0):
+                print "install ota_install.apk success"
+                # 启动app;
+                cmd = 'adb -s ' + self.taskInfo[
+                    'deviceSerial'] + r' shell am start -n com.apps.ota.myapplication/.DeleteZipActivity'
+                proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).communicate()[
+                    0]
+                return True
+        return False
+
+    def __adbUninstallApk(self, apk_name):
+        return False
+
+    
+    def __requestTaskInfo(self):
+        # 请求服务器;
+        data = {'data':{"taskId" : self.taskId}}
+        msg = {'requestMsg' : json.dumps(data)}
+        try:
+            post_data = urllib.urlencode(msg)
+            url = 'http://10.126.16.60:8580/btc_task_se/ajaxExternalInteractiveManage!getSmokeTaskInfo.action'
+            response = urllib2.urlopen(url, post_data)
+            data = response.read().decode("utf-8")
+            res = json.loads(data)
+            if str(res['responseMsg']['code']) == '00':
+                self.taskInfo = {
+                    "host": res['responseMsg']['data']['compileInfo']['serverName'],
+                    "hostPort": res['responseMsg']['data']['compileInfo']['serverPort'],
+                    "hostUser": res['responseMsg']['data']['compileInfo']['serverAccount'],
+                    "hostPwd": res['responseMsg']['data']['compileInfo']['serverPassword'],
+                    "deviceSerial": res['responseMsg']['data']['deviceId']
+                }
+                # 服务器路径;
+                ser_path = res['responseMsg']['data']['compileInfo']['resultPath']
+                self.ota_server_path = "%sDailyBuild_RT2841_%s/" % (ser_path, str(time.strftime("%m%d")))
+                print u"taskInfo",self.taskInfo
+                print u"ser_path",ser_path
+                print u"ota_server_path", self.ota_server_path
+                # 返回结果;
+                return True
+        except Exception, e:
+            print u"获取任务信息失败",e
+        # 默认返回False
+        return False 
+
+
+    def getWindowsUsbPath(self):
+        disks = self.__cmdExecute("wmic logicaldisk get deviceid, description")
+        print chardet.detect(disks)
+        disks = disks.decode("utf-16").encode('utf-8').decode('utf-8-sig')
+        disks = disks.split('\r\n')
+        for disk in disks:
+            if u'可移动磁盘' in disk:
+                return re.search(r'\w:', disk).group()
+        return None
+
+    '''
+    函数:获取任务信息;
+    参数:
+    返回:
+    '''
+
+    def getTaskInfo(self):
+        # 任务信息;
+        if self.taskId is None or self.taskInfo.__len__() == 0:  #测试开关;
+            self.taskId = 5520
+            self.taskInfo = {
+                "host": "10.201.251.254",
+                "hostPort": 22,
+                "hostUser": "wjf",
+                "hostPwd": "wjf2019",
+                "deviceSerial": '192.168.18.210'
+            }
+        # 请求服务器;
+        if self.__requestTaskInfo() is False:
+            return
+        # 初始化ssh2;
+        self.ssh2.init_ssh2(self.taskInfo['hostUser'], self.taskInfo['hostPwd'], self.taskInfo['host'], self.taskInfo['hostPort'])
+        self.redhat3.sendKey('home')
+        self.redhat3.sendKey('home')
+        self.__adbConnect()
+
+    '''
+    函数:下载apk
+    参数:
+    返回:
+    '''
+
+    def downloadApk(self, apk_url, save_path):
+        pass
+
+    '''
+    函数:apk是否已在本地;
+    参数:
+    返回:
+    '''
+
+    def isApkExist(self, apk_md5, apk_path):
+        local_md5 = ''
+        file_data = []
+        md5_local = hashlib.md5()
+        if os.path.exists(apk_path):
+            with open(apk_path, mode='rb') as f:
+                while True:
+                    data = f.read(8192)
+                    if not data:
+                        break
+                    md5_local.update(data)
+            local_md5 = md5_local.hexdigest()
+            print u"本地MD5=", local_md5
+        return True if apk_md5 == local_md5 else False
+
+    def installApk(self, apk_path):
+        # 先连接才能root;
+        if self.__adbConnect() is False:
+            return False
+
+        # 安装apk
+        if self.__adbInstallApk(apk_path) is False:
+            return False
+
+        # 断开adb连接;
+        # self.__adbDisconnect()
+        return True
+
+    def unInstallApk(self, apk_name):
+        # 先连接才能root;
+        if self.__adbConnect() is False:
+            return
+
+            # 启用adb的root权限;
+        if self.__adbRoot() is False:
+            return
+
+        if self.__adbUninstallApk(apk_name) is False:
+            return
+
+    def parseOTAImageName(self):
+        pass
+
+    def isOTAImageOnServer(self):
+        cmd = "ls %s" % self.ota_server_path
+        status, data = self.ssh2.execute_cmd(cmd)
+        if status is True and data.find(self.ota_server_name) >= 0:
+            return True
+
+        return False
+
+    def downloadOTAImage(self):
+        print u"downloadOTAImage start"
+        sftp_path = self.ota_server_path + self.ota_server_name
+        usbSwitch = TestWizardClient()
+        '''
+        尝试5次切换U盘
+        '''
+        tryCount = 5
+        switchRet = False
+        while tryCount > 0:
+            if usbSwitch.sendUsbSwitch(1) is True:
+                switchRet = True
+                break
+            time.sleep(2)
+            tryCount = tryCount - 1
+
+        if switchRet is False:
+            print u"切换U盘到电脑失败"
+            return False
+
+        time.sleep(5) # 等待Windows识别U盘。
+        usbPath = self.getWindowsUsbPath()
+        if usbPath is None:
+            print u"获取U盘盘符失败"
+            return False
+        local_path = usbPath + "\\" + self.ota_local_name
+        return self.ssh2.sftp_download_md5(sftp_path, local_path)
+
+    def isOTAImageOnLocal(self):
+        pass
+
+    def uploadOTAImage2TV(self):
+        # 必须先root才能push;
+        if self.__adbRoot() is False:
+            print u"adb root失败"
+            return False
+
+        '''
+        尝试5次切换U盘
+        '''
+        tryCount = 5
+        switchRet = False
+        usbSwitch = TestWizardClient()
+        while tryCount > 0:
+            if usbSwitch.sendUsbSwitch(0) is True:
+                switchRet = True
+                break
+            time.sleep(2)
+            tryCount = tryCount - 1
+
+        if switchRet is False:
+            print u"切换U盘到TV失败"
+            return False
+        
+        return True
+
+    def reomveOTAImageOnTV(self):
+        pass
+
+    def installOTAImage(self):
+        print u"installOTAImage start"
+        try:
+            self.redhat3.sendKey("home")
+            cmd = 'adb -s ' + self.taskInfo['deviceSerial'] + self.installCmd
+            proc = self.__cmdExecute(cmd)
+            print proc
+            self.redhat3.sendKey("down")
+            time.sleep(1.5)
+            self.redhat3.sendKey("ok")
+            time.sleep(1.5)
+            self.redhat3.sendKey("ok")
+            # 等待60秒启动安装;
+            time.sleep(60)
+        except Exception, e:
+            print u'安装失败=', e
+
+    def checkOTAInstall(self):
+        try:
+            # 先connect,再devices
+            cmd = 'adb connect ' + self.taskInfo['deviceSerial']
+            proc = self.__cmdExecute(cmd)
+            if proc.find('connected') >= 0:
+                cmd = 'adb devices'
+                proc = self.__cmdExecute(cmd)
+                if proc.find(self.taskInfo['deviceSerial'] + ':5555\tdevice') >= 0:
+                    print "device connect success"
+                    print "OTA update success!"
+                    return True
+
+            time.sleep(10)
+            return False
+        except Exception, e:
+            print u'检测失败=', e
+
+    def initAtx(self):
+        # 重启后,重连adb;
+        client = SATClient()
+        if client.send_add_device(self.taskInfo['deviceSerial']) is False:
+            print u"重连adb设备失败",self.taskInfo['deviceSerial']
+            self.__adbConnect()
+        # 等待10;
+        print "等待30秒,让TV完全启动。"
+        time.sleep(30)
+        os.system(r'D:\SAT\tools\atx-init\atx-init.bat')
+
+    def turnOnTV(self):
+        tryCount = 5
+        while tryCount > 0: 
+            if self.adbCheck() is True:
+                print u'开机成功'
+                return True
+            
+            # 按下Power键;
+            self.redhat3.sendKey("POWER")
+            # 等待60秒;
+            time.sleep(60)
+            tryCount = tryCount -1
+            # 尝试连接;
+            self.__adbConnect()
+        
+        print u'开机失败'
+        return False
+
+if __name__ == "__main__":
+    smp = SomkePretreatment()
+    smp.getTaskInfo()
+    smp.adbCheck()
+    # print smp.getWindowsUsbPath()
+    # if smp.isOTAImageOnServer():
+    #     if smp.downloadOTAImage():
+    #         print u'下载完成'
+    #     else:
+    #         print u'下载失败'
+    # smp.taskInfo = {"deviceSerial":'192.168.1.101'}
+    # #if smp.installApk('F:\SAT\SAT_Runner\ota_apk\RT2841\ota_install.apk') is True:
+    # #if smp.uploadOTAImage2TV('F:\signed-ota_rt2841_update.zip') is True:
+    # smp.installOTAImage()
+    # time.sleep(30)
+    # upStatus = False
+    # for i in range(30):
+    #     if smp.checkOTAInstall() is True:
+    #         upStatus = True
+    #         break
+    # if upStatus is False:
+    #     print "OTA update time out 15min"

+ 2 - 0
ssat_sdk/somking/__init__.py

@@ -0,0 +1,2 @@
+# -*- coding:utf-8 -*-
+import os, sys, time