浏览代码

1、同步部分代码;
2、COptionAction:
新增:getOptionInfo
新增:isOptionInPaths
3、COptionExcel
修复:getOptionValueText当参数value为int或float类型时的BUG.
新增:getOptionAllSiblingItemName

scbc.sat2 5 年之前
父节点
当前提交
a98f71a845
共有 5 个文件被更改,包括 444 次插入139 次删除
  1. 202 76
      ssat_sdk/MenuTree3/OptionAction.py
  2. 59 6
      ssat_sdk/MenuTree3/OptionExcel.py
  3. 19 0
      ssat_sdk/MenuTree3/TExcelParser.py
  4. 158 57
      ssat_sdk/MenuTree3/TMenu.py
  5. 6 0
      ssat_sdk/sound_init.py

+ 202 - 76
ssat_sdk/MenuTree3/OptionAction.py

@@ -87,6 +87,8 @@ class COptionAction(CBaseLog):
         # 当前状态下的变量,与__pos对应;
         self.__curOptionName = ''
         self.__curOptionInfo = None
+        self.__prevOptionName = ''
+        self.__prevOptionInfo = None
         # 到达value节点后,记录value值(一般只用于range() 数值记录,其他值无意义);
         self.__optionValueText = ""
         # 获取一次当前层级信息;
@@ -115,6 +117,10 @@ class COptionAction(CBaseLog):
     def optionValueInfo(self):
         return self.__optionValueInfo
 
+    @property
+    def optionPaths(self):
+        return self.__optionPaths
+
     '''
     函数:截图并返回截图路径,如果当前option/optionValue含有layout坐标参数,则返回坐标截图;
     参数:无
@@ -260,7 +266,7 @@ class COptionAction(CBaseLog):
             self.sendKey(key, 1, 0.2)
         time.sleep(1)
         # 发送ok键;
-        if others["enter_key"] != "default":
+        if not strcmp(others["enter_key"], "default"):
             self.sendKey(others["enter_key"])
         # 判断是否成功输入密码;
         current_uiPic = self.takePicture()
@@ -433,68 +439,67 @@ class COptionAction(CBaseLog):
     '''
     函数:返回到父节点
     参数:
-        isReduce    是否需要自减层级。
-                    一般用于value节点返回时,才需要用到该参数(如果value节点设置值后,不是自动返回,要设置False)。
     返回:无
-    
+
     注意:
         sendKey的等待时间太短,会导致画面未响应,截图时还是截上一状态的,比如0.1秒就很经常出现这问题。
         同时,按键等待时间,应该有所区分。
-        
+
         如果不截图,可以不考虑sendKey的等待时间.
     '''
 
-    def back2ParentNode(self, isReduce=True):
+    def back2ParentNode(self):
         # 获取当前option信息;
-        wait = 1
-        duration = 1
         if self.getCurOptionInfo():
             tKeyDict = self.__curOptionInfo['toparent_key']
+            peKeyDict = self.__prevOptionInfo['enter_key']
         else:
             tKeyDict = self.__optionValueInfo['toparent_key']
-            # 同时,根据是否有发送enter key和值类型来判断是否需要多发次返回。
-            if not self.__isEnterKeyInValue:
-                # 标记为True,防止重复进入.
-                self.__isEnterKeyInValue = True
-                self.info(u"进入了value表但未按enter key, 值类型=%s" % str(self.__valueType))
-                if self.__valueType == VType.SelectType:
-                    # 如果是选择型数据,没有enter键;
-                    self.back2ParentNode(False)
-                elif self.__valueType == VType.InputType:
-                    pass
-                    # 如果是输入型数据,没有enter键;
-                    # 可能输入后,自动进入。需要根据autoBackFlag来判断;
-                    # if autoBackFlag is False:
-                    #   self.back2ParentNode(False)
-                    # else:
-                    #   self.__pos -= 1
-                elif self.__valueType == VType.RangeType:
-                    # 如果是进度条数据,没有enter键;
-                    self.back2ParentNode(False)
-
-        # 有可能没有toParent_key参数栏,或者有toParent_key参数栏,但参数栏为空
-        if tKeyDict.__len__() == 0 or tKeyDict['key'].__len__() == 0:
-            self.sendKey('return', duration=duration)
+            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 == "":
+            # 是否为value层:
+            if self.isOnValueSheet():
+                # 如果为value层,上一父层的enter_key是否为default
+                if strcmp(peKeyDict["key"][0], "default"):
+                    # 如果是default,则toparent_key也按default处理;
+                    self.info("当前层未配toparent_key,且父层enter_key为default,默认toparent_key为default")
+                    tKeyDict = {'key': ["default", ]}
+                else:
+                    # 如果不是default,则toparent_key按照return处理;
+                    tKeyDict = {'key': ["return", ]}
+            else:
+                # 如果不为value层,toparent_key统一按return处理。
+                tKeyDict = {'key': ["return", ]}
+        #  如果配了toparent_key
         else:
-            keyList = tKeyDict['key'].split(",")
-            if tKeyDict["duration"] != "":
-                duration = tKeyDict['duration']
-            if tKeyDict["wait"] != "":
-                wait = float(tKeyDict['wait'])
-            self.info(u"toParentKey存在参数,keyList=%s,duration=%s,wait=%s" % (keyList, duration, wait))
-            for key in keyList:
-                self.sendKey(key, duration=duration)
-
-        time.sleep(wait)
-        # 返回,自减;
-        if isReduce:
-            if tKeyDict.__len__() > 0 and tKeyDict["tolevel"] != "":
-                level = tKeyDict['tolevel'].lower()
-                self.info(u"toParentkey将回退至level:%s" % level)
-                self.__pos = self.__getLevelIndex(level) + 1
+            # toparent_key是否为default
+            if strcmp(tKeyDict['key'][0], "default"):
+                # 如果是default,则toparent_key也按default处理;
+                self.info("当前层toparent_key为default")
             else:
-                self.__pos -= 1
-                self.getCurOptionInfo()
+                self.info("当前层toparent_key为%s" % str(tKeyDict['key']))
+        self.__executeToParentKey(tKeyDict)
 
     '''
         在忽略大小写的情况下,获取到该level对应的g_level的下标值
@@ -515,26 +520,18 @@ class COptionAction(CBaseLog):
     def enterNode(self):
         # 获取当前option信息;
         if self.getCurOptionInfo():
-            # 是否有等待时间
-            waitTime = self.__optionConfig.getParentWaitTime(self.__curOptionInfo['parent'])
             # 析出enter key;
+            # 优先使用option本身的enter_key
             optionEnterKey = self.__curOptionInfo['option_enter_key']
-            if optionEnterKey.__len__() == 0:
-                self.sendKey(self.__curOptionInfo['enter_key'], 1, waitTime)
-            else:
-                self.sendKey(optionEnterKey, 1, waitTime)
-            # 进入下层,自增
-            self.__pos += 1
-            # 重新获取次信息;
-            self.getCurOptionInfo()
+            # 如果option本身的enter_key为空,则使用该层parent的enter_key
+            if optionEnterKey['key'] == "":
+                optionEnterKey = self.__curOptionInfo['enter_key']
+            self.__executeEnterKey(optionEnterKey)
         else:
             if not self.__isEnterKeyInValue:
                 self.info(u"节点已在value上,且没有触发过enter key")
-                # waitTime = self.__optionConfig.getParentWaitTime(self.__curOptionInfo['parent'])
                 enterKey = self.__optionValueInfo['enter_key']
-                if enterKey != "default":
-                    self.__isEnterKeyInValue = True
-                    self.sendKey(enterKey, 1, 0.1)
+                self.__executeEnterKey(enterKey)
             else:
                 self.info(u"节点已在value上,已触发过enter key")
 
@@ -556,7 +553,7 @@ class COptionAction(CBaseLog):
             self.error(u"pos值[%d]超出路径范围:[0-%d]" % (pos, self.__optionPaths.__len__()))
             return
         self.__pos = pos
-        # 变量层级后,需要重新获取当前节点信息,保证外部调用正常;
+        # 变更后要重新读一次当前option
         self.getCurOptionInfo()
 
     '''
@@ -586,8 +583,8 @@ class COptionAction(CBaseLog):
             others = json.loads(others)
         else:
             others = {}
-        # 是否有按键延时值;
-        duration = float(others['duration']) if "duration" in others else 0.1
+        # 是否有按键延时值;<注意:有些地方duration=0.1时,会变成长按的效果。>
+        duration = float(others['duration']) if "duration" in others else 0.2
 
         # 值类型:
         # 0 表示默认选择类型.
@@ -623,13 +620,9 @@ class COptionAction(CBaseLog):
             self.sendKey(chList, 1, duration)
 
         # 最后,如果有进入键执行;
-        if enterKey != 'default':
+        if not strcmp(enterKey['key'][0], 'default') and enterKey['key'][0] != "":
             self.info(u"value节点具有enter key")
-            self.sendKey(enterKey, 1, 0.1)
-            self.__isEnterKeyInValue = True
-            # enter之后,可能会返回上层菜单,也可能不返回.
-            # if autoBackFlag is False:
-            #   self.back2ParentNode(False)
+            self.__executeEnterKey(enterKey)
 
     '''
     函数:获取当前层级的目标option详细信息.
@@ -645,6 +638,8 @@ class COptionAction(CBaseLog):
         if self.__pos >= self.__optionPaths.__len__():
             # self.__curOptionInfo = None  # 是否需要在到达value层级后置空,可能保留值会有比较多用处!
             self.warn(u"已到达value节点,无法获取路径信息")
+            self.__prevOptionName = self.__curOptionName
+            self.__prevOptionInfo = self.__curOptionInfo
             return False
 
         # 只有第一次或层级移动了才需要更新;
@@ -655,6 +650,16 @@ class COptionAction(CBaseLog):
                 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表;
@@ -680,8 +685,53 @@ class COptionAction(CBaseLog):
         nextOptionName = self.__optionPaths[g_level[self.__pos + 1]]['option']
         return self.__optionExcel.getOptionInfo(nextOptionName, self.__optionPaths)
 
-    def getCurParentInfo(self):
-        pass
+    '''
+    函数:获取指定位置的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 pos == self.__optionPaths.__len__():
+            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
 
     '''
     函数:检测路径是否有效;
@@ -788,7 +838,7 @@ class COptionAction(CBaseLog):
                     # 转为小写,保证所有比较都是小写;
                     siblingText = siblingText.lower()
                     # 兄弟项文本是否被包含在curFocusText中或相同;
-                    if siblingText in curFocusText or strcmp(siblingText, 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)
@@ -1019,7 +1069,7 @@ class COptionAction(CBaseLog):
     返回:无
     '''
 
-    def sendKey(self, key, count=1, duration=1):
+    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:
@@ -1048,6 +1098,82 @@ class COptionAction(CBaseLog):
     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)
+
 
 if __name__ == "__main__":
     exData = CExtraData()

+ 59 - 6
ssat_sdk/MenuTree3/OptionExcel.py

@@ -448,6 +448,44 @@ class COptionExcel(CBaseLog):
         # 返回结果;
         return list_ocr
 
+    '''
+    函数:获取指定option的所有兄弟项名称;
+    参数:
+    返回:
+    '''
+    def getOptionAllSiblingItemName(self, optionName):
+        # 编码转换;
+        if type(optionName) == str:
+            optionName = unicode(optionName)
+
+        found = False
+        listName = []
+        # 首先,字典不排序,需要倒序;
+        paths = reversed(self.__pathParams.paths)
+        for level in paths:
+            for parent in self.__pathParams.paths[level]:
+                for vals in self.__pathParams.paths[level][parent]["value"]:
+                    if vals['option'].lower() == optionName.lower():
+                        found = True
+                        break
+                # endfor
+                # 找到退出;
+                if found is True:
+                    break
+            # endfor
+            # 找到退出;
+            if found is True:
+                break
+        # endfor
+
+        # 遍历value列表;
+        if found is True:
+            for val in self.__pathParams.paths[level][parent]["value"]:
+                listName.append(val['option'])
+
+        # 返回结果;
+        return listName
+
     '''
     函数:获取指定option的ocr文本描述,以list返回
     参数:optionName   要返回文本描述的option节点
@@ -565,6 +603,11 @@ class COptionExcel(CBaseLog):
         if optionName is None or optionName.__len__() == 0:
             self.error(u"optionName空,退出执行")
             return []
+
+        # 如果是整数或浮点数,退出;
+        if type(value) == int or type(value) == float:
+            self.info(u"value类型是整数或浮点数")
+
         valueTextList = []
         if optionName in self.__valueParams.values:
             valueDictList = self.valueParams.values[optionName]["value"]
@@ -572,10 +615,20 @@ class COptionExcel(CBaseLog):
                 if value is None or value == "":
                     value_for_ocr = valueDict["value_for_ocr"]
                     valueTextList.extend(value_for_ocr.split(';'))
-                elif valueDict["value"] == value:
-                    value_for_ocr = valueDict["value_for_ocr"]
-                    valueTextList = value_for_ocr.split(';')
-                    break
+                else:
+                    # 如果是字符串;
+                    if type(value) == str:
+                        if valueDict["value"].lower() == value.lower():
+                            value_for_ocr = valueDict["value_for_ocr"]
+                            valueTextList = value_for_ocr.split(';')
+                            break
+                    # 如果是整数或浮点数;
+                    elif type(value) == int or type(value) == float:
+                        value_for_ocr = valueDict["value_for_ocr"]
+                        if "range" in value_for_ocr:
+                            value_for_ocr = value_for_ocr.replace("range", "")
+                            valueTextList = list(eval(value_for_ocr))
+                            break
                 # endif
             # endfor
         # endif
@@ -613,7 +666,7 @@ class COptionExcel(CBaseLog):
 
         # 编码转换;
         if type(optionName) == str:
-            option = unicode(optionName)
+            optionName = unicode(optionName)
 
         found = False
         dict_option = {}
@@ -650,7 +703,7 @@ class COptionExcel(CBaseLog):
                         break
                     else:
                         for vals in self.__pathParams.paths[level][parent]["value"]:
-                            if vals['option'].lower() == option.lower():
+                            if vals['option'].lower() == optionName.lower():
                                 if dict_option.__len__() == 0:
                                     found = True
                                     dict_option['level'] = level

+ 19 - 0
ssat_sdk/MenuTree3/TExcelParser.py

@@ -55,6 +55,12 @@ class CExcelParser(CBaseLog):
                         rowDict[pKeys[index]] = self.__parseToParentKey(item)
                     elif pKeys[index] == "layout" and oneRow[index] != "":
                         rowDict[pKeys[index]] = self.__parseLayout(item)
+                    # 如果enter_key以新格式配置,则以新格式解析
+                    elif pKeys[index] == "enter_key" and str(item).find("key[")>= 0:
+                        rowDict[pKeys[index]] = self.__parseEnterKey(item)
+                    elif pKeys[index] == "enter_key":
+                        keyStr = "key[%s]"%item
+                        rowDict[pKeys[index]] = self.__parseEnterKey(keyStr)
                     else:
                         rowDict[pKeys[index]] = item
                 if rowDict[xlsc.value_name] is None or rowDict[xlsc.value_name].__len__() < 1:
@@ -84,6 +90,12 @@ class CExcelParser(CBaseLog):
                     rowDict[pKeys[index]] = self.__parseToParentKey(item)
                 elif pKeys[index] == "layout" and oneRow[index] != "":
                     rowDict[pKeys[index]] = self.__parseLayout(item)
+                # 如果enter_key以新格式配置,则以新格式解析
+                elif pKeys[index] == "enter_key" and str(item).find("key[") is True:
+                    rowDict[pKeys[index]] = self.__parseEnterKey(item)
+                elif pKeys[index] == "enter_key":
+                    keyStr = "key[%s]"%item
+                    rowDict[pKeys[index]] = self.__parseEnterKey(keyStr)
                 else:
                     rowDict[pKeys[index]] = item
             if rowDict[xlsc.parent] is None or rowDict[xlsc.parent].__len__() < 1:
@@ -228,6 +240,7 @@ class CExcelParser(CBaseLog):
         keyList = ["key", "duration", "wait", "tolevel", "dialog"]
         # 按照"key[]"的方式切片并解析成字典
         keyDict = self.__parseMulParam(keyList, keyStr)
+        keyDict["key"] = tuple(keyDict["key"].split(","))
         return keyDict
 
     def __parseLayout(self, layoutStr):
@@ -236,6 +249,12 @@ class CExcelParser(CBaseLog):
         keyDict["bounds"] = tuple(keyDict["bounds"].split(","))
         return keyDict
 
+    def __parseEnterKey(self, keyStr):
+        keyList = ["key", "duration", "wait", "isback", "tolevel"]
+        keyDict = self.__parseMulParam(keyList, keyStr)
+        keyDict["key"] = tuple(keyDict["key"].split(","))
+        return keyDict
+
     '''
     返回:{key:value,key2:value2,...}
     '''

+ 158 - 57
ssat_sdk/MenuTree3/TMenu.py

@@ -8,6 +8,7 @@ from OptionAction import COptionAction
 from ssat_sdk.source_input import SourceGenInput
 from ssat_sdk.tv_detect import *
 from ssat_sdk.device_manage.capturecard_manager import CCardManager
+from ssat_sdk.utils.string_util import strcmp
 # 测试使用到
 import random
 
@@ -28,6 +29,8 @@ class CTMenu(CBaseLog):
         self.__exData = CExtraData()
         self.__optionExcel = COptionExcel(self.__exData)
         self.__optionConfig = COptionConfig(self.__exData, self.__optionExcel)
+        self.__curOpa = None
+        self.__lastOpa = None
 
     @property
     def exData(self):
@@ -41,6 +44,46 @@ class CTMenu(CBaseLog):
     def optionConfig(self):
         return self.__optionConfig
 
+    @property
+    def opa(self):
+        return self.__curOpa
+
+    @property
+    def pos(self):
+        return self.__curOpa.pos
+
+    def setOpa(self, opa):
+        self.__curOpa = opa
+
+    def setCurPos(self, pos):
+        self.__curOpa.setCurPos(pos)
+
+    '''
+    函数:同步两个opa信息;
+    参数:
+    返回:
+    '''
+
+    def SyncOptionInfo(self):
+        if self.__lastOpa:
+            self.info(u"lastOpa.pos=%d" % self.__lastOpa.pos)
+            len = self.__lastOpa.pos - self.__curOpa.getPathLength()
+            # 前者位置大于当前opa长度;
+            if len > 0:
+                # 判断路径是否为子集关系;
+                if self.__lastOpa.isOptionInPaths(self.__lastOpa.getPathLength(), self.__curOpa.curOptionName) is True:
+                    while len > 0:
+                        len -= 1
+                        self.__lastOpa.back2ParentNode()
+                    # 同时设置当前opa位置;
+                    self.__curOpa.setCurPos(self.__curOpa.getPathLength())
+            else:
+                # 判断路径是否为子集关系;
+                if self.__curOpa.isOptionInPaths(self.__lastOpa.pos, self.__lastOpa.curOptionName) is True:
+                    self.__curOpa.setCurPos(self.__lastOpa.pos)
+        # 记录;
+        self.__lastOpa = self.__curOpa
+
     def takePicture(self):
         img = os.path.join(getSATTmpDIR(), "menutree_runpath.png")
         self.captureCard.takePicture(img)
@@ -113,6 +156,12 @@ class CTMenu(CBaseLog):
 
         return isOnTargetNode, targetText
 
+    def move2TargetNode(self, opa, moveDirection, maxTry=15):
+        return self.__move2TargetNode(opa, moveDirection, maxTry)
+
+    def back2Home(self):
+        return self.__back2Home(self.__curOpa)
+
     '''
     函数:退出 usb 信源界面;
     参数:
@@ -177,7 +226,7 @@ class CTMenu(CBaseLog):
             # 有些信源,需要手动enter
             enterKey = opa.curOptionInfo['enter_key']
             if enterKey != "default":
-                self.redRat3.sendKey(enterKey)
+                opa.executeKey(enterKey)
 
             # 判断是否移动成功;
             targetFocus, targetText = opa.isOnTargetNode(pic)
@@ -195,7 +244,7 @@ class CTMenu(CBaseLog):
                     # 2次:移动到下/上一节点;
                     opa.move2NextSiblingNode() if moveDirection else opa.move2PrevSiblingNode()
                     opa.move2NextSiblingNode() if moveDirection else opa.move2PrevSiblingNode()
-                    self.redRat3.sendKey(enterKey, duration=2)
+                    opa.executeKey(enterKey)
                     # 因为信源界面消失了,需要再调用一次.
                     opa.callFirstOptionShortCutKey()
                     continue
@@ -226,13 +275,15 @@ class CTMenu(CBaseLog):
             self.__back2Home(opa)
             return False
 
-        # 首先,调用根菜单;
-        opa.callFirstOptionShortCutKey()
-        # time.sleep(1)
-        if opa.isOnFirstOption() is False:
-            self.error(u"未聚焦到根节点上,退出")
-            self.__back2Home(opa)
-            return False
+        # 只有从0层开始才需要调用根菜单;
+        if self.__curOpa.pos == 0:
+            # 首先,调用根菜单;
+            opa.callFirstOptionShortCutKey()
+            # time.sleep(1)
+            if opa.isOnFirstOption() is False:
+                self.error(u"未聚焦到根节点上,退出")
+                self.__back2Home(opa)
+                return False
 
         # 遍历到目标option中;
         if self.__move2TargetNode(opa, True)[0] is False:
@@ -294,24 +345,24 @@ class CTMenu(CBaseLog):
     def setOptionValue(self, optionName, optionValue):
         self.info(u"【%s】【%s】" % (optionName, optionValue))
         CTMenu.sourceInput.setPattern(11)
-        opa = COptionAction(optionName, optionValue, self.__optionConfig, self.__optionExcel)
+        self.__curOpa = COptionAction(optionName, optionValue, self.__optionConfig, self.__optionExcel)
 
         # 检测路径是否有效;
-        if self.__openOption(opa) is False:
+        if self.__openOption(self.__curOpa) is False:
             return False
 
         # 遍历到value节点中;
-        if self.__move2TargetNode(opa, True)[0] is False:
-            if self.__move2TargetNode(opa, False)[0] is False:
-                self.__back2Home(opa)
+        if self.__move2TargetNode(self.__curOpa, True)[0] is False:
+            if self.__move2TargetNode(self.__curOpa, False)[0] is False:
+                self.__back2Home(self.__curOpa)
                 return False
 
         # 找到value节点,设置值;
         self.info(u"设置value值:一般只适用于Number类型或Input类型,不适合选择类型。但都必须调用,因为里面有enterkey操作")
-        opa.setOptionValue()
+        self.__curOpa.setOptionValue()
 
         # 返回主页;
-        self.__back2Home(opa)
+        self.__back2Home(self.__curOpa)
         return True
 
     '''
@@ -325,15 +376,14 @@ class CTMenu(CBaseLog):
     def checkOptionValue(self, optionName, optionValue):
         self.info(u"【%s】【%s】" % (optionName, optionValue))
         CTMenu.sourceInput.setPattern(11)
-        opa = COptionAction(optionName, optionValue, self.__optionConfig, self.__optionExcel)
-
+        self.__curOpa = COptionAction(optionName, optionValue, self.__optionConfig, self.__optionExcel)
         # 进入节点;
-        if self.__openOption(opa) is False:
+        if self.__openOption(self.__curOpa) is False:
             return False
 
         isValueEqual = False
         # 获取value节点值;
-        valueFocus, valueText = opa.isOnTargetNode()
+        valueFocus, valueText = self.__curOpa.isOnTargetNode()
         self.info(u"===当前捕获的文本内容=%s,目标文本=%s,状态:%d===" % (str(valueText), str(optionValue), valueFocus))
 
         # 如果是数值,则判断获取的值是否相等;否则,只判断是否聚焦.
@@ -342,11 +392,8 @@ class CTMenu(CBaseLog):
         else:
             isValueEqual = valueFocus
 
-        # 因为只是查看值,没有设置值,所以需要再手动返回一层。
-        opa.back2ParentNode(False)
-
         # 返回主页;
-        self.__back2Home(opa)
+        self.__back2Home(self.__curOpa)
 
         # 聚焦状态就是结果;
         self.info(
@@ -363,15 +410,41 @@ class CTMenu(CBaseLog):
     def openOption(self, optionName):
         self.info(u"【%s】" % optionName)
         CTMenu.sourceInput.setPattern(11)
-        opa = COptionAction(optionName, None, self.__optionConfig, self.__optionExcel)
+        self.__curOpa = COptionAction(optionName, None, self.__optionConfig, self.__optionExcel)
+
+        # 同步前后信息;
+        self.SyncOptionInfo()
 
-        if self.__openOption(opa) is False:
+        if self.__openOption(self.__curOpa) is False:
             return False
 
         # 聚焦状态就是结果;
         self.info(u"成功open指定option=%s" % optionName)
         return True
 
+    '''
+    函数:设置self.__curOpa的值;
+    参数:
+    返回:
+
+    注意:一般在使用了openOption后,才调用该函数.且该函数不返回(不调用back2Home);
+    '''
+
+    def setValue(self, optionValue):
+        self.info(u"开始设置值:optionName=%s, optionValue=%s" % (self.__curOpa.curOptionName, str(optionValue)))
+        self.__curOpa.setValue(optionValue)
+        # 遍历到value节点中;
+        if self.__move2TargetNode(self.__curOpa, True)[0] is False:
+            if self.__move2TargetNode(self.__curOpa, False)[0] is False:
+                self.__back2Home(self.__curOpa)
+                return False
+
+        # 找到value节点,设置值;
+        self.info(u"设置value值:一般只适用于Number类型或Input类型,不适合选择类型。但都必须调用,因为里面有enterkey操作")
+        self.__curOpa.setOptionValue()
+
+        return True
+
     '''
     函数:聚焦到指定option的value上,不设置不打开不返回。
     参数:
@@ -386,16 +459,16 @@ class CTMenu(CBaseLog):
     def focusOptionValue(self, optionName, optionValue):
         self.info(u"【%s】【%s】" % (optionName, optionValue))
         CTMenu.sourceInput.setPattern(11)
-        opa = COptionAction(optionName, optionValue, self.__optionConfig, self.__optionExcel)
+        self.__curOpa = COptionAction(optionName, optionValue, self.__optionConfig, self.__optionExcel)
 
         # 进入节点;
-        if self.__openOption(opa) is False:
+        if self.__openOption(self.__curOpa) is False:
             return False
 
         # 遍历到value节点中;
-        if self.__move2TargetNode(opa, True)[0] is False:
-            if self.__move2TargetNode(opa, False)[0] is False:
-                self.__back2Home(opa)
+        if self.__move2TargetNode(self.__curOpa, True)[0] is False:
+            if self.__move2TargetNode(self.__curOpa, False)[0] is False:
+                self.__back2Home(self.__curOpa)
                 return False
 
         return True
@@ -410,21 +483,18 @@ class CTMenu(CBaseLog):
     def getOptionValue(self, optionName):
         self.info(u"【%s】" % optionName)
         CTMenu.sourceInput.setPattern(11)
-        opa = COptionAction(optionName, None, self.__optionConfig, self.__optionExcel)
+        self.__curOpa = COptionAction(optionName, None, self.__optionConfig, self.__optionExcel)
 
         # 检测路径是否有效;
-        if self.__openOption(opa) is False:
+        if self.__openOption(self.__curOpa) is False:
             return False, None
 
         # 获取value节点值;
-        valueFocus, valueText = opa.isOnTargetNode()
+        valueFocus, valueText = self.__curOpa.isOnTargetNode()
         self.info(u"===当前捕获的文本内容=%s,聚焦状态:%d===" % (str(valueText), valueFocus))
 
-        # 因为只是查看值,没有设置值,所以需要再手动返回一层。
-        opa.back2ParentNode(False)
-
         # 返回主页;
-        self.__back2Home(opa)
+        self.__back2Home(self.__curOpa)
 
         return valueFocus, valueText
 
@@ -437,9 +507,9 @@ class CTMenu(CBaseLog):
     def focusOption(self, optionName):
         self.info(u"【%s】" % optionName)
         CTMenu.sourceInput.setPattern(11)
-        opa = COptionAction(optionName, None, self.__optionConfig, self.__optionExcel)
+        self.__curOpa = COptionAction(optionName, None, self.__optionConfig, self.__optionExcel)
 
-        return self.__focusOption(opa)
+        return self.__focusOption(self.__curOpa)
 
     '''
     函数:
@@ -448,8 +518,8 @@ class CTMenu(CBaseLog):
     '''
 
     def inputUnlock(self, stdText, password=''):
-        opa = COptionAction(None, None, self.__optionConfig, self.__optionExcel)
-        return opa.inputUnlock(stdText, password)
+        self.__curOpa = COptionAction(None, None, self.__optionConfig, self.__optionExcel)
+        return self.__curOpa.inputUnlock(stdText, password)
 
     '''
     函数:当焦点已经在option层级上时,移动到指定的option上。
@@ -461,19 +531,19 @@ class CTMenu(CBaseLog):
     '''
 
     def moveToOption(self, optionName):
-        opa = COptionAction(optionName, None, self.__optionConfig, self.__optionExcel)
+        self.__curOpa = COptionAction(optionName, None, self.__optionConfig, self.__optionExcel)
         # 已经在该层级上.
-        opa.setCurPos(opa.getPathLength() - 1)
+        self.__curOpa.setCurPos(self.__curOpa.getPathLength() - 1)
 
         # 检测路径是否有效;
-        if opa.checkRunOptionPath() is False:
-            self.__back2Home(opa)
+        if self.__curOpa.checkRunOptionPath() is False:
+            self.__back2Home(self.__curOpa)
             return False
 
         # 遍历到目标option中;
-        if self.__move2TargetNode(opa, True)[0] is False:
-            if self.__move2TargetNode(opa, False)[0] is False:
-                self.__back2Home(opa)
+        if self.__move2TargetNode(self.__curOpa, True)[0] is False:
+            if self.__move2TargetNode(self.__curOpa, False)[0] is False:
+                self.__back2Home(self.__curOpa)
                 return False
 
         self.info(u"已移动到目标节点[%s]" % optionName)
@@ -549,27 +619,48 @@ class CTMenu(CBaseLog):
         2、usb信源下,可能会调用source按键失败,所以需要调用其他退出键(ok、exit、return)
     '''
 
-    def setSourceValue(self, optionName):
+    def setSourceValue(self, optionParent, optionName):
         self.info(u"【%s】" % optionName)
         CTMenu.sourceInput.setPattern(11)
         sourceList = self.__optionExcel.getOptionAllSiblingItemTextList1d(optionName)
-        opa = COptionAction(optionName, None, self.__optionConfig, self.__optionExcel)
+        self.__curOpa = COptionAction(optionName, None, self.__optionConfig, self.__optionExcel)
 
         # 检测路径是否有效;
-        if not opa.checkRunOptionPath():
+        if not self.__curOpa.checkRunOptionPath():
             return False
 
         # 首先,调用根菜单;
-        opa.callFirstOptionShortCutKey()
+        self.__curOpa.callFirstOptionShortCutKey()
         # 遍历到目标option中;
-        if self.__move2SourceNode(opa, True, sourceList.__len__() * 2)[0] is False:
-            if self.__move2SourceNode(opa, False, sourceList.__len__() * 2)[0] is False:
-                self.__back2Home(opa)
+        if self.__move2SourceNode(self.__curOpa, True, sourceList.__len__() * 2)[0] is False:
+            if self.__move2SourceNode(self.__curOpa, False, sourceList.__len__() * 2)[0] is False:
+                self.__back2Home(self.__curOpa)
                 return False
 
         self.info(u"成功进入指定信源:%s" % optionName)
         return True
 
+    '''
+    函数:识别传入的option / optionValue,是否在当前页面中存在
+    参数:
+        option:在value不传入的情况下,option为非value层的option名;
+        value:传入的情况下,option为value层的value_name,value则为value名
+    返回:
+        boolean,是否识别到该option / optionValue
+    '''
+
+    def checkOptionExist(self, option, value=None):
+        self.__curOpa = COptionAction(option, value, self.optionConfig, self.optionExcel)
+        self.__curOpa.setCurPos(self.__curOpa.getPathLength())
+        return self.__curOpa.isOnTargetNode()[0]
+
+    '''
+        调用当前opa的back2ParentNode()
+    '''
+
+    def back2parentNode(self):
+        return self.__curOpa.back2ParentNode()
+
 
 if __name__ == "__main__":
     ml = CTMenu()
@@ -587,6 +678,16 @@ if __name__ == "__main__":
         time.sleep(st)
         ml.setOptionValue('picture_preset', 'movie')
 
+    ml.openOption('picture_preset')
+    ml.setValue('movie')
+
+    print "--------------------------------------------------"
+
+    ml.openOption('brightness')
+    ml.setValue(90)
+
+    ml.back2Home()
+
     if 0:
         st = 5
         value = random.randint(0, 100)
@@ -739,7 +840,7 @@ if __name__ == "__main__":
         print u'------------------------------'
         ml.setSourceValue('av')
 
-    if 1:
+    if 0:
         # ml.setOptionValue('auto_volume_control', 'off')
         # ml.setOptionValue('lock', 'on')
         ml.setSourceValue('dtv')

+ 6 - 0
ssat_sdk/sound_init.py

@@ -0,0 +1,6 @@
+# -*- coding:utf-8 -*-
+from ssat_sdk.sound_tool import AudioIdentify
+
+if __name__ == "__main__":
+    audioIdentify = AudioIdentify()
+    audioIdentify.setSoundLevel()