Browse Source

自动化sdk:未大改MenuTree前的版本;

scbc.sat2 5 năm trước cách đây
mục cha
commit
72d98858cd
100 tập tin đã thay đổi với 22634 bổ sung0 xóa
  1. 176 0
      ssat_sdk/MenuTree3/TConfig.py
  2. 58 0
      ssat_sdk/MenuTree3/TData.py
  3. 884 0
      ssat_sdk/MenuTree3/TExcelParser.py
  4. 264 0
      ssat_sdk/MenuTree3/TFocus.py
  5. 2587 0
      ssat_sdk/MenuTree3/TMenu.py
  6. 425 0
      ssat_sdk/MenuTree3/TSourceImpl.py
  7. 2 0
      ssat_sdk/MenuTree3/__init__.py
  8. BIN
      ssat_sdk/MenuTree3/接口清单.xlsx
  9. 164 0
      ssat_sdk/StreamCard_SCBC.py
  10. 0 0
      ssat_sdk/TvDetect/__init__.py
  11. 109 0
      ssat_sdk/TvDetect/colorbar_detect.py
  12. BIN
      ssat_sdk/TvDetect/res/colorbar_atv_std.png
  13. BIN
      ssat_sdk/TvDetect/res/colorbar_av_std.png
  14. BIN
      ssat_sdk/TvDetect/res/colorbar_hdmi_std.png
  15. 137 0
      ssat_sdk/UATree/UAT_PathManage.py
  16. 117 0
      ssat_sdk/UATree/UAT_data.py
  17. 163 0
      ssat_sdk/UATree/UAT_excelParser.py
  18. 32 0
      ssat_sdk/UATree/UAT_fileManage.py
  19. 23 0
      ssat_sdk/UATree/UAT_log.py
  20. 139 0
      ssat_sdk/UATree/UAT_menu.py
  21. 357 0
      ssat_sdk/UATree/UAT_runner.py
  22. 810 0
      ssat_sdk/UATree/UAT_runnerCommand.py
  23. 421 0
      ssat_sdk/UATree/UAT_tree.py
  24. 6 0
      ssat_sdk/UATree/UAT_treeConstant.py
  25. 2 0
      ssat_sdk/UATree/__init__.py
  26. 4 0
      ssat_sdk/__init__.py
  27. 686 0
      ssat_sdk/action/Executor.py
  28. 60 0
      ssat_sdk/atv_channel_table.py
  29. 51 0
      ssat_sdk/atv_player.py
  30. 96 0
      ssat_sdk/baseSerial.py
  31. 131 0
      ssat_sdk/chroma22293_pattern.py
  32. 0 0
      ssat_sdk/client/__init__.py
  33. 153 0
      ssat_sdk/client/ota_client.py
  34. 2 0
      ssat_sdk/config/__init__.py
  35. 113 0
      ssat_sdk/config/all-result.xsl
  36. 9 0
      ssat_sdk/config/baidu.cfg
  37. 114 0
      ssat_sdk/config/baidu_config.py
  38. 81 0
      ssat_sdk/config/case_result_detail.xsl
  39. 44 0
      ssat_sdk/config/resource.cfg
  40. 4 0
      ssat_sdk/config/server.cfg
  41. 0 0
      ssat_sdk/core/__init__.py
  42. 24 0
      ssat_sdk/core/ibinder_listener.py
  43. 14 0
      ssat_sdk/core/ibinder_pipe.py
  44. 125 0
      ssat_sdk/core/process_sync.py
  45. 64 0
      ssat_sdk/debug_serial.py
  46. 158 0
      ssat_sdk/device_manage/Chroma22293.py
  47. 174 0
      ssat_sdk/device_manage/Model_3116A.py
  48. 112 0
      ssat_sdk/device_manage/Model_3116A_CmdParse.py
  49. 226 0
      ssat_sdk/device_manage/RedRatHub3.py
  50. 232 0
      ssat_sdk/device_manage/RedRatHub4.py
  51. 2 0
      ssat_sdk/device_manage/__init__.py
  52. 113 0
      ssat_sdk/device_manage/baseClient - 副本.py
  53. 113 0
      ssat_sdk/device_manage/baseClient.py
  54. 267 0
      ssat_sdk/device_manage/c22293_manager.py
  55. 242 0
      ssat_sdk/device_manage/capturecard_manager.py
  56. 196 0
      ssat_sdk/device_manage/dektec_manager.py
  57. 39 0
      ssat_sdk/device_manage/device_manager.py
  58. 189 0
      ssat_sdk/device_manage/sound_manager.py
  59. 110 0
      ssat_sdk/device_manage/tg39_manager.py
  60. 27 0
      ssat_sdk/device_manage/ub530_manager.py
  61. 543 0
      ssat_sdk/ocr_convert.py
  62. 326 0
      ssat_sdk/pic_tool.py
  63. 89 0
      ssat_sdk/picture/CrossColor.py
  64. 94 0
      ssat_sdk/picture/DoubleImage.py
  65. 155 0
      ssat_sdk/picture/PreImageJudge.py
  66. 193 0
      ssat_sdk/picture/RGB.py
  67. 1 0
      ssat_sdk/picture/__init__.py
  68. 241 0
      ssat_sdk/picture/cie_luv.py
  69. 329 0
      ssat_sdk/picture/color_space.py
  70. 3320 0
      ssat_sdk/picture/feature_detect.py
  71. 225 0
      ssat_sdk/picture/image_util.py
  72. 251 0
      ssat_sdk/picture/line_util.py
  73. 29 0
      ssat_sdk/picture/object_detect.py
  74. 161 0
      ssat_sdk/picture/ocr_baidu.py
  75. 43 0
      ssat_sdk/picture/ocr_tesseract.py
  76. 120 0
      ssat_sdk/picture/pic_segment.py
  77. 169 0
      ssat_sdk/picture/pq_detect.py
  78. 18 0
      ssat_sdk/picture/rect_util.py
  79. BIN
      ssat_sdk/picture/std.jpg
  80. BIN
      ssat_sdk/picture/test.jpg
  81. 1938 0
      ssat_sdk/python_uiautomator.py
  82. BIN
      ssat_sdk/resource/CIE_1976_UCS.png
  83. 224 0
      ssat_sdk/result_tool.py
  84. 2 0
      ssat_sdk/runner/__init__.py
  85. 70 0
      ssat_sdk/runner/runner_sender.py
  86. 50 0
      ssat_sdk/sample_tool.py
  87. 895 0
      ssat_sdk/sat_environment.py
  88. 61 0
      ssat_sdk/serial_tool.py
  89. 284 0
      ssat_sdk/service/Chroma22293.py
  90. 543 0
      ssat_sdk/service/TC_UB530_service.py
  91. 2 0
      ssat_sdk/service/__init__.py
  92. 123 0
      ssat_sdk/service/ccard_service.py
  93. 138 0
      ssat_sdk/service/chroma_22293_service.py
  94. 94 0
      ssat_sdk/service/redrat_service.py
  95. 112 0
      ssat_sdk/service/service_config.py
  96. 249 0
      ssat_sdk/service/service_manager.py
  97. 148 0
      ssat_sdk/service/sound_service.py
  98. 72 0
      ssat_sdk/service/tg39_service.py
  99. 2 0
      ssat_sdk/sound/__init__.py
  100. 469 0
      ssat_sdk/sound/audio_analysis.py

+ 176 - 0
ssat_sdk/MenuTree3/TConfig.py

@@ -0,0 +1,176 @@
+# -*- coding:utf-8 -*-
+import string
+import os
+import sys
+import time
+from configparser import ConfigParser
+import json
+
+
+# import ConfigParser
+
+
+# class MyConfigParser(ConfigParser.ConfigParser):
+#     def __init__(self, defaults=None):
+#         ConfigParser.ConfigParser.__init__(self, defaults=defaults)
+#
+#     def optionxform(self, optionstr):
+#         return optionstr
+
+class TConfig:
+    def __init__(self, ini_path):
+        self.path = ini_path
+        self.cfgParser = ConfigParser()
+        # self.cfgParser = MyConfigParser()
+        # self.cfgParser.read(ini_path)
+
+    # 保存修改;
+    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)
+
+    def getParentWaitTime(self, parent):
+        optionName = parent + "WaitTime"
+        try:
+            waitTime = float(self.get_value("waitTime", optionName))
+        except Exception:
+            waitTime = 1.0
+            print "获取%s界面的等待界面时间失败,使用默认值1.0s" % str(parent)
+        return waitTime
+
+    def getThresholdDict(self, option):
+        section = "Threshold"
+        thresholdDict = {}
+        value = self.get_value(section, option)
+        print "value:", value, type(value)
+        if not value:
+            return thresholdDict
+        value_str = str(value)
+        print "value_str:", value_str, type(value_str)
+        value_dict = eval(value_str)
+        print "value_dict:", value_dict, type(value_dict)
+        return value_dict
+
+
+if __name__ == "__main__":
+    # tconfig = TConfig(r'E:\SAT\SAT_API\ssat_sdk\MenuTree3\MenuTree.ini')
+    tconfig = TConfig(r'E:\SAT\resource\MenuTree\NT72\DVB_nafrican\MenuTree.ini')
+    # print tconfig.get_options('First')
+    # print tconfig.get_sections()
+    # print tconfig.get_items('First')
+    value = tconfig.get_value('value', 'Setting')
+    print value
+    # print tconfig.get_dict(value)
+    # print tconfig.set_value('First', 'fffff','aaaa')
+    # tconfig.del_option('First','fffff')
+    # tconfig.del_section('Second')
+    # tconfig.set_value('second','setting','{"offset":10, "minPeri":0, "maxPeri":0, "minArea":0, "maxArea":0}')

+ 58 - 0
ssat_sdk/MenuTree3/TData.py

@@ -0,0 +1,58 @@
+# -*- coding:utf-8 -*-
+from ssat_sdk.MenuTree3.TExcelParser import CExcelParser
+
+from ssat_sdk import getMenuTree3SelectedProjectCfgPath, getMenuTree3SelectedPExcelPath
+
+
+class CTData():
+    def __init__(self):
+        # excel路径,根据界面选择的项目来确定;
+        # self.xlspath = getMenuTree3SelectedProjectCfgPath() + '\\MenuTree.xls'
+        self.xlspath = getMenuTree3SelectedPExcelPath()
+        # excel解析器对象;
+        self.xlsparser = CExcelParser(self.xlspath)
+        # 读取excel并解析出内容;
+        self.xlsparser.read_excel()
+        # 新增表格查询接口
+
+    # 获取表格Value层级中,value_name下包含的value列表
+    def getSubValueList(self, value_name):
+        return self.xlsparser.getSubValueList(value_name)
+
+    # 获取表格First~Sixth层级中,parent下包含的option列表
+    def getSubOptionList(self, parent):
+        return self.xlsparser.getSubOptionList(parent)
+
+    # 获取表格Value层级中,value_name下的指定value的ocr列表
+    def getValueTextList(self, value_name, value):
+        return self.xlsparser.getValueTextList(value_name, value)
+
+    # 获取表格First~Sixth层级中,parent下的指定option的ocr列表
+    def getOptionTextList(self, parent, option):
+        return self.xlsparser.getOptionTextList(parent, option)
+
+
+if __name__ == "__main__":
+    tData = CTData()
+
+    value_name = "source"
+    subValueList = tData.getSubValueList(value_name)
+    print u"获取Value层级中value_name为source下包含的value列表:", subValueList
+    # 执行结果:[u'atv', u'dtv', u'tv', u'av', u'hdmi1', u'hdmi2', u'hdmi3', u'usb']
+
+    parent = "picture"
+    subOptionList = tData.getSubOptionList(parent)
+    print u"获取First~Sixth层级中parent为picture下包含的value列表:", subOptionList
+    # 执行结果:[u'picture_preset', u'backlight', u'overscan', u'content_type', u'screen_mode', u'advanced_settings_picture',u'picture_reset']
+
+    value_name = "source"
+    value = "atv"
+    valueTextList = tData.getValueTextList(value_name, value)
+    print u"获取表格Value层级中,value_name:source 下的value:atv 的ocr列表:", valueTextList
+    # 执行结果:[u'tv', u'atv']
+
+    parent = "picture"
+    option = "picture_reset"
+    optionTextList = tData.getOptionTextList(parent, option)
+    print u"获取表格First~Sixth层级中,parent:picture  下的option:picture_reset的ocr列表:", optionTextList
+    # 执行结果:[u'picture reset']

+ 884 - 0
ssat_sdk/MenuTree3/TExcelParser.py

@@ -0,0 +1,884 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+import xlrd
+import xlwt
+import json
+# 字典不排序;
+from collections import OrderedDict
+
+from xlwt import XFStyle, Pattern
+
+g_level = ['First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth', 'Seventh', 'Eighth', 'Ninth', 'Tenth', 'Eleventh',
+           'Twelfth']
+
+
+class CPathParams():
+    def __init__(self):
+        self.paths = OrderedDict()
+
+    def add_name(self, level, parent, move_key, enter_key, others):
+        if level not in self.paths:
+            self.paths[level] = {}
+        # endif
+        move_key = move_key.replace(' ', '')
+        self.paths[level][parent] = {"move_key": move_key.split(';'), "enter_key": enter_key, "others": others,
+                                     "value": []}
+
+    def add_item(self, level, parent, option, ocr, move_key, enter_key, others):
+        if level in self.paths:
+            if parent not in self.paths[level]:
+                self.paths[level][parent] = {}
+        else:
+            self.paths[level] = {}
+            self.paths[level][parent] = {}
+        self.paths[level][parent]["value"].append(
+            {"option": option, "option_for_ocr": ocr, "option_move_key": move_key, "option_enter_key": enter_key,
+             "option_others": others})
+
+
+class CValueParams():
+    def __init__(self):
+        self.values = OrderedDict()
+
+    def add_name(self, name, move_key, enter_key, others):
+        if name not in self.values:
+            move_key = move_key.replace(' ', '')
+            self.values[name] = {"move_key": move_key.split(';'), "enter_key": enter_key, "others": others, "value": []}
+
+    def add_item(self, name, option, ocr):
+        if name in self.values:
+            self.values[name]["value"].append({"value": option, "value_for_ocr": ocr})
+
+
+class CExcelParser():
+    def __init__(self, xls_path):
+        if type(xls_path) == str:
+            xls_path = xls_path.decode('utf-8')
+        self.xls_path = xls_path
+        self.pathParams = CPathParams()
+        self.valueParams = CValueParams()
+
+    def read_excel(self, path=None):
+        if path is not None:
+            if type(path) == str:
+                path = path.decode('utf-8')
+            self.xls_path = path
+
+        # 打开文件;
+        wb = xlrd.open_workbook(filename=self.xls_path)
+        if wb is None:
+            return
+
+        # 获取所有sheet;
+        sheet = None
+        for sh_name in wb.sheet_names():
+            sheet = wb.sheet_by_name(sh_name)
+            self.parse_excel(sheet, False)
+
+        # print "self.valueParams.values:", self.valueParams.values, type(self.valueParams.values)
+        # print "self.pathParams.paths:", self.pathParams.paths, type(self.pathParams.paths)
+
+        # try:
+        #     print json.dumps(self.pathParams.paths)
+        #     print '\r\n'
+        #     print json.dumps(self.valueParams.values)
+        #     print '\r\n'
+        #     # print self.pathParams.paths,'\r\n', self.valueParams.values
+        # except Exception, e:
+        #     print e
+
+    # endfun
+
+    def parse_excel(self, sheet, bpath=True):
+        last_name = None
+        if u"Value" == sheet.name:
+            for i in range(1, sheet.nrows):
+                # 获取每行内容;
+                rows = tuple(sheet.row_values(i))
+                if rows[1].__len__() > 0 and rows[2].__len__() > 0 and last_name.__len__() > 0:
+                    # print sheet.name,"last_name:",last_name
+                    self.valueParams.add_item(last_name, rows[1], rows[2])
+                elif rows[0].__len__() > 0:
+                    last_name = rows[0]
+                    # print sheet.name, "last_name:", last_name
+                    self.valueParams.add_name(last_name, rows[3], rows[4], rows[5])
+        else:  # 路径;
+            for i in range(1, sheet.nrows):
+                # 获取每行内容;
+                rows = tuple(sheet.row_values(i))
+                if rows[1].__len__() > 0 and rows[2].__len__() > 0 and last_name.__len__() > 0:
+                    move_key = rows[3].split(';')
+                    self.pathParams.add_item(sheet.name, last_name, rows[1], rows[2], move_key, rows[4], rows[5])
+                elif rows[0].__len__() > 0:
+                    last_name = rows[0]
+                    self.pathParams.add_name(sheet.name, last_name, rows[3], rows[4], rows[5])
+
+    # endfun
+
+    def get_menu_paths(self, option, value):
+        if type(option) == str:
+            option = unicode(option)
+        if type(value) == str:
+            value = unicode(value)
+        # pp必须不排序;
+        vp, pp = {}, OrderedDict()
+        if option in self.valueParams.values:
+            vp = {"option": option, "value": value, "enter_key": self.valueParams.values[option]["enter_key"],
+                  "move_key": self.valueParams.values[option]["move_key"]}
+            # 找到value对应的ocr;
+            for item in self.valueParams.values[option]["value"]:
+                if "range(" in item["value"]:
+                    vp["value_for_ocr"] = item["value_for_ocr"].split(';')
+                    break
+                elif item["value"].lower() == value.lower():
+                    vp["value_for_ocr"] = item["value_for_ocr"].split(';')
+                    break
+
+        # 首先,字典不排序,需要倒序;
+        paths = reversed(self.pathParams.paths)
+        for level in paths:
+            found = False
+            for parent in self.pathParams.paths[level]:
+                # print parent,self.pathParams.paths[level][parent],'\r\n'
+                for item in self.pathParams.paths[level][parent]["value"]:
+                    # print item
+                    if item["option"].lower() == option.lower():
+                        pp[level] = {
+                            "parent": parent,
+                            "move_key": self.pathParams.paths[level][parent]["move_key"],
+                            "enter_key": self.pathParams.paths[level][parent]["enter_key"],
+                            "others": self.pathParams.paths[level][parent]["others"],
+                            "option": item["option"],
+                            "option_for_ocr": item["option_for_ocr"].split(';'),
+                            "option_move_key": item["option_move_key"],
+                            "option_enter_key": item["option_enter_key"],
+                            "option_others": item["option_others"]
+                        }
+                        option = parent
+                        found = True
+                        break
+                if found:
+                    break
+                else:
+                    if parent == option:
+                        for item in self.pathParams.paths[level][parent]["value"]:
+                            # print item
+                            if item["option"].lower() == value.lower():
+                                pp[level] = {
+                                    "parent": parent,
+                                    "move_key": self.pathParams.paths[level][parent]["move_key"],
+                                    "enter_key": self.pathParams.paths[level][parent]["enter_key"],
+                                    "others": self.pathParams.paths[level][parent]["others"],
+                                    "option": item["option"],
+                                    "option_for_ocr": item["option_for_ocr"].split(';'),
+                                    "option_move_key": item["option_move_key"],
+                                    "option_enter_key": item["option_enter_key"],
+                                    "option_others": item["option_others"]
+                                }
+                                option = parent
+                                found = True
+                                break
+                            # endif
+                        # endfor
+                        break
+                    # endif
+        # 需要对path倒序使用;
+        return vp, pp
+
+    def get_option_paths(self, option):
+        # pp必须不排序;
+        pp = OrderedDict()
+        # 首先,字典不排序,需要倒序;
+        paths = reversed(self.pathParams.paths)
+        for level in paths:
+            found = False
+            for parent in self.pathParams.paths[level]:
+                # print parent,self.pathParams.paths[level][parent],'\r\n'
+                for item in self.pathParams.paths[level][parent]["value"]:
+                    # print item
+                    if item["option"].lower() == option.lower():
+                        pp[level] = {
+                            "parent": parent,
+                            "move_key": self.pathParams.paths[level][parent]["move_key"],
+                            "enter_key": self.pathParams.paths[level][parent]["enter_key"],
+                            "others": self.pathParams.paths[level][parent]["others"],
+                            "option": item["option"],
+                            "option_for_ocr": item["option_for_ocr"].split(';'),
+                            "option_move_key": item["option_move_key"],
+                            "option_enter_key": item["option_enter_key"],
+                            "option_others": item["option_others"]
+                        }
+                        option = parent
+                        found = True
+                        break
+                if found:
+                    break
+                else:
+                    if parent == option:
+                        for item in self.pathParams.paths[level][parent]["value"]:
+                            # print item
+                            if item["option"].lower() == option.lower():
+                                pp[level] = {
+                                    "parent": parent,
+                                    "move_key": self.pathParams.paths[level][parent]["move_key"],
+                                    "enter_key": self.pathParams.paths[level][parent]["enter_key"],
+                                    "others": self.pathParams.paths[level][parent]["others"],
+                                    "option": item["option"],
+                                    "option_for_ocr": item["option_for_ocr"].split(';'),
+                                    "option_move_key": item["option_move_key"],
+                                    "option_enter_key": item["option_enter_key"],
+                                    "option_others": item["option_others"]
+                                }
+                                option = parent
+                                found = True
+                                break
+                            # endif
+                        # endfor
+                        break
+                    # endif
+            # endfor
+        # endfor
+        # 需要对path倒序使用;
+        # dict_pp = OrderedDict()
+        # # 逆序路径key;
+        # revpp = reversed(pp)
+        # for path in revpp:
+        #     dict_pp[path] = {"parent": pp[path]['parent'],"move_key": pp[path]['move_key'],"enter_key": pp[path]['enter_key'],"others":pp[path]['others'],"option": pp[path]['option'],"option_for_ocr": pp[path]['option_for_ocr']}
+        # # 返回逆序后的结果;
+        # return dict_pp
+        return pp
+
+    # 获取键对值
+    def get_pair_values(self, name, option=True):
+        if type(name) == str:
+            name = unicode(name)
+        pairs = []
+        if option is True:
+            find = False
+            for level in self.pathParams.paths:
+                for item in self.pathParams.paths[level]:
+                    if item.lower() == name.lower():
+                        find = True
+                        for item in self.pathParams.paths[level][name]["value"]:
+                            pairs.append({item['option']: item['option_for_ocr'].split(';')})
+                        break
+                # endfor
+                if find is True:
+                    break
+            # endfor
+        else:
+            if name in self.valueParams.values:
+                for item in self.valueParams.values[name]["value"]:
+                    pairs.append({item['value']: item['value_for_ocr'].split(';')})
+        # endif
+        return pairs
+
+    # 获取指定层级的path或value的xxx_for_ocr列表;
+    def get_ocr_list(self, level, name, option, is_option=True):
+        if type(name) == str:
+            name = unicode(name)
+        if type(option) == str:
+            option = unicode(option)
+        list_ocr = []
+        if is_option is True:
+            find = False
+            for item in self.pathParams.paths[level]:
+                if item.lower() == name.lower():
+                    find = True
+                    for item in self.pathParams.paths[level][name]["value"]:
+                        if str(option).lower() != str(item["option"]).lower():
+                            list_ocr.append(item['option_for_ocr'].split(';'))
+                    break
+            # endfor
+        else:
+            if name in self.valueParams.values:
+                for item in self.valueParams.values[name]["value"]:
+                    list_ocr.append(item['value_for_ocr'].split(';'))
+        # endif
+        return list_ocr
+
+    # 获取指定value_name所在菜单项的全部同级菜单项ocr列表;
+    def get_parent_ocr_list(self, option):
+        # 编码转换;
+        if type(option) == str:
+            option = unicode(option)
+
+        found = False
+        list_ocr = []
+        # 首先,字典不排序,需要倒序;
+        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() == option.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"]:
+                list_ocr.extend(val['option_for_ocr'].split(';'))
+        # 按长度排序;
+        list_ocr.sort(key=lambda i: len(i), reverse=True)
+        # 返回结果;
+        return list_ocr
+
+    # 当value_sheet = False,表示获取path表里的option层级的所有option_for_ocr:option
+    # 当value_sheet = True,表示获取value表里的value_name层级的所有value_for_ocr:value
+    def get_parent_ocr_dict(self, option, value_sheet=False):
+        print "get_parent_ocr_dict.option:", option
+        # 编码转换;
+        if type(option) == str:
+            option = unicode(option)
+
+        found = False
+        dict_ocr = {}
+        list_ocr = []
+        if value_sheet is False:
+            # 首先,字典不排序,需要倒序;
+            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() == option.lower():
+                            found = True
+                            break
+                    # endfor
+                    # 找到退出;
+                    if found is True:
+                        break
+                # endfor
+                # 找到退出;
+                if found is True:
+                    break
+
+            # 遍历value列表;
+            if found is True:
+                for val in self.pathParams.paths[level][parent]["value"]:
+                    list_ocr = val['option_for_ocr'].split(';')
+                    for ocr in list_ocr:
+                        dict_ocr[ocr.lower()] = val['option']
+        else:
+            if option in self.valueParams.values:
+                for val in self.valueParams.values[option]["value"]:
+                    list_ocr = val['value_for_ocr'].split(';')
+                    for ocr in list_ocr:
+                        dict_ocr[ocr.lower()] = val['value']
+
+        print 'unsorted=', dict_ocr
+        # 按长度排序;
+        list_ocr = sorted(dict_ocr.keys(), key=lambda key: len(key), reverse=True)
+        dict_reuslt = OrderedDict()
+        for ocr in list_ocr:
+            dict_reuslt[ocr] = dict_ocr[ocr]
+        # 返回结果;
+        return dict_reuslt
+
+    def get_option_ocr(self, option):
+        # 编码转换;
+        if type(option) == str:
+            option = unicode(option)
+
+        found = False
+        list_ocr = []
+        # 首先,字典不排序,需要倒序;
+        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() == option.lower():
+                        list_ocr = vals['option_for_ocr'].split(';')
+                        found = True
+                        break
+                # endfor
+                # 找到退出;
+                if found is True:
+                    break
+            # endfor
+            # 找到退出;
+            if found is True:
+                break
+        # endfor
+        # 返回结果;
+        return list_ocr
+
+    def get_value(self, option, value=""):
+        # 编码转换;
+        if type(option) == str:
+            option = unicode(option)
+        xlsValue = self.valueParams.values[option]["value"]
+        valueList = []
+        valueOcrList = []
+        for item in xlsValue:
+            valueList.append(item["value"])
+            valueOcrList.append(item["value_for_ocr"])
+        vp = {}
+        if option in self.valueParams.values:
+            vp = {"option": option, "value": valueList, "value_for_ocr": valueOcrList,
+                  "enter_key": self.valueParams.values[option]["enter_key"],
+                  "move_key": self.valueParams.values[option]["move_key"],
+                  "others": self.valueParams.values[option]['others']}
+            # 找到value对应的ocr;
+            if value != "":
+                vp["value"] = value
+                for item in self.valueParams.values[option]["value"]:
+                    if "range(" in item["value"]:
+                        vp["value_for_ocr"] = item["value_for_ocr"].split(';')
+                        break
+                    elif item["value"].lower() == value.lower():
+                        vp["value_for_ocr"] = item["value_for_ocr"].split(';')
+                        break
+                # endfor
+            # endif
+        # endif
+        # 返回结果;
+        return vp
+
+    # 获取option层级内容;
+    def get_option(self, option):
+        # 编码转换;
+        if type(option) == str:
+            option = unicode(option)
+
+        found = False
+        layers = 0
+        dict_option = {}
+        # 首先,字典不排序,需要倒序;
+        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() == option.lower():
+                        if dict_option.__len__() == 0:
+                            found = True
+                            dict_option['level'] = level
+                            dict_option['parent'] = parent
+                            dict_option['others'] = self.pathParams.paths[level][parent]['others']
+                            dict_option['enter_key'] = self.pathParams.paths[level][parent]['enter_key']
+                            dict_option['move_key'] = self.pathParams.paths[level][parent]['move_key']
+                            dict_option['option_ocr'] = vals['option_for_ocr'].split(';')
+                            dict_option['option_move_key'] = vals['option_move_key']
+                            dict_option['option_enter_key'] = vals['option_enter_key']
+                            dict_option['option_others'] = vals['option_others']
+                        option = parent
+                        layers += 1  # 层数;
+                        break
+                # endfor
+            # endfor
+        # endfor
+        if found is True:
+            dict_option['layers'] = layers
+            # first层次的parent名称;
+            dict_option['first_parent'] = option
+        # 返回结果;
+        return found, dict_option
+
+    #
+    def getRepeatValueName_ValueSheet(self):
+        wb = xlrd.open_workbook(filename=self.xls_path)
+        valueSheet = wb.sheet_by_name("Value")
+        rows = valueSheet.nrows  # 获取行数
+        all_value_option_list = []
+        RepeatValueName_ValueSheet_List = []
+
+        for i in range(1, rows):
+            # 获取每行内容;
+            rows = tuple(valueSheet.row_values(i))
+            if rows[0].__len__() > 0:
+                if rows[0] in all_value_option_list:
+                    RepeatValueName_ValueSheet_List.append([rows[0], "Value"])
+                else:
+                    all_value_option_list.append(rows[0])
+        print "RepeatValueName_ValueSheet_List:", RepeatValueName_ValueSheet_List
+        return RepeatValueName_ValueSheet_List
+
+    def getRepeatOption_otherSheet(self):
+        wb = xlrd.open_workbook(filename=self.xls_path)
+        all_option_list = []
+        repeatOption_otherSheet_List = []
+        for sh_name in wb.sheet_names():
+            sheet = wb.sheet_by_name(sh_name)
+            if u"Value" == sheet.name:
+                pass
+            else:  # 路径;
+                for i in range(1, sheet.nrows):
+                    # 获取每行内容;
+                    rows = tuple(sheet.row_values(i))
+                    if rows[1].__len__() > 0:
+                        if rows[1] in all_option_list:
+                            repeatOption_otherSheet_List.append([rows[1], sheet.name])
+                            # print "rows[1]:", rows[1], "sheetName:", sheet.name
+                        else:
+                            all_option_list.append(rows[1])
+        print "repeatOption_otherSheet_List:", repeatOption_otherSheet_List
+        return repeatOption_otherSheet_List
+
+    def getRepeatOptionList(self):
+        RepeatOptionList = []
+        repeatValueName_ValueSheet = self.getRepeatValueName_ValueSheet()
+        repeatOption_otherSheet = self.getRepeatOption_otherSheet()
+        RepeatOptionList = repeatOption_otherSheet + repeatValueName_ValueSheet
+        print "RepeatOptionList:", RepeatOptionList
+        return RepeatOptionList
+
+    def getValueSheetOptionList(self):
+        wb = xlrd.open_workbook(filename=self.xls_path)
+        valueSheet = wb.sheet_by_name("Value")
+        rows = valueSheet.nrows  # 获取行数
+        all_value_option_list = []
+
+        for i in range(1, rows):
+            # 获取每行内容;
+            rows = tuple(valueSheet.row_values(i))
+            if rows[0].__len__() > 0:
+                all_value_option_list.append(rows[0])
+        print "all_value_option_list:", all_value_option_list
+        return all_value_option_list
+
+    def checkOptionPath(self, optionName):
+        optionPath = ""
+        optionPathResult = "Fail"
+        path_params = self.get_option_paths(optionName)
+        # 如果传入的option不存在则直接返回
+        if path_params.__len__() <= 0:
+            optionPathResult = "NoExit"
+            optionPath = u"表格中不存在到达Option:   %s   的路径" % str(optionName)
+            return optionPath, optionPathResult
+        else:
+            optionPath = str(path_params)
+            # 逆序路径key;
+            revpp = reversed(path_params)
+            level_count = 0
+            for level in revpp:
+                if str(level) == str(g_level[level_count]):
+                    level_count = level_count + 1
+                else:
+                    # 如果执行路径不是按照First、Second......执行则返回执行出错
+                    return optionPath, optionPathResult
+            optionPathResult = "Pass"
+            return optionPath, optionPathResult
+
+    def checkRunOptionPath(self, path_params):
+        optionPathResult = "Fail"
+        # 如果传入的option不存在则直接返回
+        if path_params.__len__() <= 0:
+            optionPathResult = "NoExit"
+            return optionPathResult
+        else:
+            optionPath = str(path_params)
+            # 逆序路径key;
+            revpp = reversed(path_params)
+            level_count = 0
+            for level in revpp:
+                if str(level) == str(g_level[level_count]):
+                    level_count = level_count + 1
+                else:
+                    # 如果执行路径不是按照First、Second......执行则返回执行出错
+                    return optionPathResult
+            optionPathResult = "Pass"
+            return optionPathResult
+
+    def getCheckOptionPathExcelData(self):
+        checkOptionPathExcelData = []
+        checkOptionPathExcelData_Fail = []
+        checkOptionPathExcelData_NoExit = []
+        checkOptionPathExcelData_Pass = []
+        valueSheetOptionList = self.getValueSheetOptionList()
+        for optionName in valueSheetOptionList:
+            optionPath, optionPathResult = self.checkOptionPath(optionName)
+            if str(optionPathResult) == "Fail":
+                checkOptionPathExcelData_Fail.append([optionName, optionPathResult, optionPath])
+            if str(optionPathResult) == "NoExit":
+                checkOptionPathExcelData_NoExit.append([optionName, optionPathResult, optionPath])
+            if str(optionPathResult) == "Pass":
+                checkOptionPathExcelData_Pass.append([optionName, optionPathResult, optionPath])
+
+            # checkOptionPathExcelData.append([optionName, optionPathResult, optionPath])
+        checkOptionPathExcelData = checkOptionPathExcelData_Fail + checkOptionPathExcelData_NoExit + checkOptionPathExcelData_Pass
+        return checkOptionPathExcelData
+
+    def saveExcelData(self, checkOptionPathExcelData, repeatOptionList, all_UI_TestLanguagePath):
+
+        # 设置字体
+        font = xlwt.Font()
+        font.bold = True
+
+        # 设置边框
+        borders = xlwt.Borders()
+        borders.left = xlwt.Borders.THIN
+        borders.right = xlwt.Borders.THIN
+        borders.top = xlwt.Borders.THIN
+        borders.bottom = xlwt.Borders.THIN
+
+        # 设置居中
+        alignment = xlwt.Alignment()
+        # alignment.horz = xlwt.Alignment.HORZ_CENTER  # 水平方向
+        alignment.horz = xlwt.Alignment.HORZ_LEFT  # 水平方向
+        alignment.vert = xlwt.Alignment.VERT_TOP  # 垂直方向
+        alignment.wrap = 1
+
+        # 设置背景颜色
+        pattern = xlwt.Pattern()
+        pattern.pattern = xlwt.Pattern.SOLID_PATTERN
+        pattern.pattern_fore_colour = 3  # 背景颜色
+
+        # 定义不同的excel style
+        style1 = xlwt.XFStyle()
+        style1.font = font
+        style1.borders = borders
+        style1.alignment = alignment
+
+        style2 = xlwt.XFStyle()
+        style2.borders = borders
+        style2.alignment = alignment
+
+        style = XFStyle()
+        style.borders = borders
+        style.alignment = alignment
+        pattern = Pattern()
+        pattern.pattern = Pattern.SOLID_PATTERN
+        pattern.pattern_fore_colour = xlwt.Style.colour_map['red']  # 设置单元格背景色为黄色
+        style.pattern = pattern
+
+        # style_align = xlwt.easyxf('align: wrap on')
+
+        if os.path.exists(all_UI_TestLanguagePath):
+            try:
+                os.remove(all_UI_TestLanguagePath)
+            except Exception, e:
+                print e
+                return False
+        # 创建工作簿;
+        book = xlwt.Workbook(encoding='utf-8')
+        # 创建sheet;
+        sheet = book.add_sheet(u'到达Option路径检测', cell_overwrite_ok=True)
+        # 设置表格自适应宽度的数组
+        col_width = [2, 25, 15, 120]
+        # 创建标题行;
+        row0 = [u'序号', u'Option名称', u'检测到达路径结果', u'到达Option路径']
+
+        for i in range(len(row0)):
+            sheet.write(0, i, row0[i], style=style1)
+        index = 1
+
+        for checkdata in checkOptionPathExcelData:
+            sheet.write(index, 0, index, style=style2)
+            for j in range(len(checkdata)):
+                # 如果结果不为Pass则在表格中标红
+                if j == 1 and checkdata[j] != 'Pass':
+                    sheet.write(index, j + 1, checkdata[j], style=style)
+                else:
+                    # if j == 1:
+                    #     sheet.write(index, j + 1, checkdata[j], style=style_align)
+                    # else:
+                    sheet.write(index, j + 1, checkdata[j], style=style2)
+
+                # if col_width[j] < self.len_byte(checkdata[j]):
+                #     col_width[j] = self.len_byte(checkdata[j])
+            index += 1
+        # 设置栏位宽度,栏位宽度小于10时候采用默认宽度
+        for i in range(len(col_width)):
+            if col_width[i] > 10:
+                sheet.col(i).width = 256 * (col_width[i] + 1)
+
+        sheet2 = book.add_sheet(u'Option查重', cell_overwrite_ok=True)
+        # 设置表格自适应宽度的数组
+        col_width2 = [2, 30, 30]
+        # 创建标题行;
+        row02 = [u'序号', u'重复Option名称', u'重复Option在源表格的sheet名称']
+
+        for i in range(len(row02)):
+            sheet2.write(0, i, row02[i], style=style1)
+        index2 = 1
+        for repeatOption in repeatOptionList:
+            sheet2.write(index2, 0, index2, style=style2)
+            for j in range(len(repeatOption)):
+                sheet2.write(index2, j + 1, repeatOption[j], style=style2)
+            index2 += 1
+        # 设置栏位宽度,栏位宽度小于10时候采用默认宽度
+        for i in range(len(col_width2)):
+            if col_width2[i] > 10:
+                sheet2.col(i).width = 256 * (col_width2[i] + 1)
+
+        # 保存xls;
+        try:
+            book.save(all_UI_TestLanguagePath)
+            return True
+        except Exception, e:
+            print e
+            return False
+
+    # 新增表格查询接口
+
+    # # 获取表格Value层级中,value_name下包含的value列表
+    # def getSubValueList(self, value_name):
+    #     valueList = []
+    #     if self.valueParams.values.has_key(value_name):
+    #         # print parser.valueParams.values["source"]
+    #         # print parser.valueParams.values["source"]["value"]
+    #         valueDictList = self.valueParams.values[value_name]["value"]
+    #         for valueDict in valueDictList:
+    #             valueList.append(valueDict["value"])
+    #     # print "valueList:", valueList, type(valueList)
+    #     return valueList
+
+    # 获取表格Value层级中,value_name下包含的value列表
+    def getSubValueList(self, value_name):
+        valueList = []
+        if self.valueParams.values.has_key(value_name):
+            # print parser.valueParams.values["source"]
+            # print parser.valueParams.values["source"]["value"]
+            valueDictList = self.valueParams.values[value_name]["value"]
+            for valueDict in valueDictList:
+                valueList.append(valueDict["value"])
+        # print "valueList:", valueList, type(valueList)
+        if valueList.__len__() == 1 and "range(" in valueList[0]:
+            value_for_ocr_low = str(valueList[0]).lower()
+            str_num = value_for_ocr_low.replace("range", "")
+            list_num = eval(str_num)
+            min_num = list_num[0]
+            max_num = list_num[1]
+            return [min_num, max_num]
+
+        return valueList
+
+    # 获取表格First~Sixth层级中,parent下包含的option列表
+    def getSubOptionList(self, parent):
+        optionList = []
+        paths = self.pathParams.paths
+        for level in paths:
+            if paths[level].has_key(parent):
+                optionDictList = paths[level][parent]["value"]
+                for optionDict in optionDictList:
+                    optionList.append(optionDict["option"])
+        # print "optionList:", optionList, type(optionList)
+        return optionList
+
+    # 获取表格Value层级中,value_name下的value的ocr列表
+    def getValueTextList(self, value_name, value):
+        valueTextList = []
+        if self.valueParams.values.has_key(value_name):
+            # print parser.valueParams.values["source"]
+            # print parser.valueParams.values["source"]["value"]
+            valueDictList = self.valueParams.values[value_name]["value"]
+            for valueDict in valueDictList:
+                if valueDict["value"] == value:
+                    value_for_ocr = valueDict["value_for_ocr"]
+                    # print "value_for_ocr:", value_for_ocr, type(value_for_ocr)
+                    valueTextList = value_for_ocr.split(';')
+
+        # print "valueTextList:", valueTextList, type(valueTextList)
+        return valueTextList
+
+    # 获取表格First~Sixth层级中,parent下的option的ocr列表
+    def getOptionTextList(self, parent, option):
+        optionTextList = []
+        paths = self.pathParams.paths
+        for level in paths:
+            if paths[level].has_key(parent):
+                optionDictList = paths[level][parent]["value"]
+                for optionDict in optionDictList:
+                    if optionDict["option"] == option:
+                        option_for_ocr = optionDict["option_for_ocr"]
+                        # print "option_for_ocr:", option_for_ocr, type(option_for_ocr)
+                        optionTextList = option_for_ocr.split(';')
+        # print "optionList:", optionTextList, type(optionTextList)
+        return optionTextList
+
+
+if __name__ == "__main__":
+    # path = r'E:\svn\SAT\项目资料\第四期-推广使用和画面检测\06-设计文档\01-蓝图设计\UiTree3.0方案\MenuTree_MS6586_EU.xls'
+    # path = r'E:\SAT\resource\MenuTree\MS3663\DVB-T2 panasonic\MenuTree.xls'
+    # path = r'E:/SAT/resource\MenuTree\MS6586\ATSC\MenuTree.xls'
+    path = r'E:\SAT\resource\MenuTree\MS6586\ATSC\MenuTree_DVBT.xls'
+    # path = r'E:\MenuTree.xls'
+    # path = r'E:\SAT\resource\MenuTree\6586\MS6586_ATSC\MenuTree.xls'
+    # path = getMenuTree3SelectedProjectCfgPath() + '\\MenuTree.xls'
+    parser = CExcelParser(path)
+    parser.read_excel()
+    dataDict = parser.get_parent_ocr_dict("network")
+    print "dataDict:", dataDict, type(dataDict)
+    # parent = "source"
+    # option = "atv"
+    # parser.getOptionTextList(parent, option)
+    # value_name = "source"
+    # value = "atv"
+    # parser.getValueTextList(value_name, value)
+    # SubValueList1 = parser.getSubValueList("source")
+    # SubValueList2 = parser.getSubValueList("backlight")
+    # parent = "picture"
+    # parent = "manual_tuning"
+    # parser.getSubOptionList(parent)
+    # optionList = []
+    # paths = parser.pathParams.paths
+    #
+    # for level in paths:
+    #     if paths[level].has_key(parent):
+    #         optionDictList = paths[level][parent]["value"]
+    #         for optionDict in optionDictList:
+    #             optionList.append(optionDict["option"])
+    # print "optionList:", optionList, type(optionList)
+
+    # print parser.get_value("source", "")
+    # checkOptionPathExcelData = parser.getCheckOptionPathExcelData()
+    # print "checkOptionPathExcelData:", checkOptionPathExcelData
+    # repeatOptionList = parser.getRepeatOptionList()
+    # isSaveExcelTrue = parser.saveExcelData(checkOptionPathExcelData, repeatOptionList,
+    #                                        r'E:\MenuTree_Check_Option_Result.xls')
+    # path = r'E:\SAT\resource\MenuTree\6586\MS6586_ATSC\MenuTree.xls'
+    # path = getMenuTree3SelectedProjectCfgPath() + '\\MenuTree.xls'
+
+    # print "isSaveExcelTrue:", isSaveExcelTrue
+    # valueSheetOptionList = parser.getValueSheetOptionList()
+
+    # repeatValueName_ValueSheet = parser.getRepeatValueName_ValueSheet()
+    # repeatOption_otherSheet = parser.getRepeatOption_otherSheet()
+
+    # optionPath, optionPathResult = parser.checkOptionPath("picture_preset11")
+    # print "optionPath:", optionPath, type(optionPath)
+    # print "optionPathResult:", optionPathResult, type(optionPathResult)
+    # optionPath, optionPathResult = parser.checkOptionPath("instant_power_on")
+    # print "optionPath:", optionPath, type(optionPath)
+    # print "optionPathResult:", optionPathResult, type(optionPathResult)
+
+    # path_params = parser.get_option_paths("picture_preset")
+    # print "path_params:", path_params, type(path_params)
+
+    # print "path_params[0][0]:",path_params[0][0]
+    # print parser.get_option_paths('picture')
+    # print parser.get_option('picture')
+    # print parser.get_value('backlight',10)
+    # print parser.get_parent_ocr_dict('source_hdmi1')
+    # print parser.get_parent_ocr_list('channel')
+    # print parser.get_parent_ocr_dict('channel')
+    # print parser.get_parent_ocr_dict('other_setting')
+    # print parser.get_parent_ocr_list('5khz')
+    # print parser.get_parent_ocr_dict('picture_preset', True)
+    # print parser.get_pair_values('picture_preset', False)
+    # print parser.get_pair_values('source')
+    # print parser.get_ocr_list('Second','picture','picture_preset')
+    # vp, pp = parser.get_menu_paths("auto_volume_control", "off")
+    # print vp
+    # vp, pp = parser.get_menu_paths("picture_preset", "dynamic")
+    # print vp
+    # pp = parser.get_option_paths('picture_preset')
+    # print pp.__len__(), pp[g_level[0]]
+    # print "\r\nvp:", vp, type(vp)
+    # print "\r\npp:", pp, type(pp)
+    # # print json.dumps(pp)
+    # revpp = reversed(pp)
+    # for item in revpp:
+    #     print item, pp[item]
+
+    # print 'value:\r\n'
+    # print vp
+    # # print "revpp:", revpp, type(revpp)

+ 264 - 0
ssat_sdk/MenuTree3/TFocus.py

@@ -0,0 +1,264 @@
+# -*- coding:utf-8 -*-
+from ssat_sdk import getMenuTree3SelectedPExcelPath
+
+from ssat_sdk.picture.RGB import RGBColor
+from ssat_sdk.picture.color_space import CIEluvCaculator
+from ssat_sdk.picture.feature_detect import FeatureDetect
+from ssat_sdk.picture.image_util import *
+
+import os
+import sys
+import time
+import numpy as np
+import cv2 as cv
+import json
+from ssat_sdk.MenuTree3.TConfig import TConfig
+from ssat_sdk.MenuTree3.TExcelParser import CExcelParser
+
+g_level = ['First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth',
+           'Seventh', 'Eighth', 'Ninth', 'Tenth', 'Eleventh', 'Twelfth']
+
+
+class TFocus():
+    def __init__(self, project_path, xlsparser=None, screen=[1920, 1080], icon_shape=[1920, 1080]):
+        self.params_path = project_path
+        self.rgbColor = RGBColor()
+        self.CIE = CIEluvCaculator()
+        self.feature_detect = FeatureDetect()
+        self.xlsparser = xlsparser
+        # icon分辨率width*height;
+        self.icon_shape = icon_shape
+        # 当前屏幕分辨率;
+        self.screen = screen
+        # 比率;
+        self.rate = self.icon_shape[0] / self.screen[0]
+
+    def findFocusByIcon(self, src_pic, icon_pic,
+                        dcfg={"offset": 20, "minPeri": 0, "maxPeri": 0, "minArea": 0, "maxArea": 0, "morphology": []}):
+        iconImg = cv.imread(icon_pic)
+        # 获取小图平均bgr值;
+        bgr = self.rgbColor.getAvgBGR(iconImg)
+        print "findFocusByIcon,bgr:%s, src_pic=%s, icon_pic=%s, dcfg=%s" % (
+            str(bgr), src_pic, icon_pic, str(dcfg))
+        if dcfg.has_key("morphology"):
+            morphology = dcfg["morphology"]
+        else:
+            morphology = []
+        return self.feature_detect.findCurrentFoucsByColor(
+            src_pic,
+            [],
+            bgr,
+            dcfg["offset"],
+            dcfg["minPeri"],
+            dcfg["maxPeri"],
+            dcfg["minArea"],
+            dcfg["maxArea"],
+            morphology)
+
+    # 文本颜色焦点区域查找;
+    def findFramelessBoxByIcon(self, src_pic, icon_pic, offset=20):
+        iconImg = cv.imread(icon_pic)
+        # 获取小图平均bgr值;
+        bgr = self.rgbColor.getAvgBGR(iconImg)
+        print "findFramelessBoxByIcon,bgr:%s, src_pic=%s, icon_pic=%s, offset=%s" % (
+            str(bgr), src_pic, icon_pic, str(offset))
+        return self.feature_detect.findFramelessFocusByColor(src_pic, [], bgr, offset)
+
+    '''
+        在传入的图片上,查找指定小图片bgr颜色框矩形区域,返回矩形区域的坐标
+        :param bigPic:图片路径
+        :param level:excel层次
+        :param first_parent:大部分情况下是根菜单;少部分情况是当前option的parent;
+        :return rectArea:矩形区域和区域图像内容
+        '''
+
+    def findRectByIcon(self, bigPic, level, first_parent, option=""):
+        print "level:", level
+        print "first_parent:", first_parent
+        if option.__len__() == 0:
+            icon_path = os.path.join(self.params_path, "icon\\", str(
+                first_parent) + "." + str(level) + ".png")
+        else:
+            icon_path = os.path.join(self.params_path, "icon\\", str(
+                first_parent) + "." + str(level) + "_" + option + ".png")
+        print "icon_path_option=", icon_path
+        if os.path.exists(icon_path) is False:
+            icon_path = os.path.join(self.params_path, "icon\\", str(
+                first_parent) + "." + str(level) + ".png")
+            print "icon_path=", icon_path
+            if os.path.exists(icon_path) is False:
+                icon_path = os.path.join(self.params_path, "icon\\", str(
+                    first_parent) + "." + str('First') + ".png")
+                print "icon_path_First=", icon_path
+                if os.path.exists(icon_path) is False:
+                    return False, []
+        # 获取配置文件值;
+        dcfg = {"offset": 20, "minPeri": 0,
+                "maxPeri": 0, "minArea": 0, "maxArea": 0}
+        ini_path = os.path.join(self.params_path, "menutree.ini")
+        if os.path.exists(ini_path) is True:
+            tconfig = TConfig(ini_path)
+            if tconfig.has_option(level, first_parent):
+                dcfg = tconfig.get_dict(tconfig.get_value(level, first_parent))
+
+        dcfg['minPeri'] = self.rate * dcfg['minPeri']
+        dcfg['maxPeri'] = self.rate * dcfg['maxPeri']
+        dcfg['minArea'] = pow(self.rate, 2) * dcfg['minArea']
+        dcfg['maxArea'] = pow(self.rate, 2) * dcfg['maxArea']
+        if "morphology" not in dcfg:
+            dcfg["morphology"] = []
+        return self.findFocusByIcon(bigPic, icon_path, dcfg)
+
+    '''
+    获取option配置信息;
+    '''
+
+    def getOptionConfig(self, cur_option, is_value_sheet=False):
+        # 获取Option的others字段信息;
+        if self.xlsparser is None:
+            self.xlsparser = CExcelParser(getMenuTree3SelectedPExcelPath())
+            # 读取excel并解析出内容;
+            self.xlsparser.read_excel()
+        # 获取option的路径表;
+        pp = self.xlsparser.get_option_paths(cur_option)
+        if pp.__len__() == 0:
+            print 'getOptionConfig.get_option_paths(%s) is null' % (cur_option)
+            return False, {"icon_path": "", "dcfg": {}, "dir_path": ""}
+
+        # 获取first_parent;
+        first_parent = pp[g_level[0]]['parent']
+        # 当前option所在level;
+        cur_level = g_level[pp.__len__() - 1]
+        # 当前父路径;
+        cur_parent = pp[cur_level]['parent']
+        if is_value_sheet is True:
+            cur_level = 'value'
+        # print first_parent, cur_level, cur_parent, cur_option
+
+        # 获取配置文件值;
+        dcfg = {}
+        ini_path = os.path.join(self.params_path, "menutree.ini")
+        tconfig = TConfig(ini_path)
+        # 首先找当前路径图;
+        icon_path = os.path.join(self.params_path, "icon\\", str(
+            cur_parent) + "." + str(cur_level) + "_" + cur_option + ".png")
+        icon_dir_path = os.path.join(self.params_path, "icon\\", str(
+            cur_parent) + "." + str(cur_level) + "_" + cur_option + ".dir.png")
+        dcfg = tconfig.get_value_dict(cur_level, cur_parent + '.' + cur_option)
+        print u"trying icon:%s"%icon_path
+        print u"trying dcfg:get_value_dict(%s, %s)"%(cur_level, cur_parent + '.' + cur_option)
+        # icon路径;
+        if os.path.exists(icon_path) is False:
+            print u'图标优先级0:[cur_parent].[cur_level]_[cur_option].png=%s is null' % (
+                icon_path)
+            # 当前次路径图;
+            icon_path = os.path.join(self.params_path, "icon\\", str(
+                cur_parent) + "." + str(cur_level) + ".png")
+            icon_dir_path = os.path.join(self.params_path, "icon\\", str(
+                cur_parent) + "." + str(cur_level) + ".dir.png")
+            dcfg = tconfig.get_value_dict(cur_level, cur_parent)
+            print u"trying icon:%s" % icon_path
+            print u"trying dcfg:get_value_dict(%s, %s)" % (cur_level, cur_parent)
+            if os.path.exists(icon_path) is False:
+                print u'图标优先级1:[cur_parent].[cur_level].png=%s is null' % (
+                    icon_path)
+                # first层;
+                icon_path = os.path.join(self.params_path, "icon\\", str(
+                    first_parent) + "." + str(cur_level) + "_" + cur_option + ".png")
+                icon_dir_path = os.path.join(self.params_path, "icon\\", str(
+                    first_parent) + "." + str(cur_level) + "_" + cur_option + ".dir.png")
+                dcfg = tconfig.get_value_dict(
+                    cur_level, first_parent + '.' + cur_option)
+                print u"trying icon:%s" % icon_path
+                print u"trying dcfg:get_value_dict(%s, %s)" % (cur_level, first_parent + '.' + cur_option)
+                if os.path.exists(icon_path) is False:
+                    print u'图标优先级2:[first_parent].[cur_level]_[cur_option].png=%s is null' % (
+                        icon_path)
+                    # first层次路径图;
+                    icon_path = os.path.join(self.params_path, "icon\\", str(
+                        first_parent) + "." + str(cur_level) + ".png")
+                    icon_dir_path = os.path.join(self.params_path, "icon\\", str(
+                        first_parent) + "." + str(cur_level) + ".dir.png")
+                    dcfg = tconfig.get_value_dict(cur_level, first_parent)
+                    print u"trying icon:%s" % icon_path
+                    print u"trying dcfg:get_value_dict(%s, %s)" % (cur_level, first_parent)
+                    if os.path.exists(icon_path) is False:
+                        print u'图标优先级3:[first_parent].[cur_level].png =%s is null' % (
+                            icon_path)
+                # endif;
+            # endif;
+        # endif;
+
+        if dcfg.__len__() == 0:
+            dcfg = {"offset": 20, "minPeri": 0,
+                    "maxPeri": 0, "minArea": 0, "maxArea": 0, "morphology": []}
+            print u"get dcfg fail!Using default para!"
+
+        if os.path.exists(icon_path) is False:
+            print 'getOptionConfig:current use icon_path=%s is null' % (
+                icon_path)
+            return False, {"icon_path": "", "dcfg": {}, "dir_path": ""}
+        print u"实际使用的icon图片路径icon_path:%s, 实际使用INI配置:%s" % (
+            str(icon_path), str(dcfg))
+
+        # 返回路径;
+        return True, {"icon_path": icon_path, "dcfg": dcfg, "dir_path": icon_dir_path}
+
+    def findRectByIcon2(self, src_pic, cur_option, is_value_sheet=False):
+        # 获取参数;
+        result, opcfg = self.getOptionConfig(cur_option, is_value_sheet)
+        if result is False:
+            return False, []
+
+        icon_path = opcfg['icon_path']
+        dcfg = opcfg['dcfg']
+        dcfg['minPeri'] = self.rate * dcfg['minPeri']
+        dcfg['maxPeri'] = self.rate * dcfg['maxPeri']
+        dcfg['minArea'] = pow(self.rate, 2) * dcfg['minArea']
+        dcfg['maxArea'] = pow(self.rate, 2) * dcfg['maxArea']
+        print u"实际使用的聚焦参数字典dcfg:%s" % str(dcfg)
+        if "morphology" not in dcfg:
+            dcfg["morphology"] = []
+
+        return self.findFocusByIcon(src_pic, icon_path, dcfg)
+
+
+def rgb2hsv(r, g, b):
+    r, g, b = r / 255.0, g / 255.0, b / 255.0
+    mx = max(r, g, b)
+    mn = min(r, g, b)
+    df = mx - mn
+    # h值
+    if mx == mn:
+        h = 0
+    elif mx == r and g >= b:
+        h = (60 * (g - b) / df) % 360
+    elif mx == r and g < b:
+        h = (60 * ((g - b) / df) + 360) % 360
+    elif mx == g:
+        h = (60 * ((b - r) / df) + 120) % 360
+    elif mx == b:
+        h = (60 * ((r - g) / df) + 240) % 360
+    # s值
+    if mx == 0:
+        s = 0
+    else:
+        s = df / mx * 255
+    # v值
+    v = mx * 255
+    # 返回;
+    return h, s, v
+
+
+if __name__ == "__main__":
+    # print '\n\n====>\n', pow(10, 2)
+    tFocus = TFocus(r"D:\SAT\resource\MenuTree\MS6586\ISDB")
+    tFocus.getOptionConfig('backlight', True)
+    tFocus.findRectByIcon2('', 'backlight')  # lock
+    if 0:
+        bigPic_2 = r"D:\CH01.JPG"
+        icon = r"D:\SAT\resource\MenuTree\mi\AM950_IND_V27N\result.png"
+        result, contourRect = tFocus.findRectByIcon(bigPic_2, 'Third', 'lock')
+        print "__name__,rect:", result, contourRect
+        if result is True:
+            saveCropPic(bigPic_2, icon, contourRect)

+ 2587 - 0
ssat_sdk/MenuTree3/TMenu.py

@@ -0,0 +1,2587 @@
+# -*- coding:utf-8 -*-
+from ssat_sdk.utils.LoggingUtil import printLog
+
+from ssat_sdk.utils.string_util import getDigitFromString
+
+from ssat_sdk.device_manage.capturecard_manager import CCardManager
+
+from ssat_sdk.MenuTree3.TExcelParser import CExcelParser
+from ssat_sdk import getMenuTree3SelectedProjectCfgPath, OCRConvert, getSATTmpDIR, ImageCMP, \
+    getMenuTree3SelectedPExcelPath, getMenuTree3SelectedTvExcelPath
+from ssat_sdk.MenuTree3.TFocus import TFocus
+from ssat_sdk.MenuTree3.TConfig import TConfig
+from ssat_sdk.MenuTree3.TSourceImpl import TSourceImpl
+from ssat_sdk.tv_operator import *
+import json
+import time
+import cv2 as cv
+from ssat_sdk.utils.string_util import strcmp
+from ssat_sdk.picture.DoubleImage import DoubleImage
+from ssat_sdk.source_input import SourceGenInput
+
+g_level = ['First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth', 'Seventh', 'Eighth', 'Ninth', 'Tenth', 'Eleventh',
+           'Twelfth']
+
+
+class CTMenu():
+    # def __init__(self, ocrDict={"lan": "CHN_ENG", "type": 10000}):
+    def __init__(self, ocrDict=[{"lan": "ChinesePRC+English", "type": 4}, {"lan": "ChinesePRC+English", "type": 253},
+                                {"lan": "ChinesePRC+English", "type": 10001}]):
+        # excel路径,根据界面选择的项目来确定;
+        # 非tv表格
+        self.xlspath = getMenuTree3SelectedPExcelPath()
+        #tv表格
+        self.tv_xlspath = getMenuTree3SelectedTvExcelPath()
+        printLog(u"构建CTMenu对象!非tv表格路径:%s" % str(self.xlspath))
+        printLog(u"tv表格路径:%s" % str(self.tv_xlspath))
+        # print "self.xlspath :", self.xlspath
+        # 创建视频采集对象
+        self.ccard = CCardManager()
+        # 创建OCR对象
+        self.ocr = OCRConvert()
+        # 图片切割对象
+        self.imgCMP = ImageCMP()
+        # excel解析器对象;
+        self.xlsparser = CExcelParser(self.xlspath)
+        # 解析非tv的menutree表格;
+        self.xlsparser.read_excel()
+        # 解析tv的menutree表格(存在tv menutree时才进行解析);
+        if self.xlspath != self.tv_xlspath:
+            self.xlsparser.read_excel(self.tv_xlspath)
+        # 读取excel并解析出内容;
+        # 红老鼠遥控对象;
+        self.redRat3 = TvOperator()
+        # 配置文件;
+        self.tconfig = TConfig(getMenuTree3SelectedProjectCfgPath() + "\\MenuTree.ini")
+        self.icon_shape = [1920, 1080]
+        if self.tconfig.has_option('Screen', 'shape'):
+            self.icon_shape = self.tconfig.get_value_dict('Screen', 'shape')
+
+        # 截图,获取分辨率;
+        self.Screen = [1920, 1080]
+        pic = self.getCurrentUIPath()
+        if os.path.exists(pic) is True:
+            img = cv.imread(pic)
+            if img is not None:
+                self.Screen = [img.shape[1], img.shape[0]]
+
+        # 创建获取聚焦区域对象
+        self.tFocus = TFocus(getMenuTree3SelectedProjectCfgPath(), self.xlsparser, self.Screen, self.icon_shape)
+        self.ocrDict = ocrDict
+        # 某数据是否读取过;
+        self.got_dict = {}
+        # 22293
+        self.sourceInput = SourceGenInput()
+        self.tSource = TSourceImpl(self.ocr, self.ccard, self.redRat3, self.tFocus, self.tconfig, self.xlsparser)
+
+    def currentTime(self):
+        return time.strftime('_%Y-%m-%d_%H_%M_%S', time.localtime(time.time()))
+
+    def get_ocr_list(self, level, option):
+        # OCR识别参数字典   默认值为:百度中英文免费类型
+        ini_path = os.path.join(getMenuTree3SelectedProjectCfgPath(), "menutree.ini")
+        if os.path.exists(ini_path) is True:
+            tconfig = TConfig(ini_path)
+            # if tconfig.has_option(level, parent + u'.ocr'):
+            #     self.ocrDict = tconfig.get_dict(tconfig.get_value(level, parent + u'.ocr'))
+            #     return
+            result, path = self.xlsparser.get_option(option)
+            print u"get_ocr_list, path:",path
+            if result:
+                # 从当前层逐层往上遍历,寻找ocr参数列表
+                for i in range(g_level.index(level), -1, -1):
+                    cur_parent = path['parent']
+                    first_parent = path['first_parent']
+                    i_level = g_level[i]
+                    if tconfig.has_option(i_level, option + u'.ocr'):
+                        self.ocrDict = tconfig.get_dict(tconfig.get_value(i_level, option + u'.ocr'))
+                        printLog("TMenu.get_ocr_list:", "Get ocr list by: %s.ocr"%option)
+                        break
+                    elif tconfig.has_option(i_level, cur_parent + u'.ocr'):
+                        self.ocrDict = tconfig.get_dict(tconfig.get_value(i_level, cur_parent + u'.ocr'))
+                        printLog("TMenu.get_ocr_list:", "Get ocr list by: %s.ocr"%cur_parent)
+                        break
+                    elif tconfig.has_option(i_level, first_parent + u'.ocr'):
+                        self.ocrDict = tconfig.get_dict(tconfig.get_value(i_level, first_parent + u'.ocr'))
+                        printLog("TMenu.get_ocr_list:", "Get ocr list by: %s.ocr"%first_parent)
+                        break
+        else:
+            # 如果没有,使用默认;
+            self.ocrDict = [{"lan": "ChinesePRC+English", "type": 4}, {"lan": "ChinesePRC+English", "type": 253},
+                            {"lan": "ChinesePRC+English", "type": 10001}]
+            printLog("TMenu.get_ocr_list:", "Get ocr list fail!!!Using default list!!!")
+
+        printLog("TMenu.get_ocr_list:", "Present ocr list:%s"%self.ocrDict)
+
+    # 截图并返回当前截图路径
+    def getCurrentUIPath(self):
+        current_uiPic = os.path.join(getSATTmpDIR(), "menutree_runpath.png")
+        self.ccard.takePicture(current_uiPic)
+        return current_uiPic
+
+    def getFocusTextBox(self, level, root, focus_box):
+        # 读取ini文件,判断是否有文字方向;
+        if self.tconfig.has_option(level, root + u".text.dir") is True:
+            dict_text = self.tconfig.get_dict(self.tconfig.get_value(level, root + u".text.dir"))
+            # 四个方向的起点坐标计算;
+            left, top, right, bottom = 0, 0, 0, 0
+            if dict_text["dir"] == u"up":
+                left = focus_box[0] + dict_text["x-offset"]
+                bottom = focus_box[1] + dict_text["y-offset"]
+                top = bottom - dict_text["height"]
+                right = left + dict_text["width"]
+            elif dict_text["dir"] == u"down":
+                left = focus_box[0] + dict_text["x-offset"]
+                top = focus_box[3] + dict_text["y-offset"]
+                right = left + dict_text["width"]
+                bottom = top + dict_text["height"]
+            elif dict_text["dir"] == u"left":
+                top = focus_box[1] + dict_text["y-offset"]
+                right = focus_box[0] + dict_text["x-offset"]
+                left = right - dict_text["width"]
+                bottom = top + dict_text["height"]
+            elif dict_text["dir"] == u"right":
+                left = focus_box[2] + dict_text["x-offset"]
+                top = focus_box[1] + dict_text["y-offset"]
+                right = left + dict_text["width"]
+                bottom = top + dict_text["height"]
+            # 返回文本坐标;
+            return [left, top, right, bottom]
+        # endif
+        # 如果没有字段原样返回;
+        return focus_box
+
+    def getFocusTextBox3(self, cur_option, focus_box, is_value_sheet=False):
+        resutl, opcfg = self.tFocus.getOptionConfig(cur_option, is_value_sheet)
+        dcfg = opcfg['dcfg']
+        icon_path = opcfg['icon_path']
+        icon_dir_path = opcfg['dir_path']
+        if resutl is False or os.path.exists(icon_dir_path) is False:
+            print u'getFocusTextBox:current use dir_path=%s is null' % (
+                icon_dir_path)
+            return focus_box
+
+        if icon_dir_path in self.got_dict:
+            x, y = focus_box[0] - self.got_dict[icon_dir_path]['ref_box'][0], focus_box[1] - \
+                   self.got_dict[icon_dir_path]['ref_box'][1]
+            return [x, y, x + self.got_dict[icon_dir_path]['width'], y + self.got_dict[icon_dir_path]['height']]
+        else:
+            if os.path.exists(icon_dir_path) is True:
+                # 读取例图,宽高;
+                img = cv.imread(icon_dir_path)
+                # 在例图中查找轮廓;
+                result, box = self.tFocus.findRectByIcon2(icon_dir_path, cur_option, is_value_sheet)
+                if result is True:
+                    self.got_dict[icon_dir_path] = {"ref_box": box, "width": img.shape[1], "height": img.shape[0]}
+                    x, y = focus_box[0] - box[0], focus_box[1] - box[1]
+                    return [x, y, x + img.shape[1], y + img.shape[0]]
+                # endif
+            # endif
+        # 如果没有字段原样返回;
+        return focus_box
+
+    def getFocusTextBox4(self, icon_dir_path, cur_option, focus_box, is_value_sheet=False):
+        if os.path.exists(icon_dir_path) is False:
+            print u'getFocusTextBox:current use dir_path=%s is null' % (
+                icon_dir_path)
+            return focus_box
+
+        if icon_dir_path in self.got_dict:
+            x, y = focus_box[0] - self.got_dict[icon_dir_path]['ref_box'][0], focus_box[1] - \
+                   self.got_dict[icon_dir_path]['ref_box'][1]
+            return [x, y, x + self.got_dict[icon_dir_path]['width'], y + self.got_dict[icon_dir_path]['height']]
+        else:
+            if os.path.exists(icon_dir_path) is True:
+                # 读取例图,宽高;
+                img = cv.imread(icon_dir_path)
+                # 在例图中查找轮廓;
+                result, box = self.tFocus.findRectByIcon2(icon_dir_path, cur_option, is_value_sheet)
+                if result is True:
+                    self.got_dict[icon_dir_path] = {"ref_box": box, "width": img.shape[1], "height": img.shape[0]}
+                    x, y = focus_box[0] - box[0], focus_box[1] - box[1]
+                    return [x, y, x + img.shape[1], y + img.shape[0]]
+                # endif
+            # endif
+        # 如果没有字段原样返回;
+        return focus_box
+
+    # 获取焦点框文本;
+    def getFocusText(self, level, first_parent, paths, src_pic, focus_box, is_for_value=False):
+        # 是否有文字方向字段存在在ini里,如果有则转换文本框坐标;
+        text_pic = os.path.join(getSATTmpDIR(), "meuttree_area_text.png")
+        contourRect = self.getFocusTextBox3(paths['option'], focus_box, is_for_value)
+        self.imgCMP.saveCropPic(src_pic, text_pic, (contourRect[0], contourRect[1], contourRect[2], contourRect[3]))
+        # 获取ocr_list;
+        cur_parent, cur_option = paths['parent'], paths['option']
+        ocr_list = self.xlsparser.get_ocr_list(level, cur_parent, cur_option,
+                                               True if cur_parent == cur_option else False)
+        print u"%s,%s,%s =>ocr_list=%s" % (level, cur_parent, cur_option, str(ocr_list))
+        # 遍历ocr类型;
+        found = False
+        ocr_str = ''
+        # 获取ocr列表;
+        self.get_ocr_list(level, cur_option)
+        for item in self.ocrDict:
+            # 识别ocr;
+            thresholdDict = self.tconfig.getThresholdDict(first_parent)
+            ocr_str = self.ocr.getStrWithImgProcess(text_pic, thresholdDict, item["lan"], item["type"], reconTimes=1)
+            printLog(u"OCR识别的类型字典:%s" % str(item))
+            printLog(u"OCR识别出的ocr_str为:%s" % str(ocr_str))
+            # print(u"getCurrentFocusTextEx.ocr_str land = %s, type =%d, ocr_str=%s:"%(item["lan"], item["type"], ocr_str.encode('GB18030')))
+            ocr_str = unicode(ocr_str).lower()
+
+            if is_for_value:
+                parent_ocr_dict = self.xlsparser.get_parent_ocr_dict(cur_parent, value_sheet=True)
+            else:
+                parent_ocr_dict = self.xlsparser.get_parent_ocr_dict(cur_option)
+
+            print u"getCurrentFocusTextEx.parent_ocr_dict:", parent_ocr_dict
+            list_parent_ocr_value = list(parent_ocr_dict.keys())
+            print u"getCurrentFocusTextEx.list_parent_ocr_value:", list_parent_ocr_value
+            current_parent_ocr_value = ""
+            for parent_ocr_value in list_parent_ocr_value:
+                parent_ocr_value = str(parent_ocr_value).lower()
+                if parent_ocr_value in ocr_str or parent_ocr_value == ocr_str:
+                    current_parent_ocr_value = parent_ocr_value
+                    break
+            try:
+                curentOption = parent_ocr_dict[current_parent_ocr_value]
+            except Exception, e:
+                curentOption = ""
+
+            printLog(u"OCR识别出的current_parent_ocr_value为:%s" % str(current_parent_ocr_value))
+            printLog(u"OCR识别出的curentOCROption为:%s" % str(curentOption))
+            # printLog(u"传入的目标option为:%s" % str(option))
+            # 通过OCR来反向识别判断当前识别的option是否为传入的option
+            if str(curentOption).lower() != "" and str(curentOption).lower() != str(cur_option).lower():
+                # print "非焦点框=%s" % (ocr_str)
+                printLog(u"非焦点框=%s" % str(ocr_str))
+                return 0, ocr_str
+
+            # 再遍历目标焦点ocr;
+            for std_str in paths["option_for_ocr"]:
+                std_str = str(std_str).lower()
+                # print 'std_str:', std_str, type(std_str)
+                # print 'ocr_str:', ocr_str, type(ocr_str)
+                if std_str in ocr_str or std_str == ocr_str:
+                    found = True
+                    break
+            # endfor
+            if found is True:
+                break
+        # endfor
+
+        return found, ocr_str
+
+    # 获取焦点框轮廓;
+    def getFocusBox_temp(self, level, first_parent, option=''):
+        # 焦点框截图;
+        bx_pic = ''
+        # 当前截图;
+        cur_img = self.getCurrentUIPath()
+        # 查找焦点框轮廓;
+        bx_found, bx_focus = self.tFocus.findRectByIcon(cur_img, level, first_parent, option)
+        printLog("getCurrentFocusBox(%d,%s):" % (int(bx_found), str(bx_focus)))
+        if bx_found is True:
+            # 保存焦点框截图;
+            bx_pic = os.path.join(getSATTmpDIR(), "menutree_focus_area" + str(time.time()) + ".png")
+            self.imgCMP.saveCropPic(cur_img, bx_pic, (bx_focus[0], bx_focus[1], bx_focus[2], bx_focus[3]))
+        # 返回结果;
+        return bx_found, bx_focus, bx_pic
+
+    # 获取焦点框滚动文本;
+    def getMarqueeText(self, level, first_parent, option=''):
+        # 获取焦点框信息;
+        bx_found, bx_focus, bx_pic = self.getFocusBox_temp(level, first_parent, option)
+        if bx_found is False:
+            printLog("getMarqueeText=>焦点框识别失败")
+            return False, ''
+
+        # 按要求,跑马灯只需要百度高精度;
+        orc_type = {"lan": "ChinesePRC+English", "type": 253}
+        # ocr识别;
+        thresholdDict = self.tconfig.getThresholdDict(first_parent)
+        # ocr_result = self.ocr.getStr(bx_pic, orc_type["lan"], orc_type["type"])
+        ocr_result = self.ocr.getStrWithImgProcess(bx_pic, thresholdDict, orc_type["lan"], orc_type["type"],
+                                                   reconTimes=1)
+        if ocr_result == "ERR<Exp>" or ocr_result.__len__() == 0:
+            printLog("getMarqueeText=>ocr识别失败=%s" % (ocr_result))
+            return False, ocr_result
+        # 返回结果;
+        return True, ocr_result
+
+    def getOCRStrListByOptionList(self, optionList):
+        OCRStrList = []
+        for option in optionList:
+            hasOption, optionDict = self.xlsparser.get_option(option)
+            if hasOption:
+                optionStrList = optionDict["option_ocr"]
+                OCRStrList.extend(optionStrList)
+        return OCRStrList
+
+    def getOCRStrDictByOptionList(self, optionList):
+        OCRStrDict = {}
+        for option in optionList:
+            hasOption, optionDict = self.xlsparser.get_option(option)
+            if hasOption:
+                optionStrList = optionDict["option_ocr"]
+                OCRStrDict[option] = optionStrList
+        return OCRStrDict
+
+    def getFocusBoxMarqueeText(self, marquee_dict, level, first_parent, paths):
+        # 截图2次,判断是否是跑马菜单;
+        # 第一次;
+        bx_found, bx_focus, bx_pic1 = self.getFocusBox_temp(level, first_parent, paths['option'])
+        if bx_found is False:
+            printLog("getFocusBoxMarqueeText:焦点框识别失败1")
+            return -1, ''
+
+        # 等待1秒后截图;
+        time.sleep(marquee_dict['sleep_time'])
+        # 发送鲜活键;
+        self.redRat3.sendKey(marquee_dict['alive_key'], 1, 0.1)
+
+        # 第二次;
+        bx_found, bx_focus, bx_pic2 = self.getFocusBox_temp(level, first_parent, paths['option'])
+        if bx_found is False:
+            printLog("getFocusBoxMarqueeText:焦点框识别失败2")
+            return -1, ''
+
+        # 二图ocr文本是否相似,如果相似认为不是滚动文本;#如果网络问题;
+        thresholdDict = self.tconfig.getThresholdDict(first_parent)
+        # ocr_result_text1 = self.ocr.getStr(bx_pic1, "ChinesePRC+English", 253)
+        ocr_result_text1 = self.ocr.getStrWithImgProcess(bx_pic1, thresholdDict, "CHN_ENG", 10000,
+                                                         reconTimes=1)
+        # 发送鲜活键;
+        self.redRat3.sendKey(marquee_dict['alive_key'], 1, 0.1)
+        # ocr_result_text2 = self.ocr.getStr(bx_pic2, "ChinesePRC+English", 253)
+        ocr_result_text2 = self.ocr.getStrWithImgProcess(bx_pic2, thresholdDict, "CHN_ENG", 10000,
+                                                         reconTimes=1)
+        # 发送鲜活键;
+        self.redRat3.sendKey(marquee_dict['alive_key'], 1, 0.1)
+
+        # 对比结果:相等非滚动文本,否则视为滚动文本;
+        if ocr_result_text1 != "" and ocr_result_text1 == ocr_result_text2:
+            return self.getCurrentFocusTextEx(level, first_parent, paths['parent'], paths['option'],
+                                              paths['option_for_ocr'])
+        else:
+            destOption = paths['option']
+            menuList = marquee_dict["menu"]
+            # 如果只有一项跑马灯,且目标option亦在跑马灯列表中,则直接返回成功结果
+            if menuList.__len__() == 1 and (destOption in menuList):
+                return 1, destOption
+            list_img = []
+            result = False
+            ocr_result_text = ''
+            ocr_text_list = []
+
+            # 截图5次;
+            for i in range(0, 5):
+                bx_found, bx_focus, bx_pic = self.getFocusBox_temp(level, first_parent, paths['option'])
+                if bx_found is True:
+                    list_img.append(bx_pic)
+                # 间隔多久截图;
+                time.sleep(marquee_dict['sleep_time'])
+                # 发送鲜活键;
+                self.redRat3.sendKey(marquee_dict['alive_key'], 1, 0.1)
+            # endfor
+
+            # 遍历文本list_img;
+            # 按要求,跑马灯只用百度识别;
+            orc_type = {"lan": "ChinesePRC+English", "type": 10000}
+            for bx_pic in list_img:
+                # ocr识别;
+                # ocr_result_text = self.ocr.getStr(bx_pic, orc_type["lan"], orc_type["type"])
+                ocr_result_text = self.ocr.getStrWithImgProcess(bx_pic, thresholdDict, orc_type["lan"],
+                                                                orc_type["type"],
+                                                                reconTimes=1)
+
+                # 识别一次,发送一次鲜活键;
+                self.redRat3.sendKey(marquee_dict['alive_key'], 1, 0.1)
+                if ocr_result_text == "ERR<Exp>" and ocr_result_text.__len__() == 0:
+                    return 0, ocr_result_text
+                ocr_text_list.append(ocr_result_text)
+            #end for
+
+            # 处理识别到的ocr列表:有部分聚焦状态会将option及其值一起包括其中,所以要把相同的部分(option或者值)去掉
+            ocr_text_list = self.removeDuplicateString(ocr_text_list)
+            # 获取到跑马灯option的ocr字典
+            ocrStrDict = self.getOCRStrDictByOptionList(menuList)
+            print u"获取到的跑马灯Option对应的ocr字典ocrStrDict:", ocrStrDict
+            print u"识别到的跑马灯ocr文字列表ocr_text_list:",ocr_text_list
+            for marquee_option in ocrStrDict:
+                option_ocr_list = ocrStrDict[marquee_option]
+                for option_ocr in option_ocr_list:
+                    # 一共5张截图,只要有3张满足条件,就算找到了该option(规避同时存在两条相似的跑马灯选项问题,以及跑马灯头尾同时显示的问题)
+                    count = 0
+                    for ocr_text in ocr_text_list:
+                        if ocr_text.lower() in option_ocr:
+                            count += 1
+                    if count >= 3 and destOption == marquee_option:
+                        return 1, destOption
+                    elif count >= 3:
+                        printLog(u"当前聚焦的跑马灯Option实际为:%s"%marquee_option)
+                        return 0, marquee_option
+            else:
+                printLog(u"未能识别到当前聚焦的跑马灯Option!!!")
+                return 0, "unknown marquee text"
+
+
+
+    def strSplit(self, str):
+        ret = []
+        str_int = ''
+        str_ch = ''
+        ch_last = ' '
+        for ch in str:
+            if ord(ch) > 47 and ord(ch) < 58:
+                str_int += ch
+                if str_ch.__len__():
+                    ret.append(str_ch)
+                    str_ch = ''
+            else:
+                if ord(ch_last) > 47 and 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
+
+    def getCurrentFocusOcr_Int(self, icon_level, first_parent, option="", isForValue=False):
+        print u"getCurrentFocusOcr_Int Start>>>", time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
+        # 聚集框识别;
+        cur_img = self.getCurrentUIPath()
+        print u"isForValue:", isForValue
+        if isForValue:
+            bxfound, cur_box = self.tFocus.findRectByIcon2(cur_img, option, True)
+        else:
+            bxfound, cur_box = self.tFocus.findRectByIcon2(cur_img, option)
+        # print "getCurrentFocusBox(%d,%s):" % (int(bxfound), str(cur_box))
+        printLog("getCurrentFocusBox(%d,%s):" % (int(bxfound), str(cur_box)))
+        if not bxfound or cur_box == []:
+            return False, "", 0
+
+        cur_box = self.getFocusTextBox3(option, cur_box, True)
+        printLog("getCurrentFocusBox(%d,%s):" % (int(bxfound), str(cur_box)))
+        # 截图文本框;
+        txt_pic = os.path.join(getSATTmpDIR(), "meuttree_area_text.png")
+        self.imgCMP.saveCropPic(cur_img, txt_pic, (cur_box[0], cur_box[1], cur_box[2], cur_box[3]))
+        # ocr识别;
+        ocr_result = ''
+        # 获取ocr列表;
+        hasOption, path = self.xlsparser.get_option(option)
+        if hasOption:
+            self.get_ocr_list(path['level'], option)
+        for orc_item in self.ocrDict:
+            thresholdDict = self.tconfig.getThresholdDict(first_parent)
+            # ocr_result = self.ocr.getStr(txt_pic, orc_item["lan"], orc_item["type"])
+            ocr_result = self.ocr.getStrWithImgProcess(txt_pic, thresholdDict, orc_item["lan"], orc_item["type"],
+                                                       reconTimes=1)
+            # print("getCurrentFocusOcr_Int=ocr识别结果=%s" % (ocr_result))
+            printLog("getCurrentFocusOcr_Int=ocr识别结果=%s" % (ocr_result))
+            if ocr_result == "ERR<Exp>" or ocr_result.__len__() == 0:
+                continue
+            # 字符串去掉末尾">"符号;
+            ocr_result = ocr_result.strip('>')
+            # 按数字分解多组文本;
+            # list_orc = getDigitFromString(ocr_result)
+            list_ocr = self.strSplit(ocr_result)
+            # 只判断最后一位是否是数字;
+            if list_ocr.__len__() < 1:
+                errorPngName = "menutree_error_" + self.currentTime() + "ocr.png"
+                error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+                self.ccard.takePicture(error_uiPic)
+                # print u"当前识别的ocr 不包含int类型值,截图为:%s" % (str(ocr_result))
+                printLog(u"当前识别的ocr 不包含int类型值,截图为:%s" % (str(ocr_result)))
+                continue
+
+            try:
+                # 获取最右边的文本,如果是数字则退出;
+                text_int_str = list_ocr[list_ocr.__len__() - 1]
+                # print 'text_int_str:', text_int_str
+                printLog('text_int_str:%s' % str(text_int_str))
+                text_num = float(text_int_str)
+                # 返回结果;
+                return True, ocr_result, text_num
+            except Exception:
+                continue
+
+        # endfor
+        print u"getCurrentFocusOcr_Int End<<<", time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
+        return False, "", 0
+
+    # 遍历获取ocr结果;
+    def getCurrentFocusTextEx(self, level, first_parent, cur_parent, option, list_str, isSource=False,
+                              isForValue=False):
+        print level, first_parent, cur_parent, option
+        current_uiPic = self.getCurrentUIPath()
+        if isForValue:
+            # cur_parent 对应value表里的option;
+            # option_info = self.xlsparser.get_option(cur_parent)
+            isFind, contourRect = self.tFocus.findRectByIcon2(current_uiPic, cur_parent, isForValue)
+        else:
+            isFind, contourRect = self.tFocus.findRectByIcon2(current_uiPic, option)
+        printLog(u"获取当前聚焦效果isFind:%s 聚焦区域contourRect:%s" % (str(isFind), str(contourRect)))
+        if not isFind or contourRect == []:
+            return -1, ""
+        else:
+            found, ocr_str = False, ''
+            # tmpPic = os.path.join(getSATTmpDIR(), "meuttree_area_text11.png")
+            # self.imgCMP.saveCropPic(current_uiPic, tmpPic,
+            #                         (contourRect[0], contourRect[1], contourRect[2], contourRect[3]))
+            # 是否有文字方向字段存在在ini里,如果有则转换文本框坐标;
+            tmpPic = os.path.join(getSATTmpDIR(), "meuttree_area_text.png")
+
+            if isForValue:
+                contourRect = self.getFocusTextBox3(cur_parent, contourRect, True)
+            else:
+                contourRect = self.getFocusTextBox3(option, contourRect)
+
+            self.imgCMP.saveCropPic(current_uiPic, tmpPic,
+                                    (contourRect[0], contourRect[1], contourRect[2], contourRect[3]))
+            if isSource:
+                self.redRat3.sendKey("ok")
+
+            # print "self.ocrDict:", self.ocrDict
+            # 获取ocr_list;
+            ocr_list = self.xlsparser.get_ocr_list(level, cur_parent, option, True if cur_parent == option else False)
+            print u"%s,%s,%s =>ocr_list=%s" % (level, cur_parent, option, str(ocr_list))
+
+            # 获取ocr列表;
+            self.get_ocr_list(level, option)
+            # 遍历ocr类型;
+            for item in self.ocrDict:
+                # 识别ocr;
+                thresholdDict = self.tconfig.getThresholdDict(first_parent)
+                ocr_str = self.ocr.getStrWithImgProcess(tmpPic, thresholdDict, item["lan"], item["type"], reconTimes=1)
+                # print "\n\ngetCurrentFocusTextEx ==========================:OCR-Type=%s, OCR-Text=%s\n\n" % (
+                #     str(item), str(ocr_str))
+                printLog(u"OCR识别的类型字典:%s" % str(item))
+                printLog(u"OCR识别出的ocr_str为:%s" % str(ocr_str))
+                # print(u"getCurrentFocusTextEx.ocr_str land = %s, type =%d, ocr_str=%s:"%(item["lan"], item["type"], ocr_str.encode('GB18030')))
+                ocr_str = unicode(ocr_str).lower()
+
+                if isSource is False:
+                    if isForValue:
+                        parent_ocr_dict = self.xlsparser.get_parent_ocr_dict(cur_parent, value_sheet=True)
+                    else:
+                        parent_ocr_dict = self.xlsparser.get_parent_ocr_dict(option)
+                    print u"getCurrentFocusTextEx.parent_ocr_dict:", parent_ocr_dict
+                    list_parent_ocr_value = list(parent_ocr_dict.keys())
+                    print u"getCurrentFocusTextEx.list_parent_ocr_value:", list_parent_ocr_value
+
+                    # 比较所有的ocr
+                    for parent_ocr_value in list_parent_ocr_value:
+                        parent_ocr_value = str(parent_ocr_value).lower()
+                        if parent_ocr_value in ocr_str or strcmp(parent_ocr_value, ocr_str):
+                            # 优先比较目标option,如果符合要求则直接返回找到结果
+                            for std_str in list_str:
+                                std_str = str(std_str).lower()
+                                if strcmp(std_str, parent_ocr_value):
+                                    printLog(u"传入的目标Value_ocr_list为:%s" % list_str)
+                                    printLog(u"与目标option的ocr_value:%s相同"%std_str)
+                                    return 1, option
+                            else:
+                                # 否则则识别出该ocr_value所对应的option
+                                try:
+                                    currentOption = parent_ocr_dict[parent_ocr_value]
+                                except Exception,e:
+                                    currentOption = ""
+                                    printLog(u"识别出的currentOption为:%s" % currentOption)
+                                if currentOption != "":
+                                    return 0, currentOption
+                else:
+                    if isSource is True:
+                        # 如果是信源  而且识别出是非目标信源则跳出该语言
+                        for ocr_std_str_list in ocr_list:
+                            # print "ocr_std_str_list:",ocr_std_str_list
+                            for ocr_std_str in ocr_std_str_list:
+                                # print "ocr_std_str:",ocr_std_str
+                                ocr_std_str = str(ocr_std_str).lower()
+                                # print 'ocr_std_str:', ocr_std_str, type(ocr_std_str)
+                                # print 'ocr_str:', ocr_str, type(ocr_str)
+                                if ocr_std_str in ocr_str or ocr_std_str == ocr_str:
+                                    printLog(u"非焦点信源=%s" % str(ocr_str))
+                                    return 0, ocr_str
+            else:
+                # 全部ocr_value都不符合要求,则视为menutree中不存在此option相关的数据
+                printLog(u"未能识别出当前Option!!!请检查Mnenutree相关参数配置!!!")
+                return 0, ocr_str
+
+
+    # 移动到目标网格焦点上;
+    def move2TargetGridNode(self, level, first_parent, paths, returnKeyEventCount, Max_Try=15):
+        result = False
+        cur_option = paths['option']
+        last_box, cur_box = [], []
+
+        # 遍历正序;
+        is_positive = True
+        key_idx = False
+        key_dir = ['right', 'left']
+        # 第一个,最后一个;
+        is_first, is_last = False, False
+        # 上一行,下一行;
+        is_up, is_down = False, False
+
+        # 获取等待时间
+        waitTime = self.getOptionWaitTime(paths['option'])
+        time.sleep(waitTime)
+
+        while True:
+            cur_pic = self.getCurrentUIPath()
+            found_box, cur_box = self.tFocus.findRectByIcon2(cur_pic, cur_option)
+            if found_box is True:
+                if last_box == cur_box:
+                    if is_positive is True:  # 正序;
+                        if is_last is True:
+                            is_positive = False
+                            key_idx = not key_idx
+                            last_box = []
+                            # 移动上一格;
+                            self.redRat3.sendKey(key_dir[int(key_idx)])
+                            continue
+
+                        if is_down is False:
+                            is_down = True
+                            # 移动下一行;
+                            self.redRat3.sendKey('down')
+                            key_idx = not key_idx
+                        else:
+                            is_last = True
+                            key_idx = not key_idx
+                    else:  # 逆序;
+                        if is_first is True:
+                            break
+
+                        if is_up is False:
+                            is_up = True
+                            # 移动上一行;
+                            self.redRat3.sendKey('up')
+                            key_idx = not key_idx
+                        else:
+                            is_first = True
+                            key_idx = not key_idx
+                    # endif
+                else:
+                    found_target, cur_text = self.getFocusText(level, first_parent, paths, cur_pic, cur_box)
+                    if found_target is True:
+                        result = True
+                        break
+                    # 保留为上一焦点框;
+                    last_box = cur_box
+                    # 移动下一焦点;
+                    self.redRat3.sendKey(key_dir[int(key_idx)])
+
+                    # 第一个,最后一个;
+                    is_first, is_last = False, False
+                    # 上一行,下一行;
+                    is_up, is_down = False, False
+                # endif
+            else:
+                printLog(u"网格焦点框识别失败")
+                break
+            # endif
+
+        # 返回结果;
+        return result
+
+    def move2TargetGridNode_matchTemp(self, level, first_parent, paths, returnKeyEventCount, Max_Try = 15):
+        # 目标结点;
+        tgt_option = paths['option']
+        temp_dir = os.path.join(getMenuTree3SelectedProjectCfgPath(), "match_temp", paths['parent'])        
+        # 选中时的模板
+        img_focus_temp = os.path.join(temp_dir, tgt_option + '_focus.jpg')
+        # 未选中模板图
+        img_unfocus_temp = os.path.join(temp_dir, tgt_option + '_unfocus.jpg')
+
+        # 获取等待时间
+        waitTime = self.getOptionWaitTime(paths['option'])
+        time.sleep(waitTime)
+        
+        # 尝试次数;
+        page_try_time = 15
+        focus_try_time = 100
+        # 目标聚集结果;
+        tgt_focus_result = False
+        while tgt_focus_result is False:
+            # 电视截图(原图);
+            img_tv = self.getCurrentUIPath()
+            # 查找聚集框是否在目标上;
+            match_result = self.tFocus.feature_detect.matchSingleImage(img_tv, None, img_focus_temp)
+            if match_result is not None:
+                tgt_focus_result = True
+                break
+            #end-if
+
+            # 查找非聚集目标;
+            match_result = self.tFocus.feature_detect.matchSingleImage(img_tv, None, img_unfocus_temp)
+            if match_result is None:
+                # 尝试次数使用完;
+                if page_try_time == 0:
+                    break
+                # 没找到,可能在别的页面,向右移动;
+                self.redRat3.sendKey('right')
+                page_try_time = page_try_time - 1
+                continue
+            # end-if
+
+            print u'匹配度=', match_result['tmpVal']
+            # 找到非聚集目标后,再查找当前聚集框坐标;
+            bfound_box, focus_box = self.tFocus.findRectByIcon2(img_tv, tgt_option, is_value_sheet=False)  # 不支持叶结点;
+            if bfound_box is False:
+                printLog("move2TargetGridNode_matchTemp:未找到聚集框")
+                break
+            #end-if
+
+            box1, box2 = focus_box, match_result['coordinate']
+            # 计算非聚集模板与聚集框的位置;
+            dir = self.tFocus.feature_detect.getOrientationEx(box1, box2)
+            if dir == -1:
+                break
+
+            # 中心点坐标;center point
+            # cpt1 = [box1[2]-box1[0],box1[3]-box1[1]]
+            # cpt2 = [box2[2]-box2[0],box2[3]-box2[1]]
+            if dir == 0:# 左上方;
+                self.redRat3.sendKey('right')
+            elif dir == 1:# 正上方;
+                self.redRat3.sendKey('down')
+            elif dir == 2:# 右上方;
+                self.redRat3.sendKey('left')
+            elif dir == 3:# 正左方;
+                self.redRat3.sendKey('right')
+            elif dir == 4:# 正右方;
+                self.redRat3.sendKey('left')
+            elif dir == 5:# 左下方;
+                self.redRat3.sendKey('right')
+            elif dir == 6:# 正下方;
+                self.redRat3.sendKey('up')
+            elif dir == 7:# 右下方;
+                self.redRat3.sendKey('left')
+                    
+            # 移动次数用完;
+            focus_try_time = focus_try_time -1
+            if focus_try_time == 0:
+                break
+        #end-while;
+
+        return tgt_focus_result
+
+    # 移动到目标节点上;
+    def move2TargetNode(self, level, first_parent, paths, returnKeyEventCount, findDirection="down", Max_Try=15):
+        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到option_for_ocr所在的区域,则按照传入方向的反方向 反向寻找 Max_Try 次
+        # 正向寻找的次数计数和最大次数
+        count = 0
+        parent = paths['parent']
+        option_list = self.xlsparser.getSubOptionList(parent)
+        if option_list.__len__() != 0:
+            Max_Try = option_list.__len__()
+            print u"move2TargetNode.%s option_list:"%parent, option_list
+        # 反向寻找的次数计数和最大次数
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try
+        print u"move2TargetNode:Max_Try:%s, Reverse_Max_Try:%s"%(Max_Try, Reverse_Max_Try)
+
+        if findDirection == "grid":
+            temp_dir = os.path.join(getMenuTree3SelectedProjectCfgPath(), "match_temp", paths['parent'])
+            if os.path.exists(temp_dir):
+                return self.move2TargetGridNode_matchTemp(level, first_parent, paths, returnKeyEventCount, Max_Try)
+            else:
+                return self.move2TargetGridNode(level, first_parent, paths, returnKeyEventCount, Max_Try)
+
+        # 记录上一个焦点识别的文字内容   用于判断是否到达列表的边界 如果到达边界则反向寻找
+        old_text = "init_old_text"
+        # 反向寻找的遥控器按键值
+        findReverseDirection = "up"
+
+        if findDirection == "up":
+            findReverseDirection = "down"
+        if findDirection == "down":
+            findReverseDirection = "up"
+        if findDirection == "left":
+            findReverseDirection = "right"
+        if findDirection == "right":
+            findReverseDirection = "left"
+
+        print u"findDirection:", findDirection, type(findDirection)
+        print u"findReverseDirection:", findReverseDirection, type(findReverseDirection)
+
+        # # 去除大小写的识别字符
+        # option_for_ocr_low = str(option_for_ocr).lower()
+
+        isMarquee = False
+        marquee_dict = {}
+        # 是否有跑马灯文本;
+        if paths["others"].__len__():
+            data_other = json.loads(paths["others"])
+            if "marquee" in data_other:
+                marquee_dict = data_other['marquee']
+                isMarquee = True
+
+        # 获取等待时间
+        waitTime = self.getOptionWaitTime(paths['option'])
+        printLog(u"move2TargetNode:获取到进入option_%s,等待时间为%s"%(paths['option'],waitTime))
+        time.sleep(waitTime)
+
+        while (True):
+            print "count:", count
+            if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                break
+            if isMarquee is True:
+                isFind, text = self.getFocusBoxMarqueeText(marquee_dict, level, first_parent, paths)
+            else:
+                isFind, text = self.getCurrentFocusTextEx(level, first_parent, paths["parent"], paths["option"],
+                                                          paths["option_for_ocr"])
+            if isFind == -1:
+                errorPngName = "menutree_error_" + str(paths["option"]) + self.currentTime() + ".png"
+                error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+                self.ccard.takePicture(error_uiPic)
+                printLog(u"聚焦到目标option:%s失败!当前界面无法找到焦点区域!!!出错界面截图%s" % (str(paths["option"]), str(error_uiPic)))
+                self.redRat3.sendKey("return", returnKeyEventCount, 0.5)
+                return False
+            # elif (self.ocr.cmpOcrStr(text, option_for_ocr)):
+            # 由比较目标文本和识别文本 是否相等  改为 识别文本是否包含目标文本
+            else:
+                if isFind == 1:
+                    printLog(u"聚焦到目标option:%s成功!" % str(paths["option"]))
+                    return True
+                # 判断两次识别的text是否相等来判断是否到达边界
+                print u"move2TargetNode.text",text
+                print u"move2TargetNode.old_text",old_text
+                if self.ocr.cmpOcrStr(text, old_text):
+                    # 如果已经到达边界则不再正方向寻找焦点
+                    count = Max_Try
+                    old_text = "init_old_text"
+                else:
+                    old_text = text
+
+                if count < Max_Try:
+                    count = count + 1
+                    self.redRat3.sendKey(findDirection)
+                else:
+                    Reversecount = Reversecount + 1
+                    self.redRat3.sendKey(findReverseDirection)
+        errorPngName = "menutree_error_" + str(paths["option"]) + self.currentTime() + ".png"
+        error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+        self.ccard.takePicture(error_uiPic)
+        printLog(u"聚焦到目标option:%s失败!正向遍历%s次,和反向遍历%s次都没有聚焦到option:%s中,截图路径:%s" % (str(paths["option"]),
+                                                                                 str(Max_Try), str(Reverse_Max_Try),
+                                                                                 str(paths["option"]),
+                                                                                 str(error_uiPic)))
+        return False
+
+    # 设置叶子节点值;
+    def setLeafNodeValue(self, level, first_parent, option, value_excel, value_for_ocr, value, move_key, enter_key,
+                         Max_Try=15):
+        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到option_for_ocr所在的区域,则按照传入方向的反方向 反向寻找 2*Max_Try 次
+
+        # 正向寻找的次数计数和最大次数
+        count = 0
+        Max_Try = Max_Try
+        # 反向寻找的次数计数和最大次数
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try * 2
+        # 记录上一个焦点识别的文字内容   用于判断是否到达列表的边界 如果到达边界则反向寻找
+        old_text = "init_old_text"
+
+        # 正向寻找的遥控器按键值
+        findDirection = move_key[1]  # 向右或者向下
+        # 反向寻找的遥控器按键值
+        findReverseDirection = move_key[0]  # 向左或者向上
+
+
+
+
+        # 获取等待时间
+        waitTime = self.getLeafWaitTime(option)
+        printLog(u"setLeafNodeValue:获取到进入叶节点%s,等待时间为%s"%(option, waitTime))
+        time.sleep(waitTime)
+
+        duration = 0.2
+
+        isMarquee = False
+        marquee_dict = {}
+        leaf_data = self.xlsparser.get_value(option)
+        if leaf_data["others"].__len__():
+            data_other = json.loads(leaf_data["others"])
+            # 是否有跑马灯文本;
+            if "marquee" in data_other:
+                marquee_dict = data_other['marquee']
+                isMarquee = True
+            # 是否有按键时间设置
+            if "duration" in data_other:
+                duration = float(data_other['duration'])
+        printLog(u"读取到的设值间隔为%s"%duration)
+
+        value_for_ocr_low = str(value_for_ocr[0]).lower()
+        # 判断设置值的类型 是rang类型还是str类型
+        if "range(" in value_for_ocr_low:
+            isRangeType = True
+            str_num = value_for_ocr_low.replace("range", "")
+            list_num = eval(str_num)
+            min_num = list_num[0]
+            max_num = list_num[1]
+            try:
+                value_num = int(value)
+            except Exception:
+                printLog(u"传入的值value:%s和option:%s对应的类型不匹配 !" % (str(value), str(option)))
+                return False
+        else:
+            isRangeType = False
+        if isRangeType:
+            # 设置数值范围类型的处理
+            pass
+            while (True):
+                # 执行超过3次操作仍然失败则返回False
+                if count > 3:
+                    break
+
+                isFind, text, text_num = self.getCurrentFocusOcr_Int(level, first_parent, option=option,
+                                                                     isForValue=True)
+                if not isFind:
+                    errorPngName = "menutree_error_" + str(option) + str(value_excel) + self.currentTime() + ".png"
+                    error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+                    self.ccard.takePicture(error_uiPic)
+                    printLog(u"设置叶子节点值时,当前界面无法找到焦点区域!!!getCurrentFocusOcr_Int出错界面截图%s" % str(error_uiPic))
+                    return False
+                else:
+
+                    if text_num < min_num or text_num > max_num:
+                        errorPngName = "menutree_error_" + str(option) + '_' + str(
+                            value_excel) + self.currentTime() + ".png"
+                        error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+                        self.ccard.takePicture(error_uiPic)
+                        printLog(u"设置叶子节点值时,当前option:%s 识别出来的值text:%s 不在value_for_ocr:%s范围内,截图为:%s" % (
+                            str(option), str(text), str(value_for_ocr), str(error_uiPic)))
+                        return False
+                    # if abs(text_num) == abs(value_num):
+                    if text_num == value_num:
+                        printLog(u"聚焦到目标option:%s,设置value:%s选项成功!" % (str(option), str(value_excel)))
+                        # 聚焦成功发送enter_key
+                        if enter_key != 'default':
+                            self.redRat3.sendKey(enter_key)
+                        return True
+                    else:
+                        if findDirection == 'input':
+                            # 发送按键;
+                            for key in list(str(value_num)):
+                                self.redRat3.sendKey(key, 1, duration)
+                            if max_num > 100:
+                                return True
+                        else:
+                            count = count + 1
+                            # 如果目标值大于现有值,则按正方向按键(向下或者向右)增加当前界面的值
+                            if value_num > text_num:
+                                self.redRat3.sendKey(findDirection, value_num - text_num, duration)
+                            # 如果目标值小于现有值,则按反方向按键(向上或者向左)减少当前界面的值
+                            if value_num < text_num:
+                                self.redRat3.sendKey(findReverseDirection, text_num - value_num, duration)
+            errorPngName = "menutree_error_" + str(option) + '_' + str(value_excel) + self.currentTime() + ".png"
+            error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+            self.ccard.takePicture(error_uiPic)
+            printLog(u"设置叶子节点值时,当前option:%s 设置3次仍未设置到目标值value:%s,截图为:%s " % (str(option), str(value), str(error_uiPic)))
+            return False
+        else:
+            # 设置选项类型的处理
+            while (True):
+                print "count:", count
+                if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                    break
+                if isMarquee is True:
+                    # 跑马灯函数需要cur_parent的参数,把cur_parent参数加入到leaf_data中再传进去
+                    result, paths = self.xlsparser.get_option(option)
+                    leaf_data['parent'] = paths['parent']
+                    leaf_data['option_for_ocr'] = leaf_data['value_for_ocr']
+                    isFind, text = self.getFocusBoxMarqueeText(marquee_dict, level, first_parent, leaf_data)
+                else:
+                    isFind, text = self.getCurrentFocusTextEx(level, first_parent, option, value_excel, value_for_ocr,
+                                                          isForValue=True)
+                if isFind == -1:
+                    errorPngName = "menutree_error_" + str(option) + '_' + str(
+                        value_excel) + self.currentTime() + ".png"
+                    error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+                    self.ccard.takePicture(error_uiPic)
+                    printLog(u"设置叶子节点值时,当前界面无法找到焦点区域!!!getCurrentFocusTextEx 出错界面截图%s" % str(error_uiPic))
+                    return False
+                # elif (self.ocr.cmpOcrStr(text, value_for_ocr)):
+                # 由比较目标文本和识别文本 是否相等  改为 识别文本是否包含目标文本
+                else:
+                    if isFind == 1:
+                        printLog(u"聚焦到目标option:%s,设置value:%s选项成功!" % (str(option), str(value_excel)))
+                        # 聚焦成功发送enter_key
+                        if enter_key != 'default':
+                            self.redRat3.sendKey(enter_key)
+                        return True
+                        # 判断两次识别
+                    # 判断两次识别的text是否相等来判断是否到达边界
+                    if self.ocr.cmpOcrStr(text, old_text):
+                        # 如果已经到达边界则不再正方向寻找焦点
+                        count = Max_Try
+                        old_text = "init_old_text"
+                    else:
+                        old_text = text
+
+                    if count < Max_Try:
+                        count = count + 1
+                        self.redRat3.sendKey(findDirection)
+                    else:
+                        Reversecount = Reversecount + 1
+                        self.redRat3.sendKey(findReverseDirection)
+            errorPngName = "menutree_error_" + str(option) + '_' + str(value_excel) + self.currentTime() + ".png"
+            error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+            self.ccard.takePicture(error_uiPic)
+            printLog(u"设置叶子节点值时,正向遍历%s次,和反向遍历%s次都没有聚焦到option:%s的value:%s选项中,截图为%s" % (
+                str(Max_Try), str(Reverse_Max_Try), str(option), str(value_excel), str(error_uiPic)))
+            return False
+
+        return True
+
+    # 处理others字段;
+    def dealOthers(self, level, path):
+        others = json.loads(path['others'])
+        if "password" in others:
+            password = self.tconfig.get_value("Password", others["password"])
+            # 发送密码前,停2秒(因为像6586机芯响应很慢,密码框还没弹出就完成了密码输入的操作);
+            time.sleep(2)
+            # 发送按键;
+            for key in list(password):
+                self.redRat3.sendKey(key, 1, 0.2)
+            time.sleep(1)
+            # 发送ok键;
+            if others["enter_key"] != "default":
+                self.redRat3.sendKey(others["enter_key"])
+                # 判断是否成功输入密码;
+            current_uiPic = self.getCurrentUIPath()
+            # 此处findRectByIcon参数3传递的不是first_parent,而是当前option的parent;
+            isFind, contourRect = self.tFocus.findRectByIcon(current_uiPic, level, path['parent'])
+            return not isFind
+        return False
+
+    # 由于测试开始之前都切到黑屏背景,去掉聚焦效果OCR验证,3次焦点验证,验证到有聚焦效果就返回
+    def isFirstOptionShow(self, level, first_parent, first_option):
+        for i in range(3):
+            current_uiPic = self.getCurrentUIPath()
+            isFind, contourRect = self.tFocus.findRectByIcon2(current_uiPic, first_option)
+            if isFind:
+                return True
+        return False
+
+    # 对外接口:设置操作值;
+    def setOptionValue(self, option, value, Max_Try=15, reTryTime=0, leafNodeValueWaitTime=0.0, returnKeyEventTimes=-1):
+        # 切黑场;
+        self.sourceInput.setPattern(11)
+        printLog(u"开始执行setOptionValue。option:%s  value:%s" % (str(option), str(value)))
+        # 寻路结果;
+        result = False
+        # 获取menu path;
+        value_params, path_params = self.xlsparser.get_menu_paths(option, value)
+        printLog(u"获取到达option:%s的路径字典path_params:%s" % (str(option), str(path_params)))
+        printLog(u"获取设置value:%s的值字典value_params:%s" % (str(value), str(value_params)))
+
+        checkRunOptionPathResult = self.xlsparser.checkRunOptionPath(path_params)
+        if str(checkRunOptionPathResult) == "NoExit":
+            printLog(u"表格中不存在到达Option:   %s   的路径,在表格中排查到达该Option路径" % str(option))
+            return False
+        if str(checkRunOptionPathResult) == "Fail":
+            printLog(u"表格中到达Option:   %s   的路径出现数据断层找不到First层级,在表格中排查到达该Option路径" % str(option))
+            return False
+
+        # returnKeyEventCount = path_params.__len__() + 1
+        returnKeyEventCount = 0
+        print u"returnKeyEventCount:", returnKeyEventCount
+        # 逆序路径key;
+        revpp = reversed(path_params)
+        # 根节点是否已进入;
+        entry = False
+        first_parent = u''
+        first_option = u''
+        # 遍历路径(已排序,从first开始);
+        # print "revpp:", revpp
+        for level in revpp:
+            # print "level:", level, path_params[level]
+            printLog(u"执行路径level:%s  对应路径字典:%s" % (str(level), str(path_params[level])))
+            # 是否进入根结点;
+            if entry == False:
+                # 标记已进入根;
+                entry = True
+                first_parent = path_params[level]["parent"]
+                first_option = path_params[level]["option"]
+                checkFirstOptionTimes = 0
+                while True:
+                    if checkFirstOptionTimes > 2:
+                        errorPngName = "menutree_error_" + str(option) + '_' + str(value) + self.currentTime() + ".png"
+                        error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+                        self.ccard.takePicture(error_uiPic)
+                        printLog(u"执行setOptionValue ERROR!option:%s  value:%s ;尝试3次快捷按键仍然没有唤出聚焦焦点界面!!!  出错截图:%s" % (
+                            str(option), str(value), str(error_uiPic)))
+                        return False
+                    printLog(u"第%s次尝试呼出一级界面" % str(checkFirstOptionTimes))
+                    # 判断是否存在others字段,others字段中存在first_key,则优先使用first_key进入
+                    try:
+                        others = json.loads(path_params[level]["others"])
+                    except Exception,e:
+                        others = {}
+                    printLog("section others:%s"%others)
+                    if "first_key" in others.keys():
+                        first_key = others["first_key"]
+                    else:
+                        first_key = path_params[level]["parent"]
+                    printLog(u"进入根节点,first_key:%s"%first_key)
+                    # 进入根节点
+                    self.redRat3.sendKey(first_key)
+                    waitTime = self.getParentWaitTime(path_params[level]["parent"])
+                    printLog(u"打开%s界面的等待时间waittime为:%s" % (str(path_params[level]["parent"]), str(waitTime)))
+                    time.sleep(waitTime)
+                    # 获取ocr列表;
+                    self.get_ocr_list(level, option)
+                    if self.isFirstOptionShow(level, first_parent, first_option):
+                        printLog(u"呼出一级菜单界面")
+                        break
+                    else:
+                        time.sleep(5)
+                        self.redRat3.sendKey("return", 1)
+                        checkFirstOptionTimes = checkFirstOptionTimes + 1
+                # 如果一级界面弹出成功,则退出时候返回按键次数加1
+                returnKeyEventCount = returnKeyEventCount + 1
+
+            print u"level:", level, "first_parent:", first_parent
+
+            # 如果 others 字段不为空;
+            if path_params[level]["others"].__len__():
+                # 如果不是跑马灯处理则按照输入密码处理
+                if self.dealOthers(level, path_params[level]) is True:
+                    # print u"解锁失败!!!"
+                    printLog(u"执行setOptionValue ERROR!option:%s  value:%s ;解锁失败!!!" % (str(option), str(value)))
+                    return False
+
+            cur_option_enter_key = path_params[level]["option_enter_key"]
+            # 移动到子节点;                
+            if cur_option_enter_key.__len__() == 0:
+                enter_key = path_params[level]["enter_key"]
+            else:
+                enter_key = cur_option_enter_key
+
+            move_key = path_params[level]["move_key"]
+
+            if str(move_key) == str([u'up', u'down']) or str(move_key) == str([u'down', u'up']):
+                findDirection = str(move_key[1])
+            elif str(move_key) == str([u'left', u'right']) or str(move_key) == str([u'right', u'left']):
+                findDirection = str(move_key[1])
+            else:
+                findDirection = 'grid'
+
+            result = self.move2TargetNode(
+                level,
+                first_parent,
+                path_params[level],
+                returnKeyEventCount,
+                findDirection=findDirection, Max_Try=Max_Try)
+            if result is True:
+                # 子节点的进入方式默认为ok;
+                if enter_key != u'default':
+                    self.redRat3.sendKey(enter_key)
+                    # 如果执行一次enter_key,则退出时按返回键的次数加一
+                    returnKeyEventCount = returnKeyEventCount + 1
+            else:
+                break
+        # end-for
+        # 寻路成功,设置叶节点值;
+        if result is True:
+            # 如果是信源选择   自动选择不需要执行 enter_key
+            if self.ocr.cmpOcrStr(option, 'source'):
+                return True
+            time.sleep(leafNodeValueWaitTime)
+            print u"执行设置叶节点:", value_params
+            result = self.setLeafNodeValue(
+                level,
+                first_parent,
+                value_params["option"],
+                value_params["value"],
+                value_params["value_for_ocr"],
+                value,
+                value_params["move_key"],
+                value_params["enter_key"],
+                Max_Try=Max_Try)
+
+        # else:
+        if returnKeyEventTimes == -1:
+            print u"执行退出,执行退出按键次数returnKeyEventCount:", returnKeyEventCount
+            # 无论成功失败,要返回到初始界面
+            self.redRat3.sendKey("return", returnKeyEventCount, 0.2)
+            time.sleep(1)
+            # 检测执行回退键是否成功,吐过仍有聚焦效果则继续执行return键
+            reCount = 0
+            while True:
+                if reCount >= returnKeyEventCount:
+                    printLog(u"执行了returnKeyEventCount:%s次,仍无法退出到初始界面" % str(returnKeyEventCount))
+                    break
+                # isFind, text = self.getCurrentFocusText("First", first_parent)
+                # if isFind:
+                if self.isFirstOptionShow("First", first_parent, first_option):
+                    print u"还没有退到初始界面"
+                    reCount = reCount + 1
+                    # 如果仍然可以检测到聚焦效果,则继续执行return
+                    self.redRat3.sendKey("return")
+                else:
+                    printLog(u"回退到初始界面成功!")
+                    break
+        else:
+            print u"执行退出,执行退出按键次数returnKeyEventTimes:", returnKeyEventTimes
+            # 无论成功失败,要返回到初始界面
+            self.redRat3.sendKey("return", returnKeyEventTimes, 0.2)
+
+        if result:
+            printLog(u"设置option:%s 选中value:%s的值成功!" % (str(option), str(value)))
+        else:
+            printLog(u"重新设置option:%s 选中value:%s的值失败!" % (str(option), str(value)))
+        # 不再执行重试动作
+        # else:
+        #     if reTryTime < 1:
+        #         reTryTime = reTryTime + 1
+        #         result = self.setOptionValue(option, value, reTryTime=reTryTime)
+        #         if result:
+        #             printLog(u"重新设置option:%s 选中value:%s的值成功!" % (str(option), str(value)))
+        #             return True
+        #         else:
+        #             printLog(u"重新设置option:%s 选中value:%s的值失败!" % (str(option), str(value)))
+        #             return False
+        #     else:
+        #         printLog(u"设置option:%s 选中value:%s的值尝试2次仍失败!" % (str(option), str(value)))
+        #         return False
+        # 返回结果;
+        return result
+
+    # 对外接口:检测操作值是否正确;
+    def checkOptionValue(self, option, value, Max_Try=15, leafNodeValueWaitTime=0.0):
+        # 切黑场;
+        self.sourceInput.setPattern(11)
+        printLog(u"开始执行checkOptionValue。option:%s  value:%s" % (str(option), str(value)))
+        # 寻路结果;
+        result = False
+        # 获取menu path;
+        value_params, path_params = self.xlsparser.get_menu_paths(option, value)
+        printLog(u"获取到达option:%s的路径字典path_params:%s" % (str(option), str(path_params)))
+        printLog(u"获取检测value:%s的值字典value_params:%s" % (str(value), str(value_params)))
+
+        checkRunOptionPathResult = self.xlsparser.checkRunOptionPath(path_params)
+        if str(checkRunOptionPathResult) == "NoExit":
+            printLog(u"表格中不存在到达Option:   %s   的路径,在表格中排查到达该Option路径" % str(option))
+            return False
+        if str(checkRunOptionPathResult) == "Fail":
+            printLog(u"表格中到达Option:   %s   的路径出现数据断层找不到First层级,在表格中排查到达该Option路径" % str(option))
+            return False
+
+        # returnKeyEventCount = path_params.__len__() + 1
+        returnKeyEventCount = 0
+        print "returnKeyEventCount:", returnKeyEventCount
+        # 逆序路径key;
+        revpp = reversed(path_params)
+        # 根节点是否已进入;
+        entry = False
+        root = u''
+        first_parent = u''
+        first_option = u''
+        # 遍历路径(已排序,从first开始);
+        # print "revpp:", revpp
+        for level in revpp:
+            # print "level:", level, path_params[level]
+            printLog(u"执行路径level:%s  对应路径字典:%s" % (str(level), str(path_params[level])))
+            # 是否进入根结点;
+            if entry == False:
+                # 标记已进入根;
+                entry = True
+                first_parent = path_params[level]["parent"]
+                first_option = path_params[level]["option"]
+                checkFirstOptionTimes = 0
+                while True:
+                    if checkFirstOptionTimes > 2:
+                        errorPngName = "menutree_error_" + str(option) + '_' + str(value) + self.currentTime() + ".png"
+                        error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+                        self.ccard.takePicture(error_uiPic)
+                        printLog(u"执行checkOptionValue ERROR!option:%s  value:%s ;尝试3次快捷按键仍然没有唤出聚焦焦点界面!!!  出错截图:%s" % (
+                            str(option), str(value), str(error_uiPic)))
+                        return False
+                    printLog(u"第%s次尝试呼出一级界面" % str(checkFirstOptionTimes))
+                    # # 进入根节点(根节点名就是遥控键名);
+                    # self.redRat3.sendKey(path_params[level]["parent"])
+
+                    # 判断是否存在others字段,others字段中存在first_key,则优先使用first_key进入
+                    try:
+                        others = json.loads(path_params[level]["others"])
+                    except Exception,e:
+                        others = {}
+                    printLog("section others:%s"%others)
+                    if "first_key" in others.keys():
+                        first_key = others["first_key"]
+                    else:
+                        first_key = path_params[level]["parent"]
+                    printLog(u"进入根节点,first_key:%s"%first_key)
+                    # 进入根节点
+                    self.redRat3.sendKey(first_key)
+                    waitTime = self.getParentWaitTime(path_params[level]["parent"])
+                    printLog(u"打开%s界面的等待时间waittime为:%s" % (str(path_params[level]["parent"]), str(waitTime)))
+                    time.sleep(waitTime)
+                    # 获取ocr列表;
+                    self.get_ocr_list(level, option)
+                    if self.isFirstOptionShow(level, first_parent, first_option):
+                        printLog(u"呼出一级菜单界面")
+                        break
+                    else:
+                        time.sleep(5)
+                        self.redRat3.sendKey("return", 1)
+                        checkFirstOptionTimes = checkFirstOptionTimes + 1
+                # 如果一级界面弹出成功,则退出时候返回按键次数加1
+                returnKeyEventCount = returnKeyEventCount + 1
+
+            # 如果 others 字段不为空;
+            if path_params[level]["others"].__len__():
+                # 如果不是跑马灯处理则按照输入密码处理
+                if self.dealOthers(level, path_params[level]) is True:
+                    # print u"解锁失败!!!"
+                    printLog(u"执行checkOptionValue ERROR!option:%s  value:%s ;解锁失败!!!" % (str(option), str(value)))
+                    return False
+
+            # 移动到子节点;
+            cur_option_enter_key = path_params[level]["option_enter_key"]
+            # 移动到子节点;                
+            if cur_option_enter_key.__len__() == 0:
+                enter_key = path_params[level]["enter_key"]
+            else:
+                enter_key = cur_option_enter_key
+
+            move_key = path_params[level]["move_key"]
+
+            if str(move_key) == str([u'up', u'down']) or str(move_key) == str([u'down', u'up']):
+                findDirection = str(move_key[1])
+            elif str(move_key) == str([u'left', u'right']) or str(move_key) == str([u'right', u'left']):
+                findDirection = str(move_key[1])
+            else:
+                findDirection = 'grid'
+
+            result = self.move2TargetNode(
+                level,
+                first_parent,
+                path_params[level],
+                returnKeyEventCount,
+                findDirection=findDirection, Max_Try=Max_Try)
+
+            if result is True:
+                # 子节点的进入方式默认为ok;
+                # self.redRat3.sendKey('ok')
+                # 子节点的进入方式默认为ok;
+                if enter_key != u'default':
+                    self.redRat3.sendKey(enter_key)
+                    # 如果执行一次enter_key,则退出时按返回键的次数加一
+                    returnKeyEventCount = returnKeyEventCount + 1
+            else:
+                break
+        # end-for
+
+        # 寻路成功,设置叶节点值;
+        if result is True:
+            # 如果是信源选择   自动选择不需要执行 enter_key
+            if self.ocr.cmpOcrStr(option, 'source') or self.ocr.cmpOcrStr(option, 'ok'):
+                return True
+            time.sleep(leafNodeValueWaitTime)
+            result = self.checkLeafNodeValue(level, root, value_params["option"],
+                                             value_params["value"],
+                                             value_params["value_for_ocr"],
+                                             value)
+
+        print u"执行退出,执行退出按键次数returnKeyEventCount:", returnKeyEventCount
+        # 无论成功失败,要返回到初始界面
+        self.redRat3.sendKey("return", returnKeyEventCount, 0.2)
+        time.sleep(1)
+        # 检测执行回退键是否成功,吐过仍有聚焦效果则继续执行return键
+        reCount = 0
+        while True:
+            if reCount >= returnKeyEventCount:
+                printLog(u"执行了returnKeyEventCount:%s次,仍无法退出到初始界面" % str(returnKeyEventCount))
+                break
+            if self.isFirstOptionShow("First", first_parent, first_option):
+                reCount = reCount + 1
+                # 如果仍然可以检测到聚焦效果,则继续执行return
+                self.redRat3.sendKey("return")
+            else:
+                printLog(u"回退到初始界面成功!")
+                break
+
+        if result:
+            printLog(u"检测option:%s 选中value:%s的值成功!" % (str(option), str(value)))
+        else:
+            printLog(u"检测option:%s 选中value:%s的值失败!" % (str(option), str(value)))
+        # 返回结果;
+        return result
+
+    def checkLeafNodeValue(self, level, parent, option, value_excel, value_for_ocr, value):
+        # 获取等待时间
+        waitTime = self.getOptionWaitTime(option)
+        printLog(u"checkLeafNodeValue:获取到进入叶节点%s,等待时间为%s"%(option, waitTime))
+        time.sleep(waitTime)
+
+        value_for_ocr_low = str(value_for_ocr[0]).lower()
+        # 判断设置值的类型 是rang类型还是str类型
+        if "range(" in value_for_ocr_low:
+            isRangeType = True
+            str_num = value_for_ocr_low.replace("range", "")
+            list_num = eval(str_num)
+            min_num = list_num[0]
+            max_num = list_num[1]
+            try:
+                value_num = int(value)
+            except Exception:
+                printLog(u"传入的值value:%S和option:%s对应的类型不匹配 !" % (str(value), str(option)))
+                return False
+        else:
+            isRangeType = False
+        if isRangeType:
+            # 设置数值范围类型的处理
+            isFind, text, text_num = self.getCurrentFocusOcr_Int(level, parent, option=option, isForValue=True)
+            if not isFind:
+                errorPngName = "menutree_error_" + str(option) + str(value_excel) + self.currentTime() + ".png"
+                error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+                self.ccard.takePicture(error_uiPic)
+                printLog(u"检测叶子节点值时,当前界面无法找到焦点区域!!!出错界面截图%s" % str(error_uiPic))
+                return False
+            else:
+                pass
+                try:
+                    list_text_num = getDigitFromString(text)
+                    if list_text_num.__len__() < 1:
+                        errorPngName = "menutree_error_" + str(option) + '_' + str(
+                            value_excel) + self.currentTime() + ".png"
+                        error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+                        self.ccard.takePicture(error_uiPic)
+                        printLog(u"检测叶子节点值时,当前option:%s 识别出来的值text:%s 不包含int类型值,截图为:%s" % (
+                            str(option), str(text), str(error_uiPic)))
+                        return False
+                    text_int_str = list_text_num[list_text_num.__len__() - 1]
+                    # print 'text_int_str:', text_int_str
+                    text_num = int(text_int_str)
+                    printLog(u"检测到的值text_num:%s" % str(text_num))
+                except Exception:
+                    errorPngName = "menutree_error_" + str(option) + '_' + str(
+                        value_excel) + self.currentTime() + ".png"
+                    error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+                    self.ccard.takePicture(error_uiPic)
+                    printLog(u"检测叶子节点值时,当前option:%s 识别出来的值text:%s 不能转换成value_for_ocr:%s范围内的int类型,截图为:%s" % (
+                        str(option), str(text_int_str), str(value_for_ocr), str(error_uiPic)))
+                    return False
+                if text_num < min_num or text_num > max_num:
+                    errorPngName = "menutree_error_" + str(option) + '_' + str(
+                        value_excel) + self.currentTime() + ".png"
+                    error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+                    self.ccard.takePicture(error_uiPic)
+                    printLog(u"检测叶子节点值时,当前option:%s 识别出来的值text:%s 不在value_for_ocr:%s范围内,截图为:%s" % (
+                        str(option), str(text), str(value_for_ocr), str(error_uiPic)))
+                    return False
+                # if abs(text_num) == abs(value_num):
+                if text_num == value_num:
+                    printLog(u"检测叶子节点值时,聚焦到目标option:%s,检测value:%s选项成功!" % (str(option), str(value_excel)))
+                    return True
+                else:
+                    return False
+        else:
+            # 设置选项类型的处理
+            isFind, text = self.getCurrentFocusTextEx(level, parent, option, value_excel, value_for_ocr,
+                                                      isForValue=True)
+            if not isFind:
+                errorPngName = "menutree_error_" + str(option) + '_' + str(value_excel) + self.currentTime() + ".png"
+                error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+                self.ccard.takePicture(error_uiPic)
+                printLog(u"检测叶子节点值时,当前界面无法找到焦点区域!!!出错界面截图:%s" % str(error_uiPic))
+                return False
+            # elif (self.ocr.cmpOcrStr(text, value_for_ocr)):
+            # 由比较目标文本和识别文本 是否相等  改为 识别文本是否包含目标文本
+            else:
+                for value_for_ocr_text in value_for_ocr:
+                    value_for_ocr_text_low = str(value_for_ocr_text).lower()
+                    if value_for_ocr_text_low in text:
+                        printLog(u"检测叶子节点值时,聚焦到目标option:%s,检测value:%s选项成功!" % (str(option), str(value_excel)))
+                        return True
+                errorPngName = "menutree_error_" + str(option) + '_' + str(value_excel) + self.currentTime() + ".png"
+                error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+                self.ccard.takePicture(error_uiPic)
+                printLog(u"检测叶子节点值时,聚焦到目标option:%s,检测value:%s选项失败!出错界面截图:%s" % (
+                    str(option), str(value_excel), str(error_uiPic)))
+                return False
+
+    # 对外接口:信源设置接口;本方法(先识别再切换)NT72项目由于界面消失太快不适用,下面封的接口逻辑(先切换再识别)
+    # def setSourceValue(self, option, value, sourceWaitTime=1.0, Max_Try=10):
+    #     printLog(u"开始执行setSourceValue。option:%s  value:%s" % (str(option), str(value)))
+    #     # self.get_ocr_list('First', 'source')
+    #     # 获取menu path;
+    #     value_params, path_params = self.xlsparser.get_menu_paths(option, value)
+    #     # print "value_params:", value_params
+    #     printLog(u"获取设置信源value:%s的值字典value_params:%s" % (str(value), str(value_params)))
+    #     value_for_ocr = value_params['value_for_ocr']
+    #
+    #     old_text = "init_old_text"
+    #
+    #     enter_key = value_params["enter_key"]
+    #     move_key = value_params["move_key"]
+    #     if str(move_key) == str([u'up', u'down']):
+    #         findDirection = "down"
+    #         findReverseDirection = "up"
+    #     else:
+    #         findDirection = "right"
+    #         findReverseDirection = "left"
+    #     # 正向寻找的次数计数和最大次数
+    #     count = 0
+    #     Max_Try = Max_Try
+    #     # 反向寻找的次数计数和最大次数
+    #     Reversecount = 0
+    #     Reverse_Max_Try = Max_Try * 2
+    #     sourceWaitTime = self.tconfig.get_value("waitTime", "sourceWaitTime")
+    #     if not sourceWaitTime:
+    #         sourceWaitTime = 1.0
+    #     while True:
+    #         if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+    #             return False
+    #         time.sleep(2)
+    #         self.redRat3.sendKey('source')
+    #         time.sleep(sourceWaitTime)
+    #         isFind, text = self.getCurrentFocusTextEx('First', 'source', 'source', option, value_for_ocr, True)
+    #         if isFind == -1:
+    #             # 6586自研机芯USB进入之后无法唤出信源 按return再一次按source呼出界面
+    #             self.redRat3.sendKey("return")
+    #             self.redRat3.sendKey('source')
+    #             time.sleep(0.2)
+    #             isFind, text = self.getCurrentFocusTextEx('First', 'source', 'source', option, value_for_ocr, True)
+    #             if isFind == -1:
+    #                 printLog(u"当前界面找不到焦点!即source没有调出信源界面")
+    #                 return False
+    #
+    #         if isFind == 1:
+    #             printLog(u"聚焦到目标option:%s,设置信源value:%s选项成功!" % (str(option), str(value)))
+    #             # 聚焦成功发送enter_key
+    #             self.redRat3.sendKey('ok')
+    #             return True
+    #             # 判断两次识别
+    #         print "text:", text, "old_text:", old_text, self.ocr.cmpOcrStr(text, old_text)
+    #
+    #         # 判断信源界面有没有消失  如果消失再次按出信源界面
+    #         isFind_source, text = self.getCurrentFocusTextEx('First', 'source', 'source', option, value_for_ocr, True)
+    #         if isFind_source == -1:
+    #             self.redRat3.sendKey('source')
+    #             time.sleep(sourceWaitTime)
+    #
+    #         # 判断两次识别的text是否相等来判断是否到达边界
+    #         if self.ocr.cmpOcrStr(text, old_text):
+    #             # 如果已经到达边界则不再正方向寻找焦点
+    #             count = Max_Try
+    #             old_text = "init_old_text"
+    #         else:
+    #             old_text = text
+    #
+    #         if count < Max_Try:
+    #             count = count + 1
+    #             self.redRat3.sendKey(findDirection)
+    #             self.redRat3.sendKey('ok')
+    #         else:
+    #             Reversecount = Reversecount + 1
+    #             self.redRat3.sendKey(findReverseDirection)
+    #             self.redRat3.sendKey('ok')
+    #
+    #     errorPngName = "menutree_error_" + str(option) + '_' + str(value) + self.currentTime() + ".png"
+    #     error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+    #     self.ccard.takePicture(error_uiPic)
+    #     printLog(u"设置信源值时,正向遍历%s次,和反向遍历%s次都没有聚焦到option:%s的value:%s选项中,截图为%s" % (
+    #         str(Max_Try), str(Reverse_Max_Try), str(option), str(value), str(error_uiPic)))
+    #     return False
+
+    # 对外接口:信源设置接口;  先切换再识别
+    '''
+    不允许切换信源,在MenuTree.ini文件中配置:source={"offset":20,"minPeri":0,"maxPeri":0,"minArea":0,"maxArea":0,"enable":0}。
+    enable没有,默认值enable=1;enable=0,表示不允许切换信源,仅判断是否为目标信源。
+    '''
+
+    def setSourceValue(self, option, value, sourceWaitTime=1.0, Max_Try=10):
+        # 切黑场;
+        self.sourceInput.setPattern(11)
+        return self.tSource.setSourceValue(option, value, sourceWaitTime, Max_Try)
+
+    def inputUnlock(self, std_str, password=''):
+        # 如果锁住,按ok会弹出输入密码框;
+        self.redRat3.sendKey("ok")
+        # 获取密码;
+        if password.__len__() == 0:
+            password = self.tconfig.get_value("Password", "super")
+        # 发送按键;
+        for key in list(password):
+            self.redRat3.sendKey(key, 1, 0.2)
+        time.sleep(1)
+        # 发送ok键;
+        self.redRat3.sendKey('ok')
+        # 判断是否成功输入密码;
+        current_uiPic = self.getCurrentUIPath()
+        self.get_ocr_list("", "")
+        # 遍历ocr类型;
+        found = False
+        for item in self.ocrDict:
+            # 识别ocr;
+            ocr_str = self.ocr.getStrWithImgProcess(current_uiPic, {}, item["lan"], item["type"], reconTimes=1)
+            ocr_str = unicode(ocr_str).lower()
+            # print("lan=%s,type=%d,ocr=%s" % (item["lan"], item["type"], ocr_str))
+            printLog("lan=%s,type=%d,ocr=%s" % (item["lan"], item["type"], ocr_str))
+            if ocr_str in std_str or std_str == ocr_str:
+                found = True
+                break
+        return not found
+
+    def openOption(self, option, Max_Try=15):
+        # 切黑场;
+        self.sourceInput.setPattern(11)
+        # 获取menu path;
+        print "openOption start >>>"
+        path_params = self.xlsparser.get_option_paths(option)
+        # print 'openOption=path_params:', path_params
+        printLog('openOption=path_params:%s' % str(path_params))
+
+        checkRunOptionPathResult = self.xlsparser.checkRunOptionPath(path_params)
+        if str(checkRunOptionPathResult) == "NoExit":
+            printLog(u"表格中不存在到达Option:   %s   的路径,在表格中排查到达该Option路径" % str(option))
+            return False
+        if str(checkRunOptionPathResult) == "Fail":
+            printLog(u"表格中到达Option:   %s   的路径出现数据断层找不到First层级,在表格中排查到达该Option路径" % str(option))
+            return False
+
+        # returnKeyEventCount = path_params.__len__() + 1
+        returnKeyEventCount = 0
+        print "returnKeyEventCount:", returnKeyEventCount
+        printLog("openOption=returnKeyEventCount:%s" % str(returnKeyEventCount))
+        # 逆序路径key;
+        revpp = reversed(path_params)
+        # 根节点是否已进入;
+        entry = False
+        first_parent = u''
+        first_option = u''
+        # 遍历路径(已排序,从first开始);
+        # print "revpp:", revpp
+        printLog("openOption=revpp:%s" % str(revpp))
+        for level in revpp:
+            # print "openOption=level:", path_params[level]
+            printLog("openOption=level:", path_params[level])
+            # 是否进入根结点;
+            if entry == False:
+                # 标记已进入根;
+                entry = True
+                first_parent = path_params[level]["parent"]
+                first_option = path_params[level]["option"]
+                checkFirstOptionTimes = 0
+                while True:
+                    if checkFirstOptionTimes > 2:
+                        printLog(u"openOption=尝试3次快捷按键没有唤出聚焦焦点界面!!!")
+                        return False
+
+                    printLog(u"第%s次尝试呼出一级界面" % str(checkFirstOptionTimes))
+                    # # 进入根节点(根节点名就是遥控键名);
+                    # self.redRat3.sendKey(path_params[level]["parent"])
+                    # 判断是否存在others字段,others字段中存在first_key,则优先使用first_key进入
+                    try:
+                        others = json.loads(path_params[level]["others"])
+                    except Exception,e:
+                        others = {}
+                    printLog("section others:%s"%others)
+                    if "first_key" in others.keys():
+                        first_key = others["first_key"]
+                    else:
+                        first_key = path_params[level]["parent"]
+                    printLog(u"进入根节点,first_key:%s"%first_key)
+                    # 进入根节点
+                    self.redRat3.sendKey(first_key)
+                    waitTime = self.getParentWaitTime(path_params[level]["parent"])
+                    printLog(u"打开%s界面的等待时间waittime为:%s" % (str(path_params[level]["parent"]), str(waitTime)))
+                    time.sleep(waitTime)
+                    # 获取ocr列表;
+                    self.get_ocr_list(level, option)
+                    if self.isFirstOptionShow(level, first_parent, first_option):
+                        printLog(u"呼出一级菜单界面")
+                        break
+                    else:
+                        time.sleep(5)
+                        self.redRat3.sendKey("return", 2)
+                        checkFirstOptionTimes = checkFirstOptionTimes + 1
+                # 如果一级界面弹出成功,则退出时候返回按键次数加1
+                returnKeyEventCount = returnKeyEventCount + 1
+
+            # 如果 others 字段不为空;
+            if path_params[level]["others"].__len__():
+                # 如果不是跑马灯处理则按照输入密码处理
+                if self.dealOthers(level, path_params[level]) is True:
+                    # print u"解锁失败!!!"
+                    printLog(u"openOption=解锁失败!!!")
+                    return False
+
+            # 移动到子节点;
+            cur_option_enter_key = path_params[level]["option_enter_key"]
+            # 移动到子节点;                
+            if cur_option_enter_key.__len__() == 0:
+                enter_key = path_params[level]["enter_key"]
+            else:
+                enter_key = cur_option_enter_key
+
+            move_key = path_params[level]["move_key"]
+
+            if str(move_key) == str([u'up', u'down']) or str(move_key) == str([u'down', u'up']):
+                findDirection = str(move_key[1])
+            elif str(move_key) == str([u'left', u'right']) or str(move_key) == str([u'right', u'left']):
+                findDirection = str(move_key[1])
+            else:
+                findDirection = 'grid'
+
+            result = self.move2TargetNode(
+                level,
+                first_parent,
+                path_params[level],
+                returnKeyEventCount,
+                findDirection=findDirection, Max_Try=Max_Try)
+
+            if result is True:
+                # 子节点的进入方式默认为ok;
+                self.redRat3.sendKey(enter_key)
+                # 如果执行一次enter_key,则退出时按返回键的次数加一
+                if enter_key != u'default':
+                    returnKeyEventCount = returnKeyEventCount + 1
+            else:
+                print "openOption end<<<<"
+                return False
+        # end-for
+        print "openOption end <<<<"
+        return True
+
+    # 对外接口:检测频道列表;
+    def checkChannelList(self, channelList, Max_Try=15):
+        failChannelList = []
+        checkResult = True
+        if channelList == []:
+            printLog(u"传入的待检测的频道列表为空!")
+            return False, failChannelList
+        for channel in channelList:
+            isSearched = self.checkOptionValue("ok", channel, Max_Try=Max_Try)
+            if not isSearched:
+                printLog(u"频道:%s查找失败!" % str(channel))
+                failChannelList.append(channel)
+                checkResult = False
+                # 部分机芯项目(如6586外协松下)按return键无法退出频道,故改为exit键
+                # self.redRat3.sendKey("return")
+                self.redRat3.sendKey("exit")
+            else:
+                printLog(u"频道:%s查找成功!" % str(channel))
+            self.redRat3.sendKey("exit")
+        printLog(u"频道列表检测结果checkResult:%s,检测失败频道列表failChannelLis:%s" % (str(checkResult), str(failChannelList)))
+        return checkResult, failChannelList
+
+    # 对外唯一接口:设置操作值;
+    def focusOptionValue(self, option, value, Max_Try=15, leafNodeValueWaitTime=0.0):
+        # 切黑场;
+        self.sourceInput.setPattern(11)
+        printLog(u"focusOptionValue。option:%s  value:%s" % (str(option), str(value)))
+        # 寻路结果;
+        result = False
+        # 获取menu path;
+        value_params, path_params = self.xlsparser.get_menu_paths(option, value)
+        printLog(u"获取到达option:%s的路径字典path_params:%s" % (str(option), str(path_params)))
+        printLog(u"获取聚焦value:%s的值字典value_params:%s" % (str(value), str(value_params)))
+
+        checkRunOptionPathResult = self.xlsparser.checkRunOptionPath(path_params)
+        if str(checkRunOptionPathResult) == "NoExit":
+            printLog(u"表格中不存在到达Option:   %s   的路径,在表格中排查到达该Option路径" % str(option))
+            return False
+        if str(checkRunOptionPathResult) == "Fail":
+            printLog(u"表格中到达Option:   %s   的路径出现数据断层找不到First层级,在表格中排查到达该Option路径" % str(option))
+            return False
+
+        # returnKeyEventCount = path_params.__len__() + 1
+        returnKeyEventCount = 0
+        print "returnKeyEventCount:", returnKeyEventCount
+        # 逆序路径key;
+        revpp = reversed(path_params)
+        # 根节点是否已进入;
+        entry = False
+        root = u''
+        first_parent = u''
+        first_option = u''
+        # 遍历路径(已排序,从first开始);
+        # print "revpp:", revpp
+        for level in revpp:
+            # print "level:", level, path_params[level]
+            printLog(u"执行路径level:%s  对应路径字典:%s" % (str(level), str(path_params[level])))
+            # 是否进入根结点;
+            if entry == False:
+                # 标记已进入根;
+                entry = True
+                first_parent = path_params[level]["parent"]
+                first_option = path_params[level]["option"]
+                checkFirstOptionTimes = 0
+                while True:
+                    if checkFirstOptionTimes > 2:
+                        errorPngName = "menutree_error_" + str(option) + '_' + str(value) + self.currentTime() + ".png"
+                        error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+                        self.ccard.takePicture(error_uiPic)
+                        printLog(u"执行focusOptionValue ERROR!option:%s  value:%s ;尝试3次快捷按键仍然没有唤出聚焦焦点界面!!!  出错截图:%s" % (
+                            str(option), str(value), str(error_uiPic)))
+                        return False
+                    printLog(u"第%s次尝试呼出一级界面" % str(checkFirstOptionTimes))
+                    # # 进入根节点(根节点名就是遥控键名);
+                    # self.redRat3.sendKey(path_params[level]["parent"])
+
+                    # 判断是否存在others字段,others字段中存在first_key,则优先使用first_key进入
+                    try:
+                        others = json.loads(path_params[level]["others"])
+                    except Exception,e:
+                        others = {}
+                    printLog("section others:%s"%others)
+                    if "first_key" in others.keys():
+                        first_key = others["first_key"]
+                    else:
+                        first_key = path_params[level]["parent"]
+                    printLog(u"进入根节点,first_key:%s"%first_key)
+                    # 进入根节点
+                    self.redRat3.sendKey(first_key)
+                    waitTime = self.getParentWaitTime(path_params[level]["parent"])
+                    printLog(u"打开%s界面的等待时间waittime为:%s" % (str(path_params[level]["parent"]), str(waitTime)))
+                    time.sleep(waitTime)
+                    # 获取ocr列表;
+                    self.get_ocr_list(level, option)
+                    if self.isFirstOptionShow(level, first_parent, first_option):
+                        printLog(u"呼出一级菜单界面")
+                        break
+                    else:
+                        time.sleep(5)
+                        self.redRat3.sendKey("return", 2)
+                        checkFirstOptionTimes = checkFirstOptionTimes + 1
+                # 如果一级界面弹出成功,则退出时候返回按键次数加1
+                returnKeyEventCount = returnKeyEventCount + 1
+            print "level:", level, "root:", root
+            # 如果 others 字段不为空;
+            if path_params[level]["others"].__len__():
+                # 如果不是跑马灯处理则按照输入密码处理
+                if self.dealOthers(level, path_params[level]) is True:
+                    # print u"解锁失败!!!"
+                    printLog(u"执行setOptionValue ERROR!option:%s  value:%s ;解锁失败!!!" % (str(option), str(value)))
+                    return False
+
+            # 移动到子节点;
+            cur_option_enter_key = path_params[level]["option_enter_key"]
+            # 移动到子节点;                
+            if cur_option_enter_key.__len__() == 0:
+                enter_key = path_params[level]["enter_key"]
+            else:
+                enter_key = cur_option_enter_key
+
+            move_key = path_params[level]["move_key"]
+
+            if str(move_key) == str([u'up', u'down']) or str(move_key) == str([u'down', u'up']):
+                findDirection = str(move_key[1])
+            elif str(move_key) == str([u'left', u'right']) or str(move_key) == str([u'right', u'left']):
+                findDirection = str(move_key[1])
+            else:
+                findDirection = 'grid'
+
+            result = self.move2TargetNode(
+                level,
+                first_parent,
+                path_params[level],
+                returnKeyEventCount,
+                findDirection=findDirection, Max_Try=Max_Try)
+
+            if result is True:
+                # 子节点的进入方式默认为ok;
+                # self.redRat3.sendKey('ok')
+                # 如果只一层,不进行enter_key执行
+                if path_params.__len__() != 1:
+                    self.redRat3.sendKey(enter_key)
+                    # 如果执行一次enter_key,则退出时按返回键的次数加一
+                    if enter_key != u'default':
+                        returnKeyEventCount = returnKeyEventCount + 1
+            else:
+                break
+        # end-for
+        # 如果只一层不进行focusLeafNodeValue设置
+        if path_params.__len__() != 1:
+
+            # 寻路成功,设置叶节点值;
+            if result is True:
+                # 如果是信源选择   自动选择不需要执行 enter_key
+                if self.ocr.cmpOcrStr(option, 'source'):
+                    return True
+                time.sleep(leafNodeValueWaitTime)
+                print u"执行设置叶节点:", value_params
+                result = self.focusLeafNodeValue(
+                    level,
+                    root,
+                    value_params["option"],
+                    value_params["value"],
+                    value_params["value_for_ocr"],
+                    value,
+                    value_params["move_key"],
+                    value_params["enter_key"],
+                    Max_Try=Max_Try)
+        # 无论成功失败,要返回到初始界面
+
+        if result:
+            # 如果执行成功则不返回到初始界面
+            printLog(u"设置option:%s 选中value:%s的值成功!" % (str(option), str(value)))
+        else:
+            printLog(u"设置option:%s 选中value:%s的值失败!" % (str(option), str(value)))
+            print u"执行退出,执行退出按键次数returnKeyEventCount:", returnKeyEventCount
+            # 执行聚焦失败时候,返回到初始界面
+            self.redRat3.sendKey("return", returnKeyEventCount, 0.2)
+            time.sleep(1)
+            # 检测执行回退键是否成功,吐过仍有聚焦效果则继续执行return键
+            reCount = 0
+            while True:
+                if reCount >= returnKeyEventCount:
+                    printLog(u"执行了returnKeyEventCount:%s次,仍无法退出到初始界面" % str(returnKeyEventCount))
+                    break
+                if self.isFirstOptionShow("First", first_parent, first_option):
+                    reCount = reCount + 1
+                    # 如果仍然可以检测到聚焦效果,则继续执行return
+                    self.redRat3.sendKey("return")
+                else:
+                    printLog(u"回退到初始界面成功!")
+                    break
+
+        # 返回结果;
+        return result
+
+    # 设置叶子节点值;
+    def focusLeafNodeValue(self, level, parent, option, value_excel, value_for_ocr, value, move_key, enter_key,
+                           Max_Try=15):
+        # 按照传入的方向寻找Max_Try次,如果仍未聚焦到option_for_ocr所在的区域,则按照传入方向的反方向 反向寻找 2*Max_Try 次
+
+        # 正向寻找的次数计数和最大次数
+        count = 0
+        Max_Try = Max_Try
+        # 反向寻找的次数计数和最大次数
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try * 2
+        # 记录上一个焦点识别的文字内容   用于判断是否到达列表的边界 如果到达边界则反向寻找
+        old_text = "init_old_text"
+
+        # 正向寻找的遥控器按键值
+        findDirection = move_key[1]  # 向右或者向下
+        # 反向寻找的遥控器按键值
+        findReverseDirection = move_key[0]  # 向左或者向上
+
+        # 获取等待时间
+        waitTime = self.getLeafWaitTime(option)
+        printLog(u"focusLeafNodeValue:获取到进入叶节点%s,等待时间为%s"%(option, waitTime))
+        time.sleep(waitTime)
+
+        value_for_ocr_low = str(value_for_ocr[0]).lower()
+        # 判断设置值的类型 是rang类型还是str类型
+        if "range(" in value_for_ocr_low:
+            isRangeType = True
+            str_num = value_for_ocr_low.replace("range", "")
+            list_num = eval(str_num)
+            min_num = list_num[0]
+            max_num = list_num[1]
+            try:
+                value_num = int(value)
+            except Exception:
+                printLog(u"传入的值value:%s和option:%s对应的类型不匹配 !" % (str(value), str(option)))
+                return False
+        else:
+            isRangeType = False
+        if isRangeType:
+            # 如果是数值类型,则直接返回聚焦成功
+            return True
+        else:
+            # 设置选项类型的处理
+            while (True):
+                print "count:", count
+                if count >= Max_Try and Reversecount >= Reverse_Max_Try:
+                    break
+                isFind, text = self.getCurrentFocusTextEx(level, parent, option, value_excel, value_for_ocr,
+                                                          isForValue=True)
+                if isFind == -1:
+                    errorPngName = "menutree_error_" + str(option) + '_' + str(
+                        value_excel) + self.currentTime() + ".png"
+                    error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+                    self.ccard.takePicture(error_uiPic)
+                    printLog(u"聚焦叶子节点值时,当前界面无法找到焦点区域!!!出错界面截图%s" % str(error_uiPic))
+                    return False
+                # elif (self.ocr.cmpOcrStr(text, value_for_ocr)):
+                # 由比较目标文本和识别文本 是否相等  改为 识别文本是否包含目标文本
+                else:
+                    if isFind == 1:
+                        printLog(u"聚焦到目标option:%s,设置value:%s选项成功!" % (str(option), str(value_excel)))
+                        # 聚焦到某个焦点成功,由于是聚焦接口调用,不用send  enter_key进入到焦点
+
+                        # if enter_key != 'default':
+                        #     self.redRat3.sendKey(enter_key)
+                        return True
+                        # 判断两次识别
+                    # 判断两次识别的text是否相等来判断是否到达边界
+                    if self.ocr.cmpOcrStr(text, old_text):
+                        # 如果已经到达边界则不再正方向寻找焦点
+                        count = Max_Try
+                        old_text = "init_old_text"
+                    else:
+                        old_text = text
+
+                    if count < Max_Try:
+                        count = count + 1
+                        self.redRat3.sendKey(findDirection)
+                    else:
+                        Reversecount = Reversecount + 1
+                        self.redRat3.sendKey(findReverseDirection)
+            errorPngName = "menutree_error_" + str(option) + '_' + str(value_excel) + self.currentTime() + ".png"
+            error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+            self.ccard.takePicture(error_uiPic)
+            printLog(u"聚焦叶子节点值时,正向遍历%s次,和反向遍历%s次都没有聚焦到option:%s的value:%s选项中,截图为%s" % (
+                str(Max_Try), str(Reverse_Max_Try), str(option), str(value_excel), str(error_uiPic)))
+            return False
+
+        return True
+
+    def getOptionValue(self, option, is_value_sheet=True):
+        # 切黑场;
+        self.sourceInput.setPattern(11)
+        # 获取路径参数;
+        result, option_data = self.xlsparser.get_option(option)
+        if result is False:
+            print(u"getOptionValue.获取option菜单内容失败")
+            return False, ''
+        first_parent = option_data['first_parent']
+        thresholdDict = self.tconfig.getThresholdDict(first_parent)
+
+        # 根据parent打开特定的option;
+        if self.openOption(option) is False:
+            self.redRat3.sendKey('return', option_data['layers'] + 1, 0.2)
+            print(u"getOptionValue.进入指定option失败")
+            return False, ''
+
+        # 获取等待时间
+        waitTime = self.getOptionWaitTime(option)
+        printLog(u"getOptionValue:获取到进入叶节点%s,等待时间为%s"%(option, waitTime))
+        time.sleep(waitTime)
+
+        time.sleep(0.5)
+        # 截图;
+        current_uiPic = self.getCurrentUIPath()
+        # 获取焦点框;
+        result, opcfg = self.tFocus.getOptionConfig(option, is_value_sheet)
+        if result is False:
+            self.redRat3.sendKey('return', option_data['layers'] + 1, 0.2)
+            print(u"getOptionValue.获取焦点参数失败")
+            return False, ''
+
+        dcfg = opcfg['dcfg']
+        icon_path = opcfg['icon_path']
+        icon_dir_path = opcfg['dir_path']
+        # 获取聚集框;
+        result, cur_box = self.tFocus.findFocusByIcon(current_uiPic, icon_path, dcfg)
+        if result is False:
+            self.redRat3.sendKey('return', option_data['layers'] + 1, 0.2)
+            print(u"getOptionValue.获取焦点框轮廓失败")
+            return False, ''
+
+        # 根据dir计算文本框;
+        cur_box = self.getFocusTextBox4(icon_dir_path, option, cur_box, is_value_sheet)
+
+        # 获取文本区域截图;
+        ocr_result_text = ''
+        txt_pic = os.path.join(getSATTmpDIR(), "meuttree_area_text.png")
+        self.imgCMP.saveCropPic(current_uiPic, txt_pic, (cur_box[0], cur_box[1], cur_box[2], cur_box[3]))
+        if is_value_sheet is True:
+            value_paramters = self.xlsparser.get_value(option, '')
+            isRangeType = False
+            # 判断设置值的类型 是rang类型还是str类型
+            if value_paramters['value_for_ocr'].__len__() and "range(" in value_paramters['value_for_ocr'][0].lower():
+                isRangeType = True
+
+            if isRangeType is True:
+                # 获取ocr列表;
+                hasOption,path = self.xlsparser.get_option(option)
+                if hasOption:
+                    self.get_ocr_list(path['level'], option)
+                for orc_item in self.ocrDict:
+                    # ocr_result = self.ocr.getStr(txt_pic, orc_item["lan"], orc_item["type"])
+                    ocr_result = self.ocr.getStrWithImgProcess(txt_pic, thresholdDict, orc_item["lan"],
+                                                               orc_item["type"],
+                                                               reconTimes=1)
+                    # print("getCurrentFocusOcr_Int=ocr识别结果=%s" % (ocr_result))
+                    printLog("getCurrentFocusOcr_Int=ocr识别结果=%s" % (ocr_result))
+                    if ocr_result == "ERR<Exp>" or ocr_result.__len__() == 0:
+                        continue
+                    # 字符串去掉末尾">"符号;
+                    ocr_result = ocr_result.strip('>')
+                    # 按数字分解多组文本;
+                    list_ocr = getDigitFromString(ocr_result)
+                    # list_ocr = self.strSplit(ocr_result)
+                    # 只判断最后一位是否是数字;
+                    if list_ocr.__len__() < 1:
+                        errorPngName = "menutree_error_" + self.currentTime() + "ocr.png"
+                        error_uiPic = os.path.join(getSATTmpDIR(), errorPngName)
+                        self.ccard.takePicture(error_uiPic)
+                        # print u"当前识别的ocr 不包含int类型值,截图为:%s" % (str(ocr_result))
+                        printLog(u"当前识别的ocr 不包含int类型值,截图为:%s" % (str(ocr_result)))
+                        continue
+
+                    try:
+                        # 获取最右边的文本,如果是数字则退出;
+                        text_int_str = list_ocr[list_ocr.__len__() - 1]
+                        # print 'text_int_str:', text_int_str
+                        printLog('text_int_str:%s' % str(text_int_str))
+                        ocr_result_text = float(text_int_str)
+                        break
+                    except Exception:
+                        continue
+            else:
+                # 普通ocr文本识别;
+                # ocr_result_text = self.ocr.getStr(txt_pic, "ChinesePRC+English", 4)
+                ocr_result_text = self.ocr.getStrWithImgProcess(txt_pic, thresholdDict, "ENG", 10000,
+                                                                reconTimes=1)
+        else:
+            # 普通ocr文本识别;
+            # ocr_result_text = self.ocr.getStr(txt_pic, "ChinesePRC+English", 4)
+            ocr_result_text = self.ocr.getStrWithImgProcess(txt_pic, thresholdDict, "ENG", 10000,
+                                                            reconTimes=1)
+        # 退出菜单;
+        self.redRat3.sendKey('return', option_data['layers'] + 1, 0.2)
+
+        # 返回结果;
+        return True, ocr_result_text
+
+    def getParentWaitTime(self, parent):
+        optionName = parent + "WaitTime"
+        try:
+            waitTime = float(self.tconfig.get_value("waitTime", optionName))
+        except Exception:
+            waitTime = 1.0
+            # print "获取%s界面的等待界面时间失败,使用默认值1.0s" % str(parent)
+        return waitTime
+
+    def getOptionWaitTime(self, option):
+        result, option_data = self.xlsparser.get_option(option)
+        print u"getOptionWaitTime.option_data:",option_data
+        if result:
+            try:
+                others = json.loads(option_data['others'])
+                waitTime = float(others['waitTime'])
+            except Exception,e:
+                print u"获取%s界面的等待时间失败,使用默认值1.0s" % str(option)
+                waitTime = 1.0
+        else:
+            waitTime = 1.0
+        return waitTime
+
+    def getLeafWaitTime(self, option):
+        leaf_data = self.xlsparser.get_value(option)
+        print u"getLeafWaitTime.option_data:",leaf_data
+        try:
+            others = json.loads(leaf_data['others'])
+            waitTime = float(others['waitTime'])
+        except Exception,e:
+            print u"获取%s界面的等待时间失败,使用默认值1.0s" % str(option)
+            waitTime = 1.0
+        return waitTime
+
+
+    # channel_count:如果有频道,最大可能的频道数量;
+    # black_th:黑色频道背景色RGB单通道值
+    # black_rate:图片分割成100块,纯黑色区域占有比
+    def getBlackChannel(self, channel_count, black_th=20, black_rate=0.8):
+        result = False
+        # 根据频道数量遍历;
+        for i in range(0, channel_count):
+            pass
+            # 截图,取左半边;
+            pic_path = self.getCurrentUIPath()
+            img = cv.imread(pic_path)
+            pic_path2 = os.path.join(getSATTmpDIR(), "menutree_focus_area_half.png")
+            self.imgCMP.saveCropPic(pic_path, pic_path2, (0, 0, img.shape[1] / 2, img.shape[0]))
+            # 是否符合要求;
+            result = self.imgCMP.isBlack(pic_path2, black_th, 10, 1 - black_rate)
+            if result is True:
+                break
+
+            # 下一频道号;
+            self.redRat3.sendKey('C+')
+            time.sleep(4)
+        # endfor
+
+        return result
+
+    # 新增操作接口
+
+    # 聚焦到Option   区别于openOption,focusOption不会执行到option的子界面中
+    def focusOption(self, option, Max_Try=15):
+        # 切黑场;
+        self.sourceInput.setPattern(11)
+        # 获取menu path;
+        print u"focusOption start >>>"
+        path_params = self.xlsparser.get_option_paths(option)
+        # print 'openOption=path_params:', path_params
+        printLog('focusOption=path_params:%s' % str(path_params))
+
+        checkRunOptionPathResult = self.xlsparser.checkRunOptionPath(path_params)
+        if str(checkRunOptionPathResult) == "NoExit":
+            printLog(u"表格中不存在到达Option:   %s   的路径,在表格中排查到达该Option路径" % str(option))
+            return False
+        if str(checkRunOptionPathResult) == "Fail":
+            printLog(u"表格中到达Option:   %s   的路径出现数据断层找不到First层级,在表格中排查到达该Option路径" % str(option))
+            return False
+        runPathCount = 0
+        pathCount = path_params.__len__()
+        printLog("focusOption=pathCount:%s" % str(pathCount))
+
+        # returnKeyEventCount = path_params.__len__() + 1
+        returnKeyEventCount = 0
+        print "returnKeyEventCount:", returnKeyEventCount
+        printLog("focusOption=returnKeyEventCount:%s" % str(returnKeyEventCount))
+        # 逆序路径key;
+        revpp = reversed(path_params)
+        # 根节点是否已进入;
+        entry = False
+        first_parent = u''
+        first_option = u''
+        # 遍历路径(已排序,从first开始);
+        # print "revpp:", revpp
+        printLog("focusOption=revpp:%s" % str(revpp))
+        for level in revpp:
+            # print "openOption=level:", path_params[level]
+            printLog("focusOption=level:", path_params[level])
+            # 是否进入根结点;
+            if entry == False:
+                # 标记已进入根;
+                entry = True
+                first_parent = path_params[level]["parent"]
+                first_option = path_params[level]["option"]
+                checkFirstOptionTimes = 0
+                while True:
+                    if checkFirstOptionTimes > 2:
+                        printLog(u"focusOption=尝试3次快捷按键没有唤出聚焦焦点界面!!!")
+                        return False
+
+                    printLog(u"第%s次尝试呼出一级界面" % str(checkFirstOptionTimes))
+                    # # 进入根节点(根节点名就是遥控键名);
+                    # self.redRat3.sendKey(path_params[level]["parent"])
+                    # 判断是否存在others字段,others字段中存在first_key,则优先使用first_key进入
+                    try:
+                        others = json.loads(path_params[level]["others"])
+                    except Exception,e:
+                        others = {}
+                    printLog("section others:%s"%others)
+                    if "first_key" in others.keys():
+                        first_key = others["first_key"]
+                    else:
+                        first_key = path_params[level]["parent"]
+                    printLog(u"进入根节点,first_key:%s"%first_key)
+                    # 进入根节点
+                    self.redRat3.sendKey(first_key)
+                    waitTime = self.getParentWaitTime(path_params[level]["parent"])
+                    printLog(u"打开%s界面的等待时间waittime为:%s" % (str(path_params[level]["parent"]), str(waitTime)))
+                    time.sleep(waitTime)
+                    # 获取ocr列表;
+                    self.get_ocr_list(level, option)
+                    if self.isFirstOptionShow(level, first_parent, first_option):
+                        printLog(u"呼出一级菜单界面")
+                        break
+                    else:
+                        time.sleep(5)
+                        self.redRat3.sendKey("return", 2)
+                        checkFirstOptionTimes = checkFirstOptionTimes + 1
+                # 如果一级界面弹出成功,则退出时候返回按键次数加1
+                returnKeyEventCount = returnKeyEventCount + 1
+
+            # 如果 others 字段不为空;
+            if path_params[level]["others"].__len__():
+                # 如果不是跑马灯处理则按照输入密码处理
+                if self.dealOthers(level, path_params[level]) is True:
+                    # print u"解锁失败!!!"
+                    printLog(u"focusOption=解锁失败!!!")
+                    return False
+
+            # 移动到子节点;
+            cur_option_enter_key = path_params[level]["option_enter_key"]
+            # 移动到子节点;
+            if cur_option_enter_key.__len__() == 0:
+                enter_key = path_params[level]["enter_key"]
+            else:
+                enter_key = cur_option_enter_key
+
+            move_key = path_params[level]["move_key"]
+
+            if str(move_key) == str([u'up', u'down']) or str(move_key) == str([u'down', u'up']):
+                findDirection = str(move_key[1])
+            elif str(move_key) == str([u'left', u'right']) or str(move_key) == str([u'right', u'left']):
+                findDirection = str(move_key[1])
+            else:
+                findDirection = 'grid'
+
+            result = self.move2TargetNode(
+                level,
+                first_parent,
+                path_params[level],
+                returnKeyEventCount,
+                findDirection=findDirection, Max_Try=Max_Try)
+
+            if result is True:
+                runPathCount = runPathCount + 1
+                printLog("focusOption=runPathCount:%s" % str(runPathCount))
+                if runPathCount == pathCount:
+                    print u"执行到最后一级"
+                    pass
+                else:
+                    # 子节点的进入方式默认为ok;
+                    self.redRat3.sendKey(enter_key)
+                    # 如果执行一次enter_key,则退出时按返回键的次数加一
+                    if enter_key != u'default':
+                        returnKeyEventCount = returnKeyEventCount + 1
+            else:
+                print "focusOption end<<<<"
+                return False
+        # end-for
+        print "focusOption end <<<<"
+        return True
+
+    # 假定已经在option所在的页面,逐步移动到目标option,聚焦到option
+    def moveToOption(self, option, Max_Try=15):
+        # 切黑场;
+        # self.sourceInput.setPattern(11)
+        # 获取menu path;
+        print "moveToOption start >>>"
+        path_params = self.xlsparser.get_option_paths(option)
+        # print 'openOption=path_params:', path_params
+        printLog('moveToOption=path_params:%s' % str(path_params))
+
+        checkRunOptionPathResult = self.xlsparser.checkRunOptionPath(path_params)
+        if str(checkRunOptionPathResult) == "NoExit":
+            printLog(u"表格中不存在到达Option:   %s   的路径,在表格中排查到达该Option路径" % str(option))
+            return False
+        if str(checkRunOptionPathResult) == "Fail":
+            printLog(u"表格中到达Option:   %s   的路径出现数据断层找不到First层级,在表格中排查到达该Option路径" % str(option))
+            return False
+        runPathCount = 0
+        pathCount = path_params.__len__()
+        printLog("moveToOption=pathCount:%s" % str(pathCount))
+
+        returnKeyEventCount = path_params.__len__()
+        # print "returnKeyEventCount:", returnKeyEventCount
+        printLog("moveToOption=returnKeyEventCount:%s" % str(returnKeyEventCount))
+        # 逆序路径key;
+        revpp = reversed(path_params)
+        # 根节点是否已进入;
+        entry = False
+        first_parent = u''
+        # first_option = u''
+        # 遍历路径(已排序,从first开始);
+        # print "revpp:", revpp
+        printLog("moveToOption=revpp:%s" % str(revpp))
+        for level in revpp:
+            # print "openOption=level:", path_params[level]
+            runPathCount = runPathCount + 1
+            print "level:", level, type(level)
+            print "runPathCount:", runPathCount, type(runPathCount)
+            print "pathCount:", pathCount, type(pathCount)
+
+            printLog("openOption=level:", path_params[level])
+            # 是否进入根结点;
+            if entry == False:
+                # 标记已进入根;
+                entry = True
+                first_parent = path_params[level]["parent"]
+
+            # 由于已经到达option同级界面,只执行最后一层
+            if runPathCount == pathCount:
+                # 移动到子节点;
+                cur_option_enter_key = path_params[level]["option_enter_key"]
+                # 移动到子节点;
+                if cur_option_enter_key.__len__() == 0:
+                    enter_key = path_params[level]["enter_key"]
+                else:
+                    enter_key = cur_option_enter_key
+
+                move_key = path_params[level]["move_key"]
+
+                if str(move_key) == str([u'up', u'down']) or str(move_key) == str([u'down', u'up']):
+                    findDirection = str(move_key[1])
+                elif str(move_key) == str([u'left', u'right']) or str(move_key) == str([u'right', u'left']):
+                    findDirection = str(move_key[1])
+                else:
+                    findDirection = 'grid'
+
+                result = self.move2TargetNode(
+                    level,
+                    first_parent,
+                    path_params[level],
+                    returnKeyEventCount,
+                    findDirection=findDirection, Max_Try=Max_Try)
+
+                if result is True:
+                    # runPathCount = runPathCount+1
+                    printLog("moveToOption=runPathCount:%s" % str(runPathCount))
+                    if runPathCount == pathCount:
+                        print "执行到最后一级"
+                        pass
+                    else:
+                        # 子节点的进入方式默认为ok;
+                        self.redRat3.sendKey(enter_key)
+                else:
+                    print "moveToOption end<<<<"
+                    return False
+        # end-for
+        print "moveToOption end <<<<"
+        return True
+
+    # 找到两个字符串左边或者右边相同的部分
+    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:
+                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 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 str in strList:
+                        if direction == "left":
+                            str = str[same_str.__len__():]
+                        else:
+                            str = str[:-same_str.__len__()]
+                        finishedList.append(str)
+        return finishedList
+
+if __name__ == "__main__":
+    tmenu = CTMenu()
+    print "执行开始!"
+    # ThresholdDict = tmenu.tconfig.getThresholdDict("factory")
+    # ThresholdDict = tmenu.tconfig.getThresholdDict("setting")
+    # print "ThresholdDict:", ThresholdDict, type(ThresholdDict)
+    # tmenu.getOptionValue("picture_preset")
+    # parent_ocr_dict = tmenu.xlsparser.get_parent_ocr_dict("automatic_search_analogue")
+    # print "parent_ocr_dict:",parent_ocr_dict
+     # 获取menu path;
+    value_params, path_params = tmenu.xlsparser.get_menu_paths('av', '')
+    print value_params, path_params
+    tmenu.redRat3.sendKey('home')
+    tmenu.move2TargetGridNode_matchTemp('First', 'home', path_params['First'], 1)
+    # isSuccessed = tmenu.setOptionValue("picture_preset", "stadium")
+    # isSuccessed = tmenu.setOptionValue("backlight", 50)
+
+    # isSuccessed = tmenu.checkOptionValue("picture_preset", "stadium")
+    # isSuccessed = tmenu.checkOptionValue("backlight", 50)
+    # print u'isSuccessed:', isSuccessed
+
+    # isSuccessed_focusOption = tmenu.focusOption("picture_preset")
+    # print u'聚焦到目标Option:auto_volume_control 结果isSuccessed_focusOption:', isSuccessed_focusOption
+    # 确保当前焦点是在目标Option同级的
+    # if isSuccessed_focusOption:
+    #     isSuccessed_moveToOption = tmenu.moveToOption("picture_reset")
+    #     print u'焦点移动到目标Option结果isSuccessed_moveToOption:', isSuccessed_moveToOption
+
+    # value_params, path_params = tmenu.xlsparser.get_menu_paths("picture_preset ", "personal")
+    # print "value_params:",value_params
+    # print "path_params:",path_params
+    #
+    # path_params_1 = tmenu.xlsparser.get_option_paths("picture_preset ")
+    # print "path_params_1:", path_params_1
+    # isHasValue, ocr_result_text = tmenu.getOptionValue("backlight")
+    # print "isHasValue:",isHasValue,type(isHasValue)
+    # print "ocr_result_text:",ocr_result_text,type(ocr_result_text)
+    # tmenu.setOptionValue("backlight",ocr_result_text)
+
+    # paths = tmenu.xlsparser.get_option_paths('usb')
+    # tmenu.move2TargetGridNode('first', 'usb', paths, 5)
+    # isSuccessed = tmenu.setOptionValue("source", "tv")
+    # isSuccessed = tmenu.setOptionValue("picture_preset", "stadium")
+    # isSuccessed = tmenu.setOptionValue("picture_preset", "dynamic")
+    # isSuccessed = tmenu.setOptionValue("gamma", 0)
+    # isSuccessed = tmenu.setOptionValue("instant_power_on", "on")
+    # isSuccessed = tmenu.setOptionValue("RGB_mode", "green_only")
+    # isSuccessed = tmenu.focusOptionValue("option", "enjoy_music")
+    # print "isSuccessed:", isSuccessed
+
+    # settingWaitTime = tmenu.getParentWaitTime("setting")
+    # print settingWaitTime
+    # sourceWaitTime = tmenu.getParentWaitTime("source")
+    # print sourceWaitTime
+
+    # print tmenu.setOptionValue('overscan', 'off')
+    # print tmenu.getOptionValue('overscan')
+    # settings 检测焦点选择类的setOptionValue
+    # isSuccessed = tmenu.setOptionValue("backlight", 50)
+    # print "isSuccessed:", isSuccessed
+    # isSuccessed = tmenu.setOptionValue("500hz", 50)
+    # print u'isSuccessed:', isSuccessed
+
+    # settings 检测值设置类的setOptionValue
+    # isSuccessed = tmenu.setOptionValue("backlight", 70)
+    # isSuccessed = tmenu.setOptionValue("auto_volume_control", "on")
+    # print u'isSuccessed:', isSuccessed
+
+    # settings 检测焦点选择类的checkOptionValue
+    # isSuccessed = tmenu.checkOptionValue("picture_preset", "movie")
+    # print u'isSuccessed:', isSuccessed
+    # isSuccessed = tmenu.setOptionValue("picture_preset", "movie", leafNodeValueWaitTime=2.0)
+    # print u'isSuccessed:', isSuccessed
+
+    # settings 检测值设置类的checkOptionValue
+    # isSuccessed = tmenu.checkOptionValue("backlight", 70)
+    # print u'isSuccessed:', isSuccessed
+
+    # 信源切换检测
+    # isSuccessed = tmenu.setSourceValue('source', 'hdmi2', sourceWaitTime=1.5)
+    # print u'进入信源(hdmi2)isSuccessed:', isSuccessed
+    # isSuccessed = tmenu.setSourceValue('source', 'av')
+    # print u'进入信源(tv)isSuccessed:', isSuccessed
+
+    # 频道列表检测
+    # checkResult,failChannelList=tmenu.checkChannelList(["6", "22"])
+    # print u'checkResult,failChannelList:', checkResult,failChannelList
+
+    # 检测自动搜台
+    # isSuccessed = tmenu.auto_search("automatic_search",[["as_tuner_mode","air"],["as_channel_type","analog"]])
+    # print u'isSuccessed:', isSuccessed
+    # isSuccessed =tmenu.auto_search("automatic_search", [["as_tuner_mode", "air"], ["as_channel_type", "analog"]],
+    #                   ["as_search", "finished"])
+    # print u'isSuccessed:', isSuccessed
+
+    # 检测手动搜台
+    # isSuccessed =tmenu.manual_search("manual_tuning", [["mt_tuner_mode", "air"], ["mt_channel_type", "analog"], ["mt_ch", 12]])
+    # print u'isSuccessed:', isSuccessed
+    # isSuccessed = tmenu.manual_search("manual_tuning",
+    #                                   [["mt_tuner_mode", "cable"], ["mt_channel_type", "digital"], ["mt_ch", 12]])
+    # print u'isSuccessed:', isSuccessed

+ 425 - 0
ssat_sdk/MenuTree3/TSourceImpl.py

@@ -0,0 +1,425 @@
+# -*- coding:utf-8 -*-
+from ssat_sdk.utils.LoggingUtil import printLog
+from ssat_sdk.pic_tool import ImageCMP
+from ssat_sdk.sat_environment import getSATTmpDIR
+from ssat_sdk.sat_environment import getMenuTree3SelectedProjectCfgPath
+import os, sys, time
+import cv2 as cv
+import json
+
+
+class TSourceImpl():
+    def __init__(self, ocr, ccard, tvOperator, tfocus, tconfig, xlsparser, \
+                 ocrDict=[{"lan": "ChinesePRC+English", "type": 4}, {"lan": "ChinesePRC+English", "type": 253}, \
+                          {"lan": "ChinesePRC+English", "type": 10001}]):
+        self.ocr = ocr
+        self.ccard = ccard
+        self.tvOperator = tvOperator
+        self.tFocus = tfocus
+        self.tConfig = tconfig
+        self.xlsParser = xlsparser
+        self.imgCMP = ImageCMP()
+        if not self.get_ocr_list():
+            self.ocrDict = ocrDict
+        # 某数据是否读取过;
+        self.got_dict = {}
+        self.sourceKeyErrCount = 0
+
+    def get_ocr_list(self):
+        if self.tConfig.has_option('First', 'source.ocr'):
+            self.ocrDict = self.tConfig.get_dict(self.tConfig.get_value('First', 'source.ocr'))
+            return True
+        else:
+            return False
+
+    # 截图并返回当前截图路径
+    def getCurrentUIPath(self):
+        current_uiPic = os.path.join(getSATTmpDIR(), "menutree_runpath.png")
+        self.ccard.takePicture(current_uiPic)
+        return current_uiPic
+
+    def getFocusTextBox2(self, level, root, focus_box):
+        icon_path = os.path.join(getMenuTree3SelectedProjectCfgPath(), "icon\\",
+                                 str(root) + "." + str(level) + ".dir.png")
+        if icon_path in self.got_dict:
+            x, y = focus_box[0] - self.got_dict[icon_path]['ref_box'][0], focus_box[1] - \
+                   self.got_dict[icon_path]['ref_box'][1]
+            return [x, y, x + self.got_dict[icon_path]['width'], y + self.got_dict[icon_path]['height']]
+        else:
+            if os.path.exists(icon_path) is True:
+                # 读取例图,宽高;
+                img = cv.imread(icon_path)
+                # 在例图中查找轮廓;
+                result, box = self.tFocus.findRectByIcon(icon_path, level, root)
+                if result is True:
+                    self.got_dict[icon_path] = {"ref_box": box, "width": img.shape[1], "height": img.shape[0]}
+                    x, y = focus_box[0] - box[0], focus_box[1] - box[1]
+                    return [x, y, x + img.shape[1], y + img.shape[0]]
+                # endif
+            # endif
+        # 如果没有字段原样返回;
+        return focus_box
+
+    '''
+    检测信源界面是否存在,分成两部分:1 检测焦点框;2 检测文字(可选 checkOCR控制)。
+    '''
+
+    def checkSourceView(self, level, first_parent, parent, option, value_for_ocr, isForValue=False, checkOCR=False):
+        print "checkSourceView:", level, first_parent, parent, option
+        current_uiPic = self.getCurrentUIPath()
+        if isForValue:
+            isFind, contourRect = self.tFocus.findRectByIcon(current_uiPic, "value", first_parent, option=parent)
+        else:
+            isFind, contourRect = self.tFocus.findRectByIcon(current_uiPic, level, first_parent, option=option)
+        if not isFind or contourRect == []:
+            return False
+        else:
+            if checkOCR is False:
+                return True
+            else:
+                isFind, ocrStr = self.getFocusText(current_uiPic, level, first_parent, parent, option, value_for_ocr)
+                return isFind
+
+    def getFocusText(self, current_uiPic, level, first_parent, parent, option, list_str, isSource=False,
+                     isForValue=False):
+        if isForValue:
+            isFind, contourRect = self.tFocus.findRectByIcon(current_uiPic, "value", first_parent, option=parent)
+        else:
+            isFind, contourRect = self.tFocus.findRectByIcon(current_uiPic, level, first_parent, option=option)
+        if not isFind or contourRect == []:
+            return False, ""
+        found, ocr_str = False, ''
+        # tmpPic = os.path.join(getSATTmpDIR(), "meuttree_area_text11.png")
+        # self.imgCMP.saveCropPic(current_uiPic, tmpPic,
+        #                         (contourRect[0], contourRect[1], contourRect[2], contourRect[3]))
+        # 是否有文字方向字段存在在ini里,如果有则转换文本框坐标;
+        tmpPic = os.path.join(getSATTmpDIR(), "meuttree_area_text.png")
+        contourRect = self.getFocusTextBox2(level, first_parent, contourRect)
+        self.imgCMP.saveCropPic(current_uiPic, tmpPic,
+                                (contourRect[0], contourRect[1], contourRect[2], contourRect[3]))
+
+        # print "self.ocrDict:", self.ocrDict
+        # 获取ocr_list;
+        ocr_list = self.xlsParser.get_ocr_list(level, parent, option, True if parent == option else False)
+        print "%s,%s,%s =>ocr_list=%s" % (level, parent, option, str(ocr_list))
+        # 遍历ocr类型;
+        for item in self.ocrDict:
+            # 识别ocr;
+            thresholdDict = self.tConfig.getThresholdDict(first_parent)
+            ocr_str = self.ocr.getStrWithImgProcess(tmpPic, {}, item["lan"], item["type"], reconTimes=1)
+            # print "\n\ngetCurrentFocusTextEx ==========================:OCR-Type=%s, OCR-Text=%s\n\n" % (
+            #     str(item), str(ocr_str))
+            printLog(u"OCR识别的类型字典:%s" % str(item))
+            printLog(u"OCR识别出的ocr_str为:%s" % str(ocr_str))
+            # print(u"getCurrentFocusTextEx.ocr_str land = %s, type =%d, ocr_str=%s:"%(item["lan"], item["type"], ocr_str.encode('GB18030')))
+            ocr_str = unicode(ocr_str).lower()
+            if isSource is False:
+                parent_ocr_dict = self.xlsParser.get_parent_ocr_dict(option)
+                print "getCurrentFocusTextEx.parent_ocr_dict:", parent_ocr_dict
+                list_parent_ocr_value = list(parent_ocr_dict.keys())
+                print "getCurrentFocusTextEx.list_parent_ocr_value:", list_parent_ocr_value
+                current_parent_ocr_value = ""
+                for parent_ocr_value in list_parent_ocr_value:
+                    parent_ocr_value = str(parent_ocr_value).lower()
+                    if parent_ocr_value in ocr_str or parent_ocr_value == ocr_str:
+                        current_parent_ocr_value = parent_ocr_value
+                        break
+                try:
+                    curentOption = parent_ocr_dict[current_parent_ocr_value]
+                except Exception, e:
+                    curentOption = ""
+
+                printLog(u"OCR识别出的current_parent_ocr_value为:%s" % str(current_parent_ocr_value))
+                printLog(u"OCR识别出的curentOCROption为:%s" % str(curentOption))
+                printLog(u"传入的目标option为:%s" % str(option))
+                # 通过OCR来反向识别判断当前识别的option是否为传入的option
+                if str(curentOption).lower() != "" and str(curentOption).lower() != str(option).lower():
+                    # print "非焦点框=%s" % (ocr_str)
+                    printLog(u"非焦点框=%s" % str(ocr_str))
+                    return False, ocr_str
+
+            # 再遍历目标焦点ocr;
+            for std_str in list_str:
+                std_str = str(std_str).lower()
+                # print 'std_str:', std_str, type(std_str)
+                # print 'ocr_str:', ocr_str, type(ocr_str)
+                if std_str in ocr_str or std_str == ocr_str:
+                    found = True
+                    break
+            # endfor
+            if found is True:
+                break
+            else:
+                if isSource is True:
+                    # 如果是信源  而且识别出是非目标信源则跳出该语言
+                    for ocr_std_str_list in ocr_list:
+                        # print "ocr_std_str_list:",ocr_std_str_list
+                        for ocr_std_str in ocr_std_str_list:
+                            # print "ocr_std_str:",ocr_std_str
+                            ocr_std_str = str(ocr_std_str).lower()
+                            # print 'ocr_std_str:', ocr_std_str, type(ocr_std_str)
+                            # print 'ocr_str:', ocr_str, type(ocr_str)
+                            if ocr_std_str in ocr_str or ocr_std_str == ocr_str:
+                                printLog(u"非焦点信源=%s" % str(ocr_str))
+                                return False, ocr_str
+
+        # endfor
+        return found, ocr_str
+
+    # 遍历获取ocr结果;
+    def getCurrentFocusTextEx(self, level, first_parent, parent, option, list_str, isSource=False,
+                              isForValue=False):
+        print level, first_parent, parent, option
+        current_uiPic = self.getCurrentUIPath()
+        if isForValue:
+            isFind, contourRect = self.tFocus.findRectByIcon(current_uiPic, "value", first_parent, option=parent)
+        else:
+            isFind, contourRect = self.tFocus.findRectByIcon(current_uiPic, level, first_parent, option=option)
+        # print "getCurrentFocusText   isFind:", isFind, contourRect
+        printLog(u"获取当前聚焦效果isFind:%s 聚焦区域contourRect:%s" % (str(isFind), str(contourRect)))
+        if not isFind or contourRect == []:
+            return -1, ""
+        else:
+            found, ocr_str = False, ''
+            # tmpPic = os.path.join(getSATTmpDIR(), "meuttree_area_text11.png")
+            # self.imgCMP.saveCropPic(current_uiPic, tmpPic,
+            #                         (contourRect[0], contourRect[1], contourRect[2], contourRect[3]))
+            # 是否有文字方向字段存在在ini里,如果有则转换文本框坐标;
+            tmpPic = os.path.join(getSATTmpDIR(), "meuttree_area_text.png")
+            contourRect = self.getFocusTextBox2(level, first_parent, contourRect)
+            self.imgCMP.saveCropPic(current_uiPic, tmpPic,
+                                    (contourRect[0], contourRect[1], contourRect[2], contourRect[3]))
+            if isSource:
+                self.tvOperator.sendKey("ok")
+
+            # print "self.ocrDict:", self.ocrDict
+            # 获取ocr_list;
+            ocr_list = self.xlsParser.get_ocr_list(level, parent, option, True if parent == option else False)
+            print "%s,%s,%s =>ocr_list=%s" % (level, parent, option, str(ocr_list))
+            # 遍历ocr类型;
+            for item in self.ocrDict:
+                # 识别ocr;
+                thresholdDict = self.tConfig.getThresholdDict(first_parent)
+                ocr_str = self.ocr.getStrWithImgProcess(tmpPic, thresholdDict, item["lan"], item["type"], reconTimes=1)
+                # print "\n\ngetCurrentFocusTextEx ==========================:OCR-Type=%s, OCR-Text=%s\n\n" % (
+                #     str(item), str(ocr_str))
+                printLog(u"OCR识别的类型字典:%s" % str(item))
+                printLog(u"OCR识别出的ocr_str为:%s" % str(ocr_str))
+                # print(u"getCurrentFocusTextEx.ocr_str land = %s, type =%d, ocr_str=%s:"%(item["lan"], item["type"], ocr_str.encode('GB18030')))
+                ocr_str = unicode(ocr_str).lower()
+                if isSource is False:
+                    parent_ocr_dict = self.xlsParser.get_parent_ocr_dict(option)
+                    print "getCurrentFocusTextEx.parent_ocr_dict:", parent_ocr_dict
+                    list_parent_ocr_value = list(parent_ocr_dict.keys())
+                    print "getCurrentFocusTextEx.list_parent_ocr_value:", list_parent_ocr_value
+                    current_parent_ocr_value = ""
+                    for parent_ocr_value in list_parent_ocr_value:
+                        parent_ocr_value = str(parent_ocr_value).lower()
+                        if parent_ocr_value in ocr_str or parent_ocr_value == ocr_str:
+                            current_parent_ocr_value = parent_ocr_value
+                            break
+                    try:
+                        curentOption = parent_ocr_dict[current_parent_ocr_value]
+                    except Exception, e:
+                        curentOption = ""
+
+                    printLog(u"OCR识别出的current_parent_ocr_value为:%s" % str(current_parent_ocr_value))
+                    printLog(u"OCR识别出的curentOCROption为:%s" % str(curentOption))
+                    printLog(u"传入的目标option为:%s" % str(option))
+                    # 通过OCR来反向识别判断当前识别的option是否为传入的option
+                    if str(curentOption).lower() != "" and str(curentOption).lower() != str(option).lower():
+                        # print "非焦点框=%s" % (ocr_str)
+                        printLog(u"非焦点框=%s" % str(ocr_str))
+                        return 0, ocr_str
+
+                # 再遍历目标焦点ocr;
+                for std_str in list_str:
+                    std_str = str(std_str).lower()
+                    # print 'std_str:', std_str, type(std_str)
+                    # print 'ocr_str:', ocr_str, type(ocr_str)
+                    if std_str in ocr_str or std_str == ocr_str:
+                        found = True
+                        break
+                # endfor
+                if found is True:
+                    break
+                else:
+                    if isSource is True:
+                        # 如果是信源  而且识别出是非目标信源则跳出该语言
+                        for ocr_std_str_list in ocr_list:
+                            # print "ocr_std_str_list:",ocr_std_str_list
+                            for ocr_std_str in ocr_std_str_list:
+                                # print "ocr_std_str:",ocr_std_str
+                                ocr_std_str = str(ocr_std_str).lower()
+                                # print 'ocr_std_str:', ocr_std_str, type(ocr_std_str)
+                                # print 'ocr_str:', ocr_str, type(ocr_str)
+                                if ocr_std_str in ocr_str or ocr_std_str == ocr_str:
+                                    printLog(u"非焦点信源=%s" % str(ocr_str))
+                                    return 0, ocr_str
+
+            # endfor
+            return int(found), ocr_str
+
+    def checkSource(self, value_for_ocr, option,enter_key,  count=0):
+        printLog(u"checkSource。开始检测当前信源")
+        sourceWaitTime = self.tConfig.getParentWaitTime("source")
+        self.tvOperator.sendKey('source', duration=sourceWaitTime)
+        isFind, text = self.getCurrentFocusTextEx('First', 'source', 'source', option, value_for_ocr)
+        # 未找到焦点
+        if isFind == -1:
+            isFind, text = self.getCurrentFocusTextEx('First', 'source', 'source', option, value_for_ocr)
+            if isFind == -1 and count < 3:
+                self.sourceKeyErrCount += 1
+                count += 1
+                return self.checkSource(value_for_ocr, option, enter_key, count)
+            elif isFind == 1:
+                self.tvOperator.sendKey(enter_key)
+                return True
+            else:
+                return False
+        # 信源不匹配
+        elif isFind == 0:
+            #画面卡顿处理
+            isFind, text = self.getCurrentFocusTextEx('First', 'source', 'source', option, value_for_ocr)
+            if isFind == 1:
+                self.tvOperator.sendKey(enter_key)
+                return True
+            else:
+                return False
+        else:
+            self.tvOperator.sendKey(enter_key)
+            return True
+
+    def toNextSource(self, moveKey, enterKey, option, value_for_ocr, count=0):
+        printLog(u"进入下一个信源.moveKey, enterKey:" + moveKey + enterKey)
+        sourceWaitTime = self.tConfig.getParentWaitTime("source")
+        self.tvOperator.sendKey('source', duration=sourceWaitTime)
+        ret = self.checkSourceView('First', 'source', 'source', option, value_for_ocr)
+        # 未弹出source界面
+        if ret is False:
+            printLog(u"未进入信源界面")
+            ret = self.checkSourceView('First', 'source', 'source', option, value_for_ocr)
+            if ret is False and count < 3:
+                count += 1
+                self.tvOperator.sendKeys('exit', "return")  # 在某些界面弹不出信源界面
+                return self.toNextSource(moveKey, enterKey, option, value_for_ocr, count)
+            elif count >= 3:
+                return False, ""
+
+        printLog(u"已进入信源界面")
+        self.tvOperator.sendKey(moveKey)
+        current_uiPic = self.getCurrentUIPath()
+        self.tvOperator.sendKey(enterKey)
+        isFind, text = self.getFocusText(current_uiPic, 'First', 'source', 'source', option, value_for_ocr,
+                                         isSource=True)
+        return isFind, text
+
+    def exitUSB(self):
+        self.tvOperator.sendKey("exit", duration=2)
+        self.tvOperator.sendKey("return", duration=2)
+
+    def exitSourceView(self, level, first_parent, parent, option, value_for_ocr, isForValue=False):
+        print u"退出信源界面"
+        count = 0
+        while count < 3:
+            count += 1
+            ret = self.checkSourceView(level, first_parent, parent, option, value_for_ocr, isForValue, checkOCR=True)
+            if ret is False:
+                break
+            self.tvOperator.sendKey("ok", duration=2)
+            ret = self.checkSourceView(level, first_parent, parent, option, value_for_ocr, isForValue, checkOCR=True)
+            if ret is False:
+                break
+            self.tvOperator.sendKey("exit", duration=2)
+            ret = self.checkSourceView(level, first_parent, parent, option, value_for_ocr, isForValue, checkOCR=True)
+            if ret is False:
+                break
+            self.tvOperator.sendKey("return", duration=2)
+            ret = self.checkSourceView(level, first_parent, parent, option, value_for_ocr, isForValue, checkOCR=True)
+            if ret is False:
+                break
+
+    def setSourceValue(self, option, value, sourceWaitTime=1.0, Max_Try=10):
+        printLog(u"开始执行setSourceValue。option:%s  value:%s" % (str(option), str(value)))
+        # self.get_ocr_list('First', 'source')
+        # 获取menu path;
+        value_params, path_params = self.xlsParser.get_menu_paths(option, value)
+        # print "value_params:", value_params
+        printLog(u"获取设置信源value:%s的值字典value_params:%s" % (str(value), str(value_params)))
+        value_for_ocr = value_params['value_for_ocr']
+        old_text = "init_old_text"
+        enter_key = value_params["enter_key"]
+        move_keyArr = value_params["move_key"]
+        if enter_key.__len__() < 1 or move_keyArr.__len__() < 2:
+            printLog(u"Error:Enter Key or Move Key错误:%s" % (str(enter_key), str(move_keyArr)))
+            return False
+        # 正向寻找的次数计数和最大次数
+        count = 0
+        # 读取menutree中的信源数量
+        source_list = self.xlsParser.getSubOptionList(option)
+        printLog(u"setSourceValue.%s source_list:%s" % (option, source_list))
+        if source_list.__len__() != 0:
+            Max_Try = source_list.__len__()
+        isOldSouceCount = 0
+        # 反向寻找的次数计数和最大次数
+        Reversecount = 0
+        Reverse_Max_Try = Max_Try
+        sourceWaitTime = self.tConfig.getParentWaitTime("source")
+        print "sourceWaitTime:", sourceWaitTime, type(sourceWaitTime)
+
+        # 第一次直接判断是不是目标信源
+        ret = self.checkSource(value_for_ocr, option, enter_key)
+        printLog(u"第一次判断是否目标信源:" + str(ret))
+        if ret is True:
+            # 退出信源
+            self.exitSourceView('First', 'source', 'source', option, value_for_ocr)
+            return True
+        if self.sourceKeyErrCount >= 3:
+            printLog(u"第一次信源判断,Source界面弹出异常次数>3")
+            return False
+        # 用于信源切换不适用场景,快速结束脚本
+        if self.enableSourceChange() is False:
+            printLog(u"设定:不允许切换信源。")
+            return False
+        while True:
+            # 循环遍历控制
+            if count < Max_Try and Reversecount < Reverse_Max_Try and isOldSouceCount < 1:
+                count += 1
+                moveKey = move_keyArr[0]
+            elif count >= Max_Try and Reversecount < Reverse_Max_Try and isOldSouceCount < 1:
+                Reversecount += 1
+                moveKey = move_keyArr[1]
+            else:
+                break
+            # 切入下一个信源
+            result, text = self.toNextSource(moveKey, enter_key, option, value_for_ocr)
+
+            if result is True:
+                return True
+            elif result is False and ("usb" in text.lower() or "media" in text.lower()):
+                isOldSouceCount = 0
+                self.exitUSB()
+                count = Max_Try
+            elif result is False and (old_text in text):
+                isOldSouceCount += 1
+                # 信源与之前信源一样,超过3次调转方向,如果已经调转方向,则返回失败结果
+                if isOldSouceCount > 3 and count >= Max_Try:
+                    Reversecount += Reverse_Max_Try
+                    isOldSouceCount = 0
+                elif isOldSouceCount > 3 and count < Max_Try:
+                    count = Max_Try
+                    isOldSouceCount = 0
+
+            # 退出信源
+            self.exitSourceView('First', 'source', 'source', option, value_for_ocr)
+
+        return False
+
+    def enableSourceChange(self):
+        jsonStr = self.tConfig.get_value("First", "source")
+        sourceDict = json.loads(jsonStr)
+        if sourceDict.has_key("enable"):
+            enable = sourceDict["enable"]
+            return enable == 1
+        else:
+            return True

+ 2 - 0
ssat_sdk/MenuTree3/__init__.py

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

BIN
ssat_sdk/MenuTree3/接口清单.xlsx


+ 164 - 0
ssat_sdk/StreamCard_SCBC.py

@@ -0,0 +1,164 @@
+# -*- coding: UTF-8 -*-
+import sys
+import os
+from os import path, access, R_OK
+import subprocess
+import thread
+
+from ssat_sdk.device_manage.dektec_manager import *
+
+
+'''
+DTV播放器,汇总各种设备进行DTV信号输出
+'''
+class StreamCard_SCBC():
+
+    def __init__(self):
+        self.dektecMgr = DektecManager()
+        self.cmdline = self.dektecMgr.cmdline
+
+    def getCmdLine(self):
+        return self.dektecMgr.cmdline
+
+    def playStream(self, streamFile, times, type, channelPoint):
+        self.dektecMgr.playStream(streamFile, times, type, channelPoint)
+
+    def playStreamAtThread(self, streamFile,  times,  type,  channelPoint ):
+        thread.start_new_thread(self.dektecMgr.playStreamAtThread,(streamFile,times,  type,  channelPoint))
+        # self.dektecMgr.playStreamAtThread(streamFile,  times,  type,  channelPoint)
+
+    def isPlaying(self):
+        return self.dektecMgr.isPlaying()
+
+    def interrupt(self):
+        self.dektecMgr.interrupt()
+
+    def taskkill(self):
+        self.dektecMgr.taskkill()
+
+    def set_cmd_clear(self):
+        self.dektecMgr.set_cmd_clear()
+
+    def set_playfile(self, playfile):
+        # 码流文件
+        self.dektecMgr.set_playfile(playfile)
+
+    def set_Device_type(self, type):
+        # 设备选择
+        self.dektecMgr.set_Device_type(type)
+
+    def set_Modulation_carrier_frequency(self, frequency):
+        # 频点
+        self.dektecMgr.set_Modulation_carrier_frequency(frequency)
+
+    '''
+    type:
+        ATSC      ATSC modulation
+        CMMB      CMMB modulation
+        DAB       DAB modulation
+        DTMB      DTMB modulation
+        DVBS      DVB-S QPSK modulation
+        DVBS2_16APSK DVB-S.2 16APSK modulation
+        DVBS2_32APSK 32APSK modulation
+        DVBS2_8PSK DVB-S.2 8PSK modulation
+        DVBS2_QPSK DVB-S.2 QPSK modulation
+        DVBS2_L3  DVB-S.2 L3 modulation
+        DVBT      DVB-T/H modulation
+        ISDBS     ISDB-S modulation
+        ISDBT     ISDB-T modulation
+        IQ        IQ direct
+        QAM4      QAM-4 modulation
+        QAM16     QAM-16 modulation
+        QAM32     QAM-32 modulation
+        QAM64     QAM-64 modulation
+        QAM128    QAM-128 modulation
+        QAM256    QAM-256 modulation
+        T2MI      T2MI modulation
+    '''
+    def set_Modulation_type(self, Modulation_type):
+        # 制式选择
+        self.dektecMgr.set_Modulation_type(Modulation_type)
+
+    def set_Modulation_bandwidth(self, Modulation_bandwidth):
+        # 带宽
+        self.dektecMgr.set_Modulation_bandwidth(Modulation_bandwidth)
+
+    def set_Modulation_transmission_mode(self, transmission_mode):
+        # 载波数
+        self.dektecMgr.set_Modulation_transmission_mode(transmission_mode)
+
+    def set_Modulation_constellation (self, constellation ):
+        # 调制方式
+        self.dektecMgr.set_Modulation_constellation(constellation)
+
+    def set_Modulation_guard_interval(self, guard_interval):
+        # 保护间隔
+        self.dektecMgr.set_Modulation_guard_interval(guard_interval)
+
+    def set_Modulation_Convolutional_rate(self, Convolutional_rate):
+        # 信号纠错码
+        self.dektecMgr.set_Modulation_Convolutional_rate(Convolutional_rate)
+
+    def set_Modulation_Output_level(self, Output_level):
+        # 信号强度
+        self.dektecMgr.set_Modulation_Output_level(Output_level)
+
+    def set_loop_times(self, loop_times):
+        # 播放次数
+        self.dektecMgr.set_loop_times(loop_times)
+
+    def set_rate(self, rate):
+        # 输出码率
+        self.dektecMgr.set_rate(rate)
+
+    def play_out(self):
+        thread.start_new_thread(self.dektecMgr.play_out,())
+        # self.dektecMgr.play_out()
+
+    def play_out_wait(self):
+        self.dektecMgr.play_out_wait()
+
+    def DtPlay_exe_CMD(self, str):
+        self.dektecMgr.DtPlay_exe_CMD(str)
+
+    def DtPlay_exe_CMD_wait(self, str):
+        self.dektecMgr.DtPlay_exe_CMD_wait(str)
+
+
+    def help(self):
+        self.dektecMgr.help()
+
+    def __del__(self):
+        self.dektecMgr.__del__()
+
+
+# ------------------------------------------------------------------------------
+if __name__ == "__main__":
+    scscbc = StreamCard_SCBC()
+    streamFile = r"D:\test\482_1218_105425.ts"
+    times = 1
+    type = "DTMB"
+    channelPoint = "52.5MHz"
+    # scscbc.playStream( streamFile,  times,  type,  channelPoint)
+    # scscbc.help()
+    # scscbc.DtPlay_exe_CMD( streamFile + " -mt DTMB -mf 52.5MHz")
+
+    # 按照 SteamXpress 界面的设置
+    scscbc.set_playfile(streamFile)
+    scscbc.set_Device_type("215")
+    scscbc.set_Modulation_carrier_frequency("578MHz")
+    scscbc.set_Modulation_type("DVBT")
+    scscbc.set_Modulation_bandwidth("8")
+    scscbc.set_Modulation_transmission_mode("8k")
+    # scscbc.set_Modulation_constellation("QAM64")
+    scscbc.set_Modulation_constellation("QPSK")
+    scscbc.set_Modulation_guard_interval("1/32")
+    scscbc.set_Modulation_Convolutional_rate("7/8")
+    scscbc.set_Modulation_Output_level("-90dBm")#单位可以不写,前面的负号"-"一定要写
+    scscbc.set_loop_times("0")
+
+    scscbc.set_rate("18661765")
+
+    print scscbc.dektecMgr.cmdline
+    # 频繁操作注意延迟3秒,画面才播放出来。
+    scscbc.play_out()

+ 0 - 0
ssat_sdk/TvDetect/__init__.py


+ 109 - 0
ssat_sdk/TvDetect/colorbar_detect.py

@@ -0,0 +1,109 @@
+# -*- coding:utf-8 -*-
+import os, sys,time
+import cv2 as cv
+import numpy as np
+
+from ssat_sdk.picture.RGB import RGBColor
+from ssat_sdk.picture.pic_segment import PicSegment
+from ssat_sdk.picture import image_util
+from ssat_sdk.picture.rect_util import RectUtil
+
+class ColorBarDetect():
+    HDMI_CB_SAMPLE_AREAS = {"whiteArea":(20,200,150,600),"yellowArea":(240,200,400,600),"lightBlueArea":(500,200,650,600)}
+    AV_CB_SAMPLE_AREAS = {"whiteArea":(20,200,150,600),"yellowArea":(240,200,400,600),"lightBlueArea":(500,200,650,600)}
+    ATV_CB_SAMPLE_AREAS = {"whiteArea":(20,200,150,600),"yellowArea":(240,200,400,600),"lightBlueArea":(500,200,650,600)}
+    curDIR = os.path.dirname(__file__)
+    HDMI_CB_STD_PIC = os.path.join(curDIR, "res/colorbar_hdmi_std.png")
+    AV_CB_STD_PIC = os.path.join(curDIR, "res/colorbar_av_std.png")
+    ATV_CB_STD_PIC = os.path.join(curDIR, "res/colorbar_atv_std.png")
+    print "HDMI_CB_STD_PIC=",HDMI_CB_STD_PIC
+    def __init__(self):
+        self.BGR = RGBColor()
+        self.picSegment = PicSegment()
+        self.rectUtil = RectUtil()
+
+    '''
+    检测当前画面是否彩条画面。    
+    :param picPath:  图片路径
+    :return True/False
+    '''
+    def isHDMIColorBar(self, picPath):
+        srcImg = cv.imread(picPath)
+        #1 根据图片分辨率,选择标准图片
+        stdImg = cv.imread(self.HDMI_CB_STD_PIC)
+        SRC_CB_Areas, STD_CB_Areas = self.getCBAreas(srcImg,stdImg,self.HDMI_CB_SAMPLE_AREAS)
+        #3 根据周长和面积,判断是不是彩条画面
+        for key in STD_CB_Areas:
+            src_al = self.rectUtil.calArcLength(SRC_CB_Areas[key])
+            src_area = self.rectUtil.calArea(SRC_CB_Areas[key])
+            std_al = self.rectUtil.calArcLength(STD_CB_Areas[key])
+            std_area = self.rectUtil.calArea(STD_CB_Areas[key])
+            if src_al/std_al < 0.99 and src_area/std_area < 0.99:
+                return False
+        return True
+
+    '''
+        检测当前画面是否彩条画面。    
+        :param picPath:  图片路径
+        :return True/False
+        '''
+
+    def isAVColorBar(self, picPath):
+        srcImg = cv.imread(picPath)
+        # 1 根据图片分辨率,选择标准图片
+        stdImg = cv.imread(self.AV_CB_STD_PIC)
+        SRC_CB_Areas, STD_CB_Areas = self.getCBAreas(srcImg, stdImg, self.AV_CB_SAMPLE_AREAS)
+        # 3 根据周长和面积,判断是不是彩条画面
+        for key in STD_CB_Areas:
+            src_al = self.rectUtil.calArcLength(SRC_CB_Areas[key])
+            src_area = self.rectUtil.calArea(SRC_CB_Areas[key])
+            std_al = self.rectUtil.calArcLength(STD_CB_Areas[key])
+            std_area = self.rectUtil.calArea(STD_CB_Areas[key])
+            if src_al / std_al < 0.99 and src_area / std_area < 0.99:
+                return False
+        return True
+
+    '''
+            检测当前画面是否彩条画面。    
+            :param picPath:  图片路径
+            :return True/False
+            '''
+
+    def isATVColorBar(self, picPath):
+        srcImg = cv.imread(picPath)
+        # 1 根据图片分辨率,选择标准图片
+        stdImg = cv.imread(self.ATV_CB_STD_PIC)
+        SRC_CB_Areas, STD_CB_Areas = self.getCBAreas(srcImg, stdImg, self.ATV_CB_SAMPLE_AREAS)
+        # 3 根据周长和面积,判断是不是彩条画面
+        for key in STD_CB_Areas:
+            src_al = self.rectUtil.calArcLength(SRC_CB_Areas[key])
+            src_area = self.rectUtil.calArea(SRC_CB_Areas[key])
+            std_al = self.rectUtil.calArcLength(STD_CB_Areas[key])
+            std_area = self.rectUtil.calArea(STD_CB_Areas[key])
+            if src_al / std_al < 0.99 and src_area / std_area < 0.99:
+                return False
+        return True
+
+    def getCBAreas(self, srcImg, stdImg, sampleAreas):
+        SRC_CB_BGRs = {}
+        STD_CB_BGRs = {}
+        for key in sampleAreas:
+            srcdImg = image_util.cutImage(srcImg, sampleAreas[key])
+            bgrSrc = self.BGR.getAvgBGR(srcdImg)
+            SRC_CB_BGRs[key] = bgrSrc
+            stddImg = image_util.cutImage(stdImg, sampleAreas[key])
+            bgrStd = self.BGR.getAvgBGR(stddImg)
+            STD_CB_BGRs[key] = bgrStd
+        print "isHDMIColorBar:SRC_CB_BGRs=", SRC_CB_BGRs
+        print "isHDMIColorBar:STD_CB_BGRs=", STD_CB_BGRs
+        # 2 根据颜色提取纯色区域,保存所有区域坐标
+        SRC_CB_Areas = {}
+        STD_CB_Areas = {}
+        for key in STD_CB_BGRs:
+            result, area = self.picSegment.findAreaByColor(srcImg, [], STD_CB_BGRs[key])
+            # print "isHDMIColorBar:key,result,area=",key,result,area
+            SRC_CB_Areas[key] = area
+            result, area = self.picSegment.findAreaByColor(srcImg, [], STD_CB_BGRs[key])
+            STD_CB_Areas[key] = area
+        return SRC_CB_Areas,STD_CB_Areas
+

BIN
ssat_sdk/TvDetect/res/colorbar_atv_std.png


BIN
ssat_sdk/TvDetect/res/colorbar_av_std.png


BIN
ssat_sdk/TvDetect/res/colorbar_hdmi_std.png


+ 137 - 0
ssat_sdk/UATree/UAT_PathManage.py

@@ -0,0 +1,137 @@
+# -*- coding:utf-8 -*-
+
+from ssat_sdk.UATree.UAT_data import UATData
+from UAT_tree import UATTree
+from UAT_treeConstant import TreeConst
+from ssat_sdk.utils.string_util import strcmp
+from UAT_log import error,info,debug
+import os, sys, time
+
+DEBUG = True
+INFO = True
+ERROR = True
+class UATPathManage():
+    def __init__(self):
+        self.uatData = UATData()
+        self.levelList = self.uatData.UATree.treeDict.keys()
+
+    '''
+    构建一条从first parent到目标option路径。
+    path最后一个元素,是目标option
+    :return result,path. result:-1 代表option异常;0 代表路径中断异常;1 代表路径完整,找到了first parent
+    '''
+    def getOptionPath(self, option, parent = None):
+        path = []
+        if option is None and parent is None:
+            error(str(self.__class__), "getOptionPath",  "getPath Error:Param option is None!!", ERROR)
+            return -1, path
+        #获取option所在的parent
+        elif parent is None:
+            parent = self.uatData.getParentByOption(option)
+        if parent is None:
+            error(str(self.__class__), "getOptionPath",
+                  "getPath Error:option%s's parent not found!!" % (option[UATTree.TAB_NAME]), ERROR)
+            return -1, path
+        path.append(option)
+        self.addPrevParentPath(parent, path)
+        if path.__len__() == 0:
+            result = -1
+        elif strcmp(path[-1]["level"], "first") is False:
+            error(str(self.__class__), "getOptionPath", "getPath Error:option%s path interrupt!!"%(option[UATTree.TAB_NAME]), ERROR)
+            result = 0
+        else:
+            result = 1
+        return result, path
+
+
+    def addPrevParentPath(self, parent, path):
+        if parent is None:
+            return
+        path.append(parent)
+        prevParent = parent[UATTree.TAB_PREV_PARENT]
+        if prevParent is None:
+            return
+        else:
+            self.addPrevParentPath(prevParent, path)
+
+    def info(self,path):
+            for item in path:
+                if item is not None:
+                    info(str(self.__class__), "info", "Path: " + item[UATTree.TAB_NAME], INFO)
+                else:
+                    info(str(self.__class__), "info", "Path: " + str(item), INFO)
+
+    '''
+    构建一条当前parent到option的路径。
+    如果当前电视页面分析,不在UATree中,则构建一条first parent到目标option中的目录。
+    :return backPath, forwardPath. backPath是返回路径,forwardPath是去往目标option的路径。
+    注意:backPath路径是顺序执行, forwardPath的路径是要反过来逐步执行。
+    backPath格式:[currentOption, currentParent, parent1, parent2, ..., FirstParent]
+    forwardPath格式: [targetOption, targetParent, parent1, parent2, ..., FirstParent]
+      forwardPath路径为空:表示路径异常。
+      forwardPath路径包含option和parent:表示在路径最后一个parent所在页面,或者表示需要从first parent进入
+        1 如果backPath为空,则表示forwardPath从First parent重新进入。
+        2 如果backPath包含option和parent,则表示在backPath的option所在页面,需要返回到forwardPath最后一个parent所在页面。
+            backPath返回到forwardPath最后一个parent所在页面:
+            注意:backPath和forwardPath的最后一个parent,一定会一样。
+            1 backPath,返回到最后一个parent页面。
+            2 在backPath最后一个parent页面,即forwardPath最后一个parent页面,再依次递进,进入targetParent页面,选中targetOption。
+    '''
+    def genOptionSmartPath(self, curParent, option):
+        backPath = []
+        forwardPath = []
+        if curParent is None:
+            result,forwardPath = self.getOptionPath(option)
+            return backPath,forwardPath
+        print "genOptionSmartPath:curParent:", curParent
+        curOptionDict = curParent[UATTree.TAB_OPTION]
+        if curOptionDict.__len__() > 0:
+            curOption = curOptionDict[curOptionDict.keys()[0]]
+        else:
+            curOption = None
+        resutl1, toFirstPath = self.getOptionPath(curOption, parent=curParent)
+        print "toFirstPath:",self.info(toFirstPath)
+        resutl2, targetOptionPath = self.getOptionPath(option)
+        print "targetOptionPath:",self.info(targetOptionPath)
+        if resutl1 < 1 and resutl2 == 1:
+            forwardPath = targetOptionPath
+            return backPath, forwardPath
+        elif resutl1 < 1:
+            return backPath, forwardPath
+
+        #levelMin代表parent的个数,所以要去掉option计数
+        fLen = toFirstPath.__len__()
+        tLen = targetOptionPath.__len__()
+        levelMin = min(fLen, tLen) - 1
+        for i in range(levelMin): #仅核对parent部分
+            print toFirstPath[fLen-1-i][UATTree.TAB_NAME],targetOptionPath[tLen-1-i][UATTree.TAB_NAME]
+            #从first parent开始核对,找到第一个不同点
+            if toFirstPath[fLen-1-i][UATTree.TAB_NAME] <> targetOptionPath[tLen-1-i][UATTree.TAB_NAME]:
+                if i == 0: #first parent不吻合。toFirstPath和targetOptionPath路径没有交点
+                    print "<> i == 0:", i
+                    forwardPath = targetOptionPath
+                else: #toFirstPath和targetOptionPath路径是相交关系
+                    print "<> else:", i
+                    backPath = toFirstPath[0:fLen-i+1] #取到上一个相同的parent。
+                    forwardPath = targetOptionPath[0:tLen-i+1]  # 取到上一个相同的parent。
+                break
+            if i == levelMin-1:#toFirstPath和targetOptionPath路径是包含关系
+                print "i == levelMin:", i
+                backPath = toFirstPath[0:fLen-i]  # 包含关系,取到当前相同parent
+                forwardPath = targetOptionPath[0:tLen-i]  #包含关系,取到当前相同parent
+        return backPath, forwardPath
+
+
+if __name__ == '__main__':
+    uatPathManage = UATPathManage()
+    parentName = "usb_video_h264-ac3_replay"
+    curParent = uatPathManage.uatData.getParentDict(parentName)
+    print curParent
+    # backPath, forwardPath = uatPathManage.genOptionSmartPath(curParent,option)
+    # print "option:", option
+    # print "backPath:"
+    # uatPathManage.info(backPath)
+    # print "forwardPath:"
+    # uatPathManage.info(forwardPath)
+
+

+ 117 - 0
ssat_sdk/UATree/UAT_data.py

@@ -0,0 +1,117 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+
+from ssat_sdk.UATree.UAT_excelParser import UATExcelParser
+from ssat_sdk.UATree.UAT_fileManage import UATFileManage
+from ssat_sdk.UATree.UAT_tree import UATTree
+from UAT_log import error,info,debug
+
+DEBUG = True
+INFO = True
+ERROR = True
+class UATData():
+    cls = "UATData"
+    def __init__(self):
+        self.UATree = UATTree()
+        self.excelParser = UATExcelParser(self.UATree)
+        self.fileManage = UATFileManage()
+        treeFileList = self.fileManage.getUATreeList()
+        for treeFile in treeFileList:
+            self.addUATExcel(treeFile)
+        keyCodeFile = self.fileManage.getKeyCodeFile()
+        self.addKeyCode(keyCodeFile)
+
+    '''加载指定的UATree excel表格'''
+    def addUATExcel(self, excelPath):
+        self.excelParser.read_excel(excelPath)
+
+    '''加载eventkey 键值代码表格'''
+    def addKeyCode(self, excelPath):
+        self.excelParser.read_keyCode(excelPath)
+
+    '''
+    获取parent的完成字典数据
+    '''
+    def getParentDict(self, parentName):
+        return self.UATree.findParentDict(parentName)
+
+    '''
+    获取指定parent的option列表,option包含所有属性值
+    :param : parent名字
+    :return dict:option 字典
+    '''
+    def getSubOptionDict(self, parentName):
+        return self.UATree.getSubOptionDict(parentName)
+
+    '''
+    获取指定parent的option名字列表
+    param: parent名字
+    return: list:option 名字列表
+    '''
+    def getSubOptionNames(self, parentName):
+        optionDict = self.getSubOptionDict(parentName)
+        return optionDict.keys()
+
+    '''
+    获取指定option的所有参数
+    param: option名字
+    :return option:整个option参数
+    '''
+    def getOption(self,optionName):
+        return self.UATree.findOption(optionName)
+
+    '''
+    在某一层级获取指定option所有参数
+    :param targetOption 名字
+    :param level 所在层级
+    :return option:整个option参数
+    '''
+    def getOpitonInLevel(self,targetOption, level):
+        return self.UATree.getOpitonInLevel(targetOption, level)
+
+    '''
+    获取option的parent
+    '''
+    def getParentByOption(self, option):
+        return self.UATree.getParentByOption(option)
+    '''
+    根据当前parent,获取当前
+    '''
+    def getPrevParentByParent(self, parent):
+        return self.UATree.getPrevParent(parent)
+
+    '''
+    获取指定option的指定参数
+    :param  option名字
+    :param param
+        param=optionView
+        param=move_key
+        param=enter_key
+        param=textValue
+    :return option指定的值
+    '''
+    def getOptionParams(self, optionName, param=""):
+        optionDict = self.getOption(optionName)
+        if optionDict.has_key(param):
+            return optionDict[param]
+        return None
+
+    def getTreeDict(self):
+        return self.UATree.treeDict
+
+    def getKeyCodeDict(self):
+        return self.excelParser.eventKeyCode
+
+if __name__ == "__main__":
+    uatData = UATData()
+    print uatData.getOpitonInLevel("usb_picture_1","third")
+    print uatData.getOpitonInLevel("usb_picture_picture","second")
+    # print "getParentDict:",uatData.getParentDict("usb_mediaBrowser")
+    # print "getParentDict:",uatData.getParentDict("usb_device")
+    # print "getOption:",uatData.getOption("usb_device")
+    # print "getOption:",uatData.getOption("usb_video_h264-ac3")
+    # print "getParentDict:",uatData.getParentDict("usb_video_h264-ac3")
+    # print "getSubOptionDict:",uatData.getSubOptionDict("usb_mediaBrowser")
+    # print "getSubOptionNames:",uatData.getSubOptionNames("usb_mediaBrowser")
+    # print "getOption:",uatData.getOption("usb_mediaBrowser")
+    # print "getOptionParams:",uatData.getOptionParams("usb_device", "enter_key")

+ 163 - 0
ssat_sdk/UATree/UAT_excelParser.py

@@ -0,0 +1,163 @@
+# -*- coding:utf-8 -*-
+from UAT_log import info,debug,error
+from UAT_tree import FirstLevel,NLevel,DialogLevel,UATTree
+
+import os, sys, time
+import xlrd# 字典不排序;
+import json
+
+
+DEBUG = True
+INFO = True
+ERROR = True
+class UATExcelParser():
+    cls = "UATExcelParser"
+    def __init__(self, uiTree):
+        self.levelList=[]
+        self.uiTree = uiTree
+        self.eventKeyCode = {}
+
+    def read_excel(self, path=None):
+        debug(self.cls, "read_excel", "path:"+path, DEBUG)
+        if path is not None:
+            if type(path) == str:
+                path = path.decode('utf-8')
+            self.xls_path = path
+
+        # 打开文件;
+        wb = xlrd.open_workbook(filename=self.xls_path)
+        if wb is None:
+            error(self.cls, "read_excel","文件打开失败!"+path, ERROR)
+            return
+        # 获取所有sheet;
+        sheet = None
+        self.levelList = wb.sheet_names()
+        if "dialog" in self.levelList:
+            sheet = wb.sheet_by_name("dialog")
+            self.parse_excel(sheet, False)
+            self.levelList.remove("dialog")
+        for sh_name in self.levelList:
+            debug(self.cls, "read_excel", "sheet_name="+sh_name, DEBUG)
+            sheet = wb.sheet_by_name(sh_name)
+            self.parse_excel(sheet, False)
+
+        # self.uiTree.printTree()
+
+    def read_keyCode(self, path):
+        if path is None:
+            info(self.cls, "read_excel_keyCode", "未读取到相关的keyCode文件,以默认eventKey为准", INFO)
+            return
+        debug(self.cls, "read_excel_keyCode", "keycode_path:"+path, DEBUG)
+        # 打开文件;
+        path = path.decode('utf-8')
+        wb = xlrd.open_workbook(filename=path)
+        sheet = wb.sheet_by_index(0)
+        self.parse_keyCode(sheet)
+
+
+    "根据level sheet名字,确定在第几层"
+    def getLevelNO(self, levelSheet):
+        return self.levelList.index(levelSheet)
+
+    # endfun
+
+    def parse_excel(self, sheet, bpath=True):
+        parentName = ""
+        if u"first" == sheet.name.lower():
+            for i in range(1, sheet.nrows):
+                # 获取每行内容;
+                oneRow = tuple(sheet.row_values(i))
+                parentName = self.parseFLevelRow(oneRow, sheet.name, parentName)
+        elif u"dialog" == sheet.name.lower():
+            for i in range(1, sheet.nrows):
+                # 获取每行内容;
+                oneRow = tuple(sheet.row_values(i))
+                parentName = self.parseDLevelRow(oneRow, parentName)
+        else:  # 路径;
+            for i in range(1, sheet.nrows):
+                # 获取每行内容;
+                oneRow = tuple(sheet.row_values(i))
+                parentName = self.parseNLevelRow(oneRow, sheet.name, parentName)
+
+    # endfun
+
+    def parse_keyCode(self, sheet):
+        for i in range(1, sheet.nrows):
+            # 获取每行内容;
+            oneRow = tuple(sheet.row_values(i))
+            name = oneRow[0].upper()
+            code = int(oneRow[1])
+            self.eventKeyCode[name] = code
+
+    #endfun
+
+    def parseFLevelRow(self,oneRow, level, parentName):
+        # debug(self.cls, "parseFLevelRow", "level:%s, parentName:%s"%(level, parentName), DEBUG)
+        pn = oneRow[FirstLevel.ParentCol]
+        if pn.__len__() > 0:
+            self.uiTree.addParent(level.lower(), pn.lower(),
+                                  oneRow[FirstLevel.ShortCutKeyCol],
+                                  oneRow[FirstLevel.UIViewCol],
+                                  oneRow[FirstLevel.MoveKeyCol],
+                                  oneRow[FirstLevel.ToParentKeyCol],
+                                  oneRow[FirstLevel.LayoutCol],
+                                  oneRow[FirstLevel.OthersCol])
+            return pn
+        oname = oneRow[FirstLevel.OptionCol]
+        if oname.__len__() > 0:
+            self.uiTree.addOption(level.lower(), parentName.lower(), oname.lower(),
+                                  oneRow[FirstLevel.OptionViewCol],
+                                  oneRow[FirstLevel.FocusSelectCol],
+                                  oneRow[FirstLevel.FocuseViewCol],
+                                  oneRow[FirstLevel.EnterKeyCol],
+                                  oneRow[FirstLevel.TextValueCol],
+                                  oneRow[FirstLevel.InfoViewCol])
+        return parentName
+
+    def parseNLevelRow(self,oneRow, level, parentName):
+        # debug(self.cls, "parseFLevelRow", "level:%s, parentName:%s"%(level, parentName), DEBUG)
+        pn = oneRow[NLevel.ParentCol]
+        if pn.__len__() > 0:
+            self.uiTree.addParent(level.lower(), pn.lower(), "",
+                                  oneRow[NLevel.UIViewCol],
+                                  oneRow[NLevel.MoveKeyCol],
+                                  oneRow[NLevel.ToParentKeyCol],
+                                  oneRow[NLevel.LayoutCol],
+                                  oneRow[NLevel.OthersCol])
+            return pn
+        oname = oneRow[NLevel.OptionCol]
+        if oname.__len__() > 0:
+            self.uiTree.addOption(level.lower(), parentName.lower(), oname.lower(),
+                                  oneRow[NLevel.OptionViewCol],
+                                  oneRow[NLevel.FocusSelectCol],
+                                  oneRow[NLevel.FocuseViewCol],
+                                  oneRow[NLevel.EnterKeyCol],
+                                  oneRow[NLevel.TextValueCol],
+                                  oneRow[NLevel.InfoViewCol])
+        return parentName
+
+    # 添加dialog
+    def parseDLevelRow(self, oneRow, parentName):
+        # debug(self.cls, "parseDLevelRow", "level:%s, parentName:%s"%(level, parentName), DEBUG)
+        pn = oneRow[DialogLevel.DialogCol]
+        if pn.__len__() > 0:
+            self.uiTree.addDialog(pn.lower(), "",
+                                  oneRow[DialogLevel.UIViewCol],
+                                  oneRow[DialogLevel.MoveKeyCol],
+                                  oneRow[DialogLevel.ToParentKeyCol],
+                                  oneRow[DialogLevel.LayoutCol],
+                                  oneRow[DialogLevel.OthersCol])
+            return pn
+        oname = oneRow[DialogLevel.OptionCol]
+        if oname.__len__() > 0:
+            self.uiTree.addDialogOption(parentName.lower(), oname.lower(),
+                                  oneRow[DialogLevel.OptionViewCol],
+                                  oneRow[DialogLevel.FocusSelectCol],
+                                  oneRow[DialogLevel.FocuseViewCol],
+                                  oneRow[DialogLevel.EnterKeyCol],
+                                  oneRow[DialogLevel.TextValueCol],
+                                  oneRow[DialogLevel.InfoViewCol])
+        return parentName
+
+    def parseParentDialog(self):
+        self.uiTree.parseDialogParam()

+ 32 - 0
ssat_sdk/UATree/UAT_fileManage.py

@@ -0,0 +1,32 @@
+# -*- coding:utf-8 -*-
+
+from ssat_sdk.sat_environment import getMenuTree3SelectedProjectCfgPath
+from UAT_log import debug,info,error
+
+import os, sys, time
+
+DEBUG = True
+INFO = True
+ERROR = True
+class UATFileManage():
+    cls="UATFileManage"
+    def __init__(self):
+        self.UATreeDir = getMenuTree3SelectedProjectCfgPath()
+        info(self.cls, "__init__", "UATreeDir:" + str(self.UATreeDir), INFO)
+
+    def getUATreeList(self):
+        fileList = os.listdir(self.UATreeDir)
+        treeList = []
+        for filePath in fileList:
+            if filePath.lower().startswith("uatree") and (filePath.lower().endswith(".xls") or filePath.lower().endswith(".xlsx")):
+                treeList.append(os.path.join(self.UATreeDir, filePath))
+        return treeList
+
+    def getKeyCodeFile(self):
+        fileList = os.listdir(self.UATreeDir)
+        keyCodeFile = None
+        for filePath in fileList:
+            if filePath.lower() == "eventkey_code.xlsx":
+                keyCodeFile = os.path.join(self.UATreeDir, filePath)
+                break
+        return keyCodeFile

+ 23 - 0
ssat_sdk/UATree/UAT_log.py

@@ -0,0 +1,23 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+from ssat_sdk.utils.LoggingUtil import printLog
+
+TAG = "UATree:"
+DEBUG = True
+INFO = True
+ERROR = True
+
+def debug(cls, fun, msg, enable):
+    if DEBUG and enable:
+        pstr = "Debug:" + TAG + cls + "[" + fun + "]" + msg
+        printLog(pstr)
+
+def info(cls, fun, msg, enable):
+    if INFO and enable:
+        pstr = "Info:" + TAG + cls + "[" + fun + "]" + msg
+        printLog(pstr)
+
+def error(cls, fun, msg, enable):
+    if ERROR and enable:
+        pstr = "Error:" + TAG + cls + "[" + fun + "]" + msg
+        printLog(pstr)

+ 139 - 0
ssat_sdk/UATree/UAT_menu.py

@@ -0,0 +1,139 @@
+# -*- 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)
+        return self.uatRunner.moveToOption(option)
+
+
+    '''
+    在目标option所在菜单下,进入目标option(单层操作)
+    :param optionName 目标option名字
+    '''
+    def openMoveOption(self, optionName):
+        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
+        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)
+        return self.uatRunner.focusOption(option, fromFirst)
+
+    '''
+    进入目标option,如果在option所在页面,直接用moveToOption移动焦点选中并进入,按路径执行选中步骤。
+    :param optionName 目标option名字
+    :param fromFirst 是否固定从firstparent到目标option
+    '''
+    def openOption(self, optionName, fromFirst = False):
+        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
+        return self.uatRunner.openOption(option, fromFirst)
+
+    '''
+    进入目标parent,在openOption基础上,增加了后续路过弹窗的无option界面parent。例如:usb的视频播放界面,有弹出提示框,continue或者replay。
+    :param parentName 目标parent名字
+    :param fromFirst 是否固定从firstparent到目标option
+    '''
+    def openParent(self, parentName, fromFirst=False):
+        parent = self.uatRunner.uatPathManage.uatData.getParentDict(parentName)
+        return self.uatRunner.openParent(parent, fromFirst)
+	
+    '''
+    选中到目标option,如果在option所在页面,直接用moveToOption移动焦点选中,按路径执行选中步骤。
+    :param optionName 目标option名字
+    :param value 目标option所设的值
+	:param exitMenu 执行完以后,是否退出当前菜单
+	:param fromFirst 是否从第一层开始执行(菜单页面是否处于初始状态)
+    '''
+    def setOptionValue(self, optionName, value, exitMenu=True, fromFirst=True):
+        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
+        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)
+        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)
+        return self.uatRunner.getOptionInfoView(option)
+
+    '''
+    检查当前页面是否存在该option
+    '''
+    def checkOptionExist(self, optionName):
+        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
+        return self.uatRunner.checkOptionExist(option)
+
+    def getOptionObject(self, optionName):
+        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
+        return self.uatRunner.getOptionObject(option)
+
+    '''
+    获取该option的取值范围,返回一个元组,包含左起最小值和右起最大值两个元素。
+    '''
+    def getOptionValueRange(self, optionName):
+        option = self.uatRunner.uatPathManage.uatData.getOption(optionName)
+        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()
+    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")

+ 357 - 0
ssat_sdk/UATree/UAT_runner.py

@@ -0,0 +1,357 @@
+# -*- coding:utf-8 -*-
+from UAT_PathManage import UATPathManage
+from ssat_sdk.UATree.UAT_tree import UATTree
+from ssat_sdk.UATree.UAT_treeConstant import TreeConst
+from UAT_runnerCommand import UATRunnerCommand
+from ssat_sdk.python_uiautomator import PyUIAutomator, FocusManageAndroid, DirectionManageAndroid
+from UAT_log import error, debug, info
+
+
+import os, sys, time
+
+DEBUG = True
+INFO =True
+ERROR = True
+class UATRunner():
+    cls = "UATRunner"
+    def __init__(self):
+        self.uatPathManage = UATPathManage()
+        self.pyU = PyUIAutomator()
+        self.dm = DirectionManageAndroid()
+        self.fm = FocusManageAndroid(self.pyU, self.dm)
+        self.runnerCmd = UATRunnerCommand(self.uatPathManage, self.pyU, self.dm, self.fm)
+        # setOptionValue之后需要一个退回的pathlist来退出菜单
+        self.executedPath = []
+
+    '''
+     当电视重启以后,uiautomator连接会断开,需要重新赋予UATRunner一个可用的连接对象
+    '''
+    def rebuildUiAutomator(self, pyUIAutomator, directionManageAndroid, focusManageAndroid):
+        self.pyU = pyUIAutomator
+        self.dm = directionManageAndroid
+        self.fm = focusManageAndroid
+        self.runnerCmd = UATRunnerCommand(self.uatPathManage, self.pyU, self.dm, self.fm)
+
+    '''
+    返回option的object
+    '''
+    def getOptionObject(self, option):
+        className = option[UATTree.TAB_OPTION_VIEW][UATTree.View_Class]
+        desc = option[UATTree.TAB_OPTION_VIEW][UATTree.View_Desc]
+        resId = option[UATTree.TAB_OPTION_VIEW][UATTree.View_ID]
+        text = option[UATTree.TAB_OPTION_VIEW][UATTree.View_Text]
+        return self.pyU.getUiObject(className=className, resourceId=resId, description=desc, text=text)
+
+    '''
+    根据parent的layout和一个option判断在 UATree中的哪个界面,或者还没有进入UATree界面中
+    :return parent数据,或者None。None代表未进入UATree界面中
+    '''
+    def locateParentUI(self):
+        cparent = None
+        treeDict = self.uatPathManage.uatData.getTreeDict()
+        # print "locateParentUI: treeDict levels:",treeDict.keys()
+        parentList = []
+        levelList = treeDict.keys()
+        flag = False
+        for index in range(levelList.__len__()-1, -1, -1):
+            level = levelList[index]
+            # print "locateParentUI,level:", index, level
+            levelDict = treeDict[level]
+            for parent in levelDict:
+                parentDict = levelDict[parent]
+                layout = parentDict["layout"]
+                # print "locateParentUI: layout:", layout
+                ret = self.runnerCmd.checkParentChoose(parentDict)
+                if ret > 0:
+                    cparent = parentDict
+                    flag = True
+                    break
+            if flag == True:
+                break
+
+        if cparent is None:
+            info(self.cls, "locateParentUI", "没有选中Parent,电视界面不知道在哪里", INFO)
+        else:
+            info(self.cls, "locateParentUI", "选中Parent:" + cparent[UATTree.TAB_NAME], INFO)
+        print "ParentParam:", cparent
+        return cparent
+
+    '''
+    在该option所在页面下,聚焦到目标option
+    '''
+    def moveToOption(self, option):
+        parent = self.uatPathManage.uatData.getParentByOption(option)
+        return self.runnerCmd.focusTargetOption(parent, option)
+
+    '''
+    :return -1 执行失败,放弃重新定位执行;0 执行失败,重新定位执行; 1 执行成功
+    '''
+    def runForwardPath(self, backPath, forwardPath):
+        fLen = forwardPath.__len__()
+        fParent = forwardPath[fLen-1]
+        # 当backPath和forwardPath的父交点为first层时,不能走shortcut_key,否则会导致执行出现问题(USB界面)
+        if fParent[UATTree.TAB_LEVEL] == "first" \
+                and backPath.__len__() < 2:
+            ret = self.openFirstParent(fParent, forwardPath)
+            if ret < 1: #注意打开firstparent界面成功,定位错误,会重新定位。场景:usb
+                return ret
+            # 逐步进入后面的parent页面
+            else:
+                ret = self.goMidPath(fParent, forwardPath[1: fLen- 1])
+            if ret is False:
+                return -1
+            #在目标parent中,选中目标option
+            return self.moveToOption(forwardPath[0])
+        else:
+            ret = self.goMidPath(forwardPath[fLen - 1], forwardPath[1:fLen - 1])
+            if ret is False:
+                return -1
+            # 在目标parent中,选中目标option
+            return self.moveToOption(forwardPath[0])
+
+    '''
+    打开第一个页面
+    :return: -1 代表activity打开失败,0 代表activity打开成功,焦点不在parent layout中;1 表示页面打开成功,焦点在parent layout中
+            -2 表示处理弹窗的过程中失败了
+    '''
+    def openFirstParent(self, fParent, forwardPath):
+        info(self.cls, "openFirstParent", "GOTO first parent " + fParent[UATTree.TAB_NAME], INFO)
+        flag = self.runnerCmd.executeShortCutKey(fParent)
+        if flag is False:
+            error(self.cls, "openFirstParent", "Open first parent%s fail."%(fParent[UATTree.TAB_NAME]), ERROR)
+            return -1
+        ret = self.runnerCmd.executeDialog(fParent)
+        if ret < 1:
+            return -2
+        flag1 = self.runnerCmd.checkParentChoose(fParent)
+        if flag1 < 1:
+            error(self.cls, "openFirstParent", "Open first parent activity.But parent %s not foucsed." % (fParent[UATTree.TAB_NAME]), ERROR)
+            return 0
+        return 1
+
+    def goMidPath(self, curParent, midParentList):
+        mLen = midParentList.__len__()
+        for index in range(mLen-1, -1, -1):
+            parent = midParentList[index]
+            info(self.cls, "goMidPath", "GOTO parent %s from parent %s "%(parent[UATTree.TAB_NAME], curParent[UATTree.TAB_NAME]), INFO)
+            ret = self.gotoNextParent(curParent, parent, midParentList)
+            if ret == 0:
+                return False
+            curParent = parent
+        return True
+
+    '''
+    :return 0:代表失败;1代表enterKey发送成功,进入下一个界面
+    '''
+    def gotoNextParent(self, curParent, targetParent, parentList):
+        targetOptionName = targetParent[UATTree.TAB_NAME]
+        targetOption = curParent[UATTree.TAB_OPTION][targetOptionName]
+        flag = self.moveToOption(targetOption)
+        if flag is False:
+            return 0
+        info(self.cls, "gotoNextParent", "moveToOption:%s done! executing enter key!"%targetOption[UATTree.TAB_NAME], INFO)
+        ret = self.runnerCmd.executeKey(targetOption[UATTree.TAB_ENTER_KEY])
+        if ret is False:
+            error(self.cls, "gotoNextParent",
+                  "Send parent %s enter key fail." % (curParent[UATTree.TAB_NAME]), ERROR)
+            return 0
+        else:
+            ret = self.runnerCmd.executeDialog(targetParent)
+            return ret
+
+
+    '''
+    执行backPath
+    :return true成功,false失败
+    '''
+    def runBackPath(self, backPath):
+        bLen = backPath.__len__()
+        if bLen <= 1:
+            return False
+        if bLen == 2:
+            return True
+        for index in range(1, bLen-1):
+            curParent = backPath[index]
+            prevParent = backPath[index+1]
+            info(self.cls, "runBackPath", "back to parent %s from parent %s."
+                 %(backPath[index][UATTree.TAB_NAME], backPath[index+1][UATTree.TAB_NAME]), INFO)
+            flag = self.backToPrevParent(curParent, prevParent)
+            if flag is False:
+                return False
+        return True
+
+    def backToPrevParent(self, curParent, targetParent):
+        backKeys = curParent[UATTree.TAB_TOPARENT_KEY]
+        self.runnerCmd.executeKey(backKeys)
+        dialog_A = curParent[UATTree.TAB_UI_VIEW][UATTree.UIView_Dialog_B]
+        if dialog_A.__len__() > 0:
+            ret = self.runnerCmd.executeDialog(curParent, isForward=False)
+            if ret == 0:
+                return False
+        flag = self.runnerCmd.checkParentChoose(targetParent)
+        if flag < 1:
+            error(self.cls, "backToPrevParent",
+                  "back to parent %s from parent %s fail." % (targetParent[UATTree.TAB_NAME], curParent[UATTree.TAB_NAME]), ERROR)
+            return False
+        return True
+
+    '''
+    fromFirst:表示是否指定从FirstParent打开开始路径执行。
+    count:递归控制变量,执行路径的最大尝试次数
+    '''
+    def focusOption(self, option, fromFirst, count = 0):
+        if fromFirst is True:
+            curParent = None
+        else:
+            curParent = self.locateParentUI()
+        backPath,forwardPath = self.uatPathManage.genOptionSmartPath(curParent, option)
+        info(self.cls,"focusOption", "backPath:", INFO)
+        self.uatPathManage.info(backPath)
+        info(self.cls,"focusOption", "forwardPath:", INFO)
+        self.uatPathManage.info(forwardPath)
+        if forwardPath.__len__() == 0:
+            return False
+        if backPath.__len__() == 0 and forwardPath.__len__() >= 2:
+            info(self.cls, "focusOption", "runBackPath success", INFO)
+            ret = self.runForwardPath(backPath, forwardPath)
+            self.executedPath = self.getExecutedPath(forwardPath)
+            if ret ==0: #重新计算路径执行
+                if count < 3:
+                    return self.focusOption(option, fromFirst, count+1)
+                else:
+                    info(self.cls, "focusOption", "runForwardPath fail %s times, focusOption Fail!!!"%count, INFO)
+                    return False
+        if backPath.__len__() >= 2 and forwardPath.__len__() >=2:
+            backFlag = self.runBackPath(backPath)
+            if backFlag is False:
+                info(self.cls, "focusOption", "runBackPath fail", INFO)
+                return False
+            info(self.cls, "focusOption", "runBackPath success", INFO)
+            goFlag = self.runForwardPath(backPath, forwardPath)
+            self.executedPath = self.getExecutedPath(forwardPath)
+            if goFlag < 1:
+                info(self.cls, "focusOption", "runForwardPath fail", INFO)
+                return False
+        info(self.cls, "focusOption", "runForwardPath success", INFO)
+        return True
+
+    '''
+    回退路径,仅仅只是使用forwardPath的话,最后一个元素是option,但是实际上已经进入了这个界面,应该判断的是parent。
+    所以要把forwardPath里面的option替换为parent;如果没有配这个option对应的parent,则去掉该option,以便exitMenuByPath函数使用。
+    '''
+    def getExecutedPath(self, forwardPath):
+        option = forwardPath[0]
+        parentName = option[UATTree.TAB_NAME]
+        parent = self.uatPathManage.uatData.getParentDict(parentName)
+        if parent is None:
+            forwardPath.remove(option)
+        else:
+            forwardPath[0] = parent
+        return forwardPath
+    '''
+    执行该option的EnterKey
+    '''
+    def sendOptionEnterKey(self, option):
+        enterKey = option[UATTree.TAB_ENTER_KEY]
+        return self.runnerCmd.executeKey(enterKey)
+
+    '''
+    检查当前页面是否存在目标option
+    '''
+    def checkOptionExist(self, option):
+        parent = self.uatPathManage.uatData.getParentByOption(option)
+        if not self.runnerCmd.checkParentChoose(parent):
+            return False
+        return self.runnerCmd.checkOptionExist(option, parent)
+
+
+    '''
+    在focusOption基础上,执行目标option的enter_key
+    '''
+    def openOption(self, option, fromFirst):
+        ret = self.focusOption(option, fromFirst)
+        if ret is False:
+            return False
+        print "executing enter key"
+        ret = self.runnerCmd.executeKey(option[UATTree.TAB_ENTER_KEY])
+        print "openOption, executeKey:%s"%ret
+        parent = self.uatPathManage.uatData.getParentDict(option[UATTree.TAB_NAME])
+        if parent is None:
+            return ret
+        else:
+            return self.runnerCmd.executeDialog(parent)
+
+    def openParent(self, parent, fromFirst):
+        if parent[UATTree.TAB_LEVEL] == 'first':
+            return self.openFirstParent(parent, "")
+        else:
+            prevOption = self.uatPathManage.uatData.getOption(parent[UATTree.TAB_NAME])
+            return self.openOption(prevOption, fromFirst)
+
+    def setOptionValue(self, option, value, exitMenu=True, fromFirst=True):
+        ret = self.openOption(option, fromFirst)
+        if ret is False:
+            info(self.cls, "setOptionValue", "openOption过程执行失败!!!", INFO)
+            if exitMenu is True:
+                self.runnerCmd.exitMenuByPath(self.executedPath)
+            return False
+        ret1 = self.runnerCmd.setValue(option, value)
+        if exitMenu is True:
+            ret2 = self.runnerCmd.exitMenuByPath(self.executedPath)
+        else:
+            ret2 = True
+        return (ret1 and ret2)
+
+
+
+    def checkOptionInfoView(self, option, cmpText = "", cmpPic = ""):
+        infoObj = self.getOptionInfoView(option)
+        result, picPath, cutPicPath = self.runnerCmd.getUIObjPic(infoObj)
+        if result is False:
+            error(self.cls, "checkOptionInfoView", "Warning:option %s 截取infoView所在坐标图片失败!!!" % option["name"], ERROR)
+            return -1, picPath, cutPicPath
+        if cmpPic != "":
+            result = self.runnerCmd.infoViewCmpPic(cmpPic, cutPicPath)
+            return result, picPath, cutPicPath
+        elif cmpText != "":
+            result = self.runnerCmd.infoViewCmpText(cmpText, cutPicPath)
+            return result, picPath, cutPicPath
+        else:
+            info(self.cls, "checkOptionInfoView", "未传入cmpText和cmpPic参数,仅检查infoView是否存在", INFO)
+            if infoObj is None:
+                error(self.cls, "checkOptionInfoView", "Warning:option %s 获取infoView失败!!!" % option["name"],
+                      ERROR)
+                return -1, picPath, cutPicPath
+            else:
+                info(self.cls, "checkOptionInfoView", "已识别到option %s的infoView" % option["name"], INFO)
+                return 1, picPath, cutPicPath
+
+
+    def getOptionInfoView(self, option):
+        optionView = option[UATTree.TAB_INFO_VIEW]
+        viewId = optionView[UATTree.View_ID]
+        viewText = optionView[UATTree.View_Text]
+        viewDesc = optionView[UATTree.View_Desc]
+        viewClass = optionView[UATTree.View_Class]
+        if viewId.__len__() == 0:
+            return None
+        infoObj = self.pyU.getUiObject(resourceId=viewId, text=viewText, className=viewClass, description=viewDesc)
+        return infoObj
+
+
+if __name__ == "__main__":
+    uatRunner = UATRunner()
+    option = uatRunner.uatPathManage.uatData.getOption("usb_video_h264-ac3")
+    uatRunner.openOption(option, False)
+    # print uatRunner.locateParentUI()
+    # parent = uatRunner.uatPathManage.uatData.getParentDict("usb_bmp")
+    # uatRunner.runnerCmd.checkParentChoose(parent)
+    # print option
+    # option = uatRunner.uatPathManage.uatData.getOption("usb_aac_option")
+    # cmpPic = r"D:/SAT/tmp\1572432282.74_cutPic_[932, 548, 1812, 597].png"
+    # result, picpath, cutPicPath = uatRunner.checkOptionInfoView(option, cmpPic=cmpPic)
+    # uatRunner.moveToOption(option)
+    # parent = uatRunner.locateParentUI()
+    # print "locateParentUI,parent:", parent
+    # option = uatRunner.uatPathManage.uatData.getOption("usb_brightness")
+    # print uatRunner.setOptionValue(option, 50)

+ 810 - 0
ssat_sdk/UATree/UAT_runnerCommand.py

@@ -0,0 +1,810 @@
+# -*- 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
+
+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()
+
+    #检测option组件是不是在电视上显示了
+    def checkOptionExist(self, option, parent):
+        #print "checkOptionExist,option:",option
+        moveKey = parent[UATTree.TAB_MOVE_KEY]
+        print "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
+            # 如果找到目标直接返回
+            if self.pyU.isElementExist(resourceId=option["optionView"][UATTree.UIView_ID],
+                                       text=option["optionView"][UATTree.UIView_Text],
+                                       description=option["optionView"][UATTree.UIView_Desc]):
+                return True
+            # choosingObj = self.getChooseUIObj(option)
+            # if lastObj != "":
+            #     if choosingObj.info == lastObj.info:
+            #         print "choosingObj.info == lastObj.info!!!!"
+            #         if count < Max_Try:
+            #             Max_Try = count
+            #         else:
+            #             break
+            # lastObj = choosingObj
+            if count < Max_Try:
+                flag = 1
+                count += 1
+                print "now count:", count
+            else:
+                flag = 0
+                Reversecount += 1
+                print "now Reversecount:", Reversecount
+            self.executeMoveKey(moveKey, flag)
+
+        return False
+
+    '''
+    检测option组件,在电视上是否被选中了
+    :param option 数据字典
+    :return -1:未进入option的页面,找不到option UIObject;0:进入了option的页面,未选中option;1 已经选中option
+        -2:代表未找到焦点
+    '''
+    def checkOptionChoose(self, option):
+        optionUIObj = self.pyU.getUiObject(text=option["optionView"][UATTree.View_Text],
+                                           resourceId=option["optionView"][UATTree.View_ID],
+                                           description=option["optionView"][UATTree.View_Desc])
+        if optionUIObj is None or optionUIObj.count == 0:
+            return -1
+        curUIObj = self.getChooseUIObj(option)
+        if curUIObj is None or curUIObj.count == 0:
+            return -2
+        if self.dm.isHasAnotherBounds(curUIObj.info["bounds"], optionUIObj.info['bounds']):
+            return 1
+        else:
+            return 0
+
+
+    '''
+    根据option配置的焦点方式,返回当前页面焦点组件
+    '''
+    def getChooseUIObj(self, option):
+        print "getChooseUIObj option:",option["focus-select"]
+        if option["focus-select"]["type"].__len__() > 1:
+            chooseType = option["focus-select"][UATTree.FS_Type]
+            if chooseType.lower() == "focus":
+                return self.pyU.getFocusedUIObject()
+            elif chooseType.lower() == "select":
+                return self.pyU.getSelectedUIObject()
+            elif chooseType.lower() == "no":
+                resId = option[UATTree.TAB_OPTION_VIEW][UATTree.View_ID]
+                text = option[UATTree.TAB_OPTION_VIEW][UATTree.View_Text]
+                className = option[UATTree.TAB_OPTION_VIEW][UATTree.View_Class]
+                desc = option[UATTree.TAB_OPTION_VIEW][UATTree.View_Desc]
+                return self.pyU.getUiObject(resourceId=resId, text=text, className=className, description=desc)
+            else:
+                error(self.cls, "getChooseUIObj", option["name"]+" option focus-select属性配置异常", ERROR)
+                return None
+        elif option["focuseView"][UATTree.Focus_Text].__len__() > 0\
+                or option["focuseView"][UATTree.Focus_ID].__len__() > 1\
+                or option["focuseView"][UATTree.Focus_Desc].__len__() > 0:
+            return self.pyU.getUiObject(text=option["focuseView"][UATTree.Focus_Text],
+                                           resourceId=option["focuseView"][UATTree.Focus_ID],
+                                           description=option["focuseView"][UATTree.Focus_Desc])
+        else:
+            error(self.cls, "getChooseUIObj", option["name"] + " option 需要配置 focus-select或者FocusView", ERROR)
+            return None
+
+    '''
+    检测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["name"], DEBUG)
+        chooseTypeDict = {}
+        layoutResId = parent["layout"][UATTree.Layout_ID]
+        uiView = parent[UATTree.TAB_UI_VIEW]
+        uiViewResId = uiView[UATTree.View_ID]
+        uiViewText = uiView[UATTree.View_Text]
+        uiViewDesc = uiView[UATTree.View_Desc]
+        if layoutResId == "" and uiViewResId == "" and uiViewText == "" and uiViewDesc == "":
+            debug(self.cls, "checkParentChoose", "Warning:Parent %s的Layout resId和UIView信息获取失败!!请注意检查UATree文件!!!"%parent['name'], DEBUG)
+            return -1
+        elif layoutResId != "":
+            # 如果存在UIView信息,则先判断UIView
+            if uiViewResId != "" or uiViewText != "" or uiViewDesc != "":
+                isExist = self.checkUIViewExist(uiView)
+                if isExist is False:
+                    info(self.cls, "checkParentChoose", "当前页面不存在Parent:%s的UIView组件,判断该界面非此parent"%parent['name'], INFO)
+                    return -3
+                else:
+                    info(self.cls, "checkParentChoose", "已识别出Parent:%s的UIView组件"%parent['name'], INFO)
+            description = parent["layout"][UATTree.Layout_Desc]
+            if description != "":
+                layoutUIObj = self.pyU.getUiObject(resourceId=parent["layout"][UATTree.Layout_ID],
+                                                   description=parent["layout"][UATTree.Layout_Desc])
+            else:
+                layoutUIObj = self.pyU.getUiObject(resourceId=parent["layout"][UATTree.Layout_ID])
+            if layoutUIObj is None or layoutUIObj.count == 0:
+                # debug(self.cls, "checkParentChoose", "parent isn't " + parent["name"], DEBUG)
+                return -1
+            # print "checkParentChoose, layoutUIObj:",layoutUIObj.info
+            debug(self.cls, "checkParentChoose", "layoutUIObj:"+str(layoutUIObj.info), DEBUG)
+            for optionName in parent["option"]:
+                option = parent["option"][optionName]
+                if option["focus-select"]["type"].__len__() > 1:
+                    chooseTypeDict[option["focus-select"][UATTree.FS_Type]] = option
+                elif option["focuseView"][UATTree.Focus_Text].__len__() > 0\
+                    or option["focuseView"][UATTree.Focus_ID].__len__() > 1\
+                    or option["focuseView"][UATTree.Focus_Desc].__len__() > 0:
+                    chooseTypeDict[option["focuseView"][UATTree.Focus_ID]
+                                + option["focuseView"][UATTree.Focus_Text]
+                                + option["focuseView"][UATTree.Focus_Desc]] = option
+            info(self.cls, "checkParentChoose", str(chooseTypeDict), INFO)
+            for key in chooseTypeDict.keys():
+                option = chooseTypeDict[key]
+                chooseUIObj = self.getChooseUIObj(option)
+                #TODO 如果选中效果,是没有type[no],例如快捷键,如何处理?
+                if chooseUIObj is None or chooseUIObj.count == 0:
+                    return -2
+                debug(self.cls, "checkParentChoose", "chooseUIObj:" + str(chooseUIObj.info), DEBUG)
+
+                # 获取到chooseUIObj后,与layout对比坐标,确认焦点是否存在于layout之中
+                layoutBounds = layoutUIObj.info['bounds']
+                choosingBounds = chooseUIObj.info['bounds']
+                if not self.dm.isHasAnotherBounds(layoutBounds, choosingBounds):
+                    info(self.cls, "checkParentChoose",
+                         "识别出parent %s的Layout,但焦点不在该Layout之中,判断界面未处于该parent" % (parent['name']), INFO)
+                    return -2
+
+                # 如果该页面text属性不为空,则确认该parent下所有option的text,是否存在于当前页面中;如果text属性为空,则判断index属性
+                for optionName in parent["option"]:
+                    option = parent["option"][optionName]
+                    # 如果有text属性则确认Text,是否能对比成功
+                    optionText = option[UATTree.TAB_OPTION_VIEW][UATTree.View_Text]
+                    optionId = option[UATTree.TAB_OPTION_VIEW][UATTree.View_ID]
+                    optionIndex = option[UATTree.TAB_OPTION_VIEW][UATTree.View_Index]
+                    if optionText.__len__() >0:
+                        info(self.cls, "checkParentChoose", "checking parent %s text %s"%(parent['name'], optionText), INFO)
+                        textObj = self.pyU.getUiObject(text=optionText)
+                    elif optionId.__len__() >0:
+                        info(self.cls, "checkParentChoose", "checking parent %s optionId %s" % (parent['name'], optionId),
+                             INFO)
+                        textObj = self.pyU.getUiObject(resourceId=optionId)
+                    elif optionIndex != "":
+                        # index属性无法用于判断option,如果在以index属性作为判断依据的页面,则直接返回在这个页面
+                        info(self.cls, "checkParentChoose",
+                             "已找到目标parent %s,该页面为Index item页面,无法判断具体option", INFO)
+                        return 1
+                    else:
+                        error(self.cls, "checkParentChoose", "当前参数不足以判断option %s是否存在"%(option['name']), ERROR)
+                        continue
+                    if textObj.exists:
+                        info(self.cls, "checkParentChoose",
+                             "已找到目标parent %s,并识别出该parent下的option %s" % (parent['name'], option['name']), INFO)
+                        return 1
+                else:
+                    info(self.cls, "checkParentChoose",
+                         "return 0" , 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
+
+    def checkUIViewExist(self, uiView):
+        resid = uiView[UATTree.View_ID]
+        text = uiView[UATTree.View_Text]
+        description = uiView[UATTree.View_Desc]
+        uiViewObj = self.pyU.getUiObject(resourceId=resid,
+                                         text=text,
+                                         description=description)
+        if not uiViewObj.exists:
+            return False
+        else:
+            return True
+
+    '''
+    检测某个弹窗是否存在。
+    弹窗相关的参数放在UIView中配置。
+    当存在resId参数时,使用resId获取对象并获取文本,再与text做比较;
+    当不存在resId参数时,使用text直接获取对象,判断对象是否存在。
+    与checkParentChoose判断不一致,做特殊处理。
+    '''
+    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 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":
+                if viewText != "" and viewId != "":
+                    info(self.cls, "focusTargetOption", "going toDestFocusByText_with_FocuseResourceId, Option:%s"%option[UATTree.TAB_NAME], INFO)
+                    info(self.cls, "focusTargetOption", "viewText=%s, focuseResId=%s"%(viewText, focuseViewId), INFO)
+                    return self.fm.toDestFocusByText_with_FocuseResourceId(text=viewText,
+                                                                           focuseResId=viewId,
+                                                                           Max_Try=Max_Try,
+                                                                           keyType=keyType,
+                                                                           hb_keyDict=hb_keyDict)
+
+                elif viewText != "":
+                    info(self.cls, "focusTargetOption", "going toDestFocusByText_for_RecyclerView, Option:%s"%option[UATTree.TAB_NAME], INFO)
+                    return self.fm.toDestFocusByText_for_RecyclerView(text = viewText,
+                                                                   findDirection = findDirection,
+                                                                   Max_Try = Max_Try,
+                                                                   keyType = keyType,
+                                                                   hb_keyDict=hb_keyDict)
+                elif viewId != "":
+                    info(self.cls, "focusTargetOption", "going toDestTargetByResourceId_for_RecyclerView, Option:%s"%option[UATTree.TAB_NAME], INFO)
+                    return self.fm.toDestTargetByResourceId_for_RecyclerView(resourceId = viewId,
+                                                                             chooseType = chooseType.lower(),
+                                                                             Max_Try = Max_Try,
+                                                                             keyType = keyType,
+                                                                             hb_keyDict=hb_keyDict)
+                elif viewDesc != "":
+                    info(self.cls, "focusTargetOption", "going toDestFocusByDescription_for_RecyclerView, Option:%s"%option[UATTree.TAB_NAME], INFO)
+                    return self.fm.toDestFocusByDescription_for_RecyclerView(description=viewDesc,
+                                                                             Max_Try = Max_Try,
+                                                                             keyType = keyType,
+                                                                             hb_keyDict=hb_keyDict)
+                elif viewIndex != "":
+                    info(self.cls, "focusTargetOption", "going focusItemByIndexFromRecyclerView, Option:%s"%option[UATTree.TAB_NAME], INFO)
+                    return self.fm.focusItemByIndexFromRecyclerView(recyclerView_resId = recyclerViewId,
+                                                                    child_class = viewClass,
+                                                                    target_index = viewIndex,
+                                                                    Max_Try = Max_Try,
+                                                                    keyType = keyType,
+                                                                    hb_keyDict=hb_keyDict)
+                else:
+                    error(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " option focus-select属性配置异常", ERROR)
+                    return False
+            elif chooseType.lower() == "select":
+                if viewText != "":
+                    info(self.cls, "focusTargetOption", "going toDestSelectByText_for_RecyclerView, Option:%s"%option[UATTree.TAB_NAME], INFO)
+                    return self.fm.toDestSelectByText_for_RecyclerView(text = viewText,
+                                                                        optionViewResoucreId = viewId,
+                                                                        listViewResourceId = recyclerViewId,
+                                                                        findDirection = findDirection,
+                                                                        Max_Try = Max_Try,
+                                                                        keyType = keyType,
+                                                                        hb_keyDict=hb_keyDict)
+                elif viewId != "":
+                    info(self.cls, "focusTargetOption", "going toDestTargetByResourceId_for_RecyclerView, Option:%s"%option[UATTree.TAB_NAME], INFO)
+                    return self.fm.toDestTargetByResourceId_for_RecyclerView(resourceId = viewId,
+                                                                        chooseType = chooseType.lower(),
+                                                                        Max_Try = Max_Try,
+                                                                        keyType = keyType,
+                                                                        hb_keyDict=hb_keyDict)
+            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)
+                return True
+            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
+
+    '''
+    执行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):
+        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, 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.checkDialogExist(dialog)
+                if ret < 1:
+                    info(self.cls, "executeDialog", "parent %s dialog %s doesnt 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.executeKey(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
+
+    '''
+    执行设值动作,区分数字设值以及非数字设值
+    '''
+    def setValue(self, option, value):
+        textValue = option[UATTree.TAB_TEXT_VALUE]
+        info(self.cls, "setValue", "textValue:%s"%textValue, INFO)
+        if textValue[UATTree.ValueView_Value] != "":
+            enterKey = option[UATTree.TAB_ENTER_KEY]
+            if enterKey[UATTree.Key_Event] != "":
+                keyList = enterKey[UATTree.Key_Event]
+                keyType = UATTree.Key_Event
+            elif enterKey[UATTree.Key_IR] != "":
+                keyList = enterKey[UATTree.Key_IR]
+                keyType = UATTree.Key_IR
+            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):
+                    # 获取数值时存在两种情况,一种是数值文本在聚焦组件里,一种在聚焦组件之外,分别处理。
+                    # 先在聚焦组件内找数值
+                    choosedObj = self.getChooseUIObj(option)
+                    print "choosedObj.info:", choosedObj.info
+                    text = self.getValueInObjectByTextResourceId(choosedObj, valueViewResId)
+                    if text is None:
+                        # 组件之中找不到时,则直接用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)
+                    text_num = float(text)
+                    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.executeKey(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):
+                    # 获取当前值时存在两种情况,一种是数值文本在聚焦组件里,一种在聚焦组件之外,分别处理。
+                    # 先在聚焦组件内找当前值
+                    choosedObj = self.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)
+            ret = self.focusTargetOption(parent, option)
+            if not ret:
+                info(self.cls, "setValue", "未能聚焦至目标value %s,设值失败"%value, INFO)
+                return False
+            info(self.cls, "setValue", "已聚焦至目标value %s"%value, INFO)
+            print "executing enter_key"
+            self.executeKey(option[UATTree.TAB_ENTER_KEY])
+            return True
+
+    '''
+    根据传入路径进行退出菜单的动作。
+    顺着传入的parent path,逐个确认当前页面是否为该parent。如果不是该parent,则往path后面继续遍历;如果是该parent,则执行该层parent的toParentKey
+    '''
+    def exitMenuByPath(self, path):
+        print "exitMenuByPath path:", path
+        for i in range(0, path.__len__()):
+            parent = path[i]
+            info(self.cls, "exitMenuByPath", "check parent:%s"%parent, INFO)
+            ret = self.checkParentChoose(parent)
+            if ret < 1:
+                continue
+            toParentKey = parent[UATTree.TAB_TOPARENT_KEY]
+            info(self.cls, "exitMenuByPath", "now is in parent:%s"%parent, 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, INFO)
+                self.executeKeyByType("back", UATTree.Key_Event)
+            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

+ 421 - 0
ssat_sdk/UATree/UAT_tree.py

@@ -0,0 +1,421 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+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为该项参数的括符符号,必须成对带入。默认为中括号[]
+    '''
+    def parseParam(self, key, params, char = "[]"):
+        # 为防止key在其他地方存在相同关键字,添加一个"["做区别
+        key = key + char[0]
+        keyIndex = params.find(key)
+        if keyIndex == -1:
+            return ""
+        # key.__len__()-1 为去掉"["的处理
+        str1 = params[keyIndex + key.__len__()-1:params.__len__()]
+        i1 = str1.find(char[0])
+        i2 = str1.find(char[1])
+        if i1 == -1 or i2 == -1:
+            return ""
+        str2 = str1[i1 + 1: i2]
+        return str2.strip()
+
+    def parseMulParam(self, keyList, params):
+        paramDict = {}
+        for key in keyList:
+            value = self.parseParam(key, params)
+            paramDict[key] = value
+        return paramDict
+
+    #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[]组合
+    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
+
+    #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"
+    def parseView(self, viewStr):
+        keyList = [self.View_ID, self.View_Text, self.View_Desc, self.View_Bounds, self.View_Index, self.View_Class]
+        return self.parseMulParam(keyList, viewStr)
+
+    # 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 坐标
+    def parseLayout(self, viewStr):
+        keyList = [self.Layout_ID, self.Layout_Desc, self.Layout_Bounds]
+        return self.parseMulParam(keyList, viewStr)
+
+    #Android FocusView数据
+    Focus_ID = "resid" #android view resource-id
+    Focus_Text = "text" #android view 文本内容
+    Focus_Desc = "desc" #android view描述
+    def parseFocus(self, viewStr):
+        keyList = [self.Focus_ID, self.Focus_Text, self.Focus_Desc]
+        return self.parseMulParam(keyList, viewStr)
+
+    #Value数据
+    ValueView_ID = "resid" #android view resource-id
+    ValueView_Text = "text" #android view 文本内容
+    ValueView_Desc = "desc" #android view描述
+    ValueView_Value = "value"  # 输入的值范围。格式:first, second, ...  或者 0-100
+    ValueView_Min = "minvalue" # 如果是数值范围形式的值,返回最小值
+    ValueView_Max = "maxvalue" # 如果是数值范围形式的值,返回最小值
+    ValueView_StepSize = "stepsize" # 如果是数值范围形式的值,每次调整数值的步长。读不到此参数时,默认为1。
+    ValueView_Duration = "duration" # 如果是数值范围形式的值,每次调整数值的间隔时间。读不到此参数时,默认为0.2。
+    def parseValueView(self, viewStr):
+        keyList = [self.ValueView_ID, self.ValueView_Text, self.ValueView_Desc, self.ValueView_Value, self.ValueView_StepSize, self.ValueView_Duration]
+        paramDict = self.parseMulParam(keyList, viewStr)
+        valueStr = paramDict[self.ValueView_Value]
+        if valueStr != "":
+            i = valueStr.find(",")
+            if i != -1:
+                valueList = strToList(valueStr, ",")
+                isRange = False
+                # 处理range类型(数值)
+                try:
+                    if valueList.__len__() == 2:
+                        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
+        return paramDict
+
+    #focus-select数据
+    FS_Type = "type"
+    def parseFocusSelectType(self, fsType):
+        keyList = [self.FS_Type]
+        return self.parseMulParam(keyList, fsType)
+
+
+    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:
+            prevLevel = self.getPrevLevel(level)
+            prevOption = self.getOpitonInLevel(parentName,prevLevel)
+            if prevOption is None:
+                error(str(self.__class__), "addParent", "Option %s not found in level %s"%(parentName, prevLevel), ERROR)
+            prevParent = self.getParentByOption(prevOption)
+            if prevParent is not None:
+                parentDict[UATTree.TAB_PREV_PARENT] = prevParent
+                prevParent[UATTree.TAB_NEXT_PARENT][parentName] = parentDict
+            else:
+                error(str(self.__class__), "addParent", "Option %s's parent not found in level %s"%(parentName, prevLevel), ERROR)
+
+        self.treeDict[level][parentName] = parentDict
+
+    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.parseView(optionView)
+            optionDict[UATTree.TAB_FOCUS_SELECT] = self.parseFocusSelectType(focusSelect)
+            optionDict[UATTree.TAB_FOCUSE_VIEW] = self.parseFocus(focusView)
+            optionDict[UATTree.TAB_ENTER_KEY] = self.parseKey(enterKey)
+            optionDict["textValue"] = 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.parseView(optionView)
+            optionDict[UATTree.TAB_FOCUS_SELECT] = self.parseFocusSelectType(focusSelect)
+            optionDict[UATTree.TAB_FOCUSE_VIEW] = self.parseFocus(focusView)
+            optionDict[UATTree.TAB_ENTER_KEY] = self.parseKey(enterKey)
+            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的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
+
+    '''
+    获取当前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()

+ 6 - 0
ssat_sdk/UATree/UAT_treeConstant.py

@@ -0,0 +1,6 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+
+class TreeConst():
+    P_Dialog_F = "dialog_forward" #dialog_forward:前进中的弹窗;
+    P_Dialog_B = "dialog_back" #dialog_back:后退中的弹窗

+ 2 - 0
ssat_sdk/UATree/__init__.py

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

+ 4 - 0
ssat_sdk/__init__.py

@@ -0,0 +1,4 @@
+__all__ = ["sound_tool", "pic_tool", "sat_environment"]
+
+from pic_tool import *
+from sat_environment import *

+ 686 - 0
ssat_sdk/action/Executor.py

@@ -0,0 +1,686 @@
+#-*- coding:utf-8 -*-
+import os, sys, time
+import xlrd
+import xlwt
+import sqlite3
+import json
+from ssat_sdk.picture.feature_detect import FeatureDetect
+from ssat_sdk.utils import LoggingUtil
+from ssat_sdk.picture import image_util 
+
+class CExcelData():
+    def __init__(self):
+        # 编号;
+        self.num = 0
+        # 类型;
+        self.type = ''
+        # 控件id;
+        self.ctrlid = ''
+        # 类型描述;
+        self.desc = ''
+        # 类型事件;
+        self.event = ''
+        # 类型参数;
+        self.params = ''
+        # 类型结果;
+        self.typeresult = ''
+        # 事件结果;
+        self.eventresult = ''
+    
+    def getdata(self, data):
+        if data is not None:
+            self.num, self.type, self.ctrlid, self.desc, self.event, self.params, self.typeresult, self.eventresult = tuple(data)
+    #end-fun
+
+    def tolist(self):
+        return [self.num, self.type, self.ctrlid, self.desc, self.event, self.params, self.typeresult, self.eventresult]
+    #end-fun
+
+class CImgView():
+    def __init__(self):
+        # 图元id;
+        self.id = ''
+        # 图元名称;
+        self.name = ''
+        # 图元类型:按钮、滑块
+        self.type = ''
+        # 图元领空区域;
+        self.airspace = []
+        # 图元未选时图标区域;
+        self.iconarea = []
+        # 图元未选时文本区域;
+        self.textarea = []
+        # 图元未选时文本内容;
+        self.textcontent = ''
+        # 图元未选时图标内容:使用路径;
+        self.iconcontent = ''
+        # 图元选中时图标区域;
+        self.iconarea2 = []
+        # 图元选中时文本区域;
+        self.textarea2 = []
+        # 图元选中时的文件内容;
+        self.textcontent2 = ''
+        # 图元选中时的图标内容;
+        self.iconcontent2 = ''
+        # 展开键;
+        self.openkey = ''
+        # 返回键;
+        self.returnkey = ''
+        # 弹出键;
+        self.popkey = ''
+        # 图元所属图区id;
+        self.areaid = 0
+        # 图元关联图区id;
+        self.nextareaid = 0
+    
+    def getdata(self,data):
+        self.id = data[0]
+        self.name = data[1]
+        self.type = data[2]
+        self.airspace = [] if data[3] is None else json.loads(data[3])
+        self.iconarea = [] if data[4] is None else json.loads(data[4])
+        self.textarea = [] if data[5] is None else json.loads(data[5])
+        self.textcontent = data[6]
+        self.iconcontent = data[7]
+        self.iconarea2 = data[8]
+        self.textarea2 = data[9]
+        self.textcontent2 = data[10]
+        self.iconcontent2 = data[11]
+        self.openkey = data[12]
+        self.returnkey = data[13]
+        self.popkey = data[14]
+        self.areaid = data[15]
+        self.nextareaid = data[16]
+
+class CImgArea():
+    def __init__(self):
+        self.id = ''
+        self.name = ''
+        self.isroot = False
+        self.popkey = ''
+        self.returnkey = ''
+        self.examplepic = ''
+        self.coodinate = []
+        self.bgproperty = ''
+        self.uidtype = ''
+        self.uidcontent = ''
+        self.uidarea = []
+        self.layouttype = ''
+        self.direction = ''
+        self.method = ''
+        self.refcoordFocusArea = []
+        self.refcoordIconArea = []
+        self.refcoordTextArea = []
+        self.methodParam = {}
+        self.layoutParam = {}
+        self.ocrParam = []
+    
+    def getdata(self, data):
+        self.id = data[0]
+        self.name = data[1]
+        self.isroot = data[2]
+        self.popkey = data[3]
+        self.returnkey = data[4]
+        self.examplepic = data[5]
+        self.coodinate = [] if data[6] is None else json.loads(data[6])
+        self.bgproperty = data[7]
+        self.uidtype = data[8]
+        self.uidcontent = data[9]
+        self.uidarea = data[10]
+        self.layouttype = data[11]
+        self.direction = data[12]
+        self.method = data[13]
+        self.refcoordFocusArea = [] if data[14] is None else json.loads(data[14])
+        self.refcoordIconArea = [] if data[15] is None else json.loads(data[15])
+        self.refcoordTextArea = [] if data[16] is None else json.loads(data[16])
+        self.methodParam = {} if data[17] is None else json.loads(data[17])
+        self.layoutParam = {} if data[18] is None else json.loads(data[18])
+        self.ocrParam = [] if data[19] is None else json.loads(data[19])
+
+    #end-fun
+
+class CParameters():
+    def __init__(self):
+        self.id = 0
+        self.areaid = 0
+        self.method = ''
+        self.params = ''
+    
+    def getdata(self,data):
+        self.id = data[0]
+        self.areaid = data[1]
+        self.method = data[2]
+        self.params = data[3]
+        if self.method == u'全局二值化':
+            print(u'全局二值化')
+        elif self.method == u'局部二值化':
+            pass
+        elif self.method == u'方差二值化':
+            pass
+        elif self.method == u'模板匹配':
+            pass
+        elif self.method == u'形状匹配':
+            pass
+    #end-fun
+
+class CExecutor():
+    def __init__(self, xls_path, db_path):
+        # db连接对象;
+        self.conn = None
+        # 游标;
+        self.cursor = None
+        # excel路径;
+        self.xls_path = xls_path
+        # db路径;
+        self.db_path = db_path
+        # excel数据;
+        self.xls_data = []
+        # 识别类对象;
+        self.feature = FeatureDetect()
+    #end-fun
+    
+    def read_excel(self, path = None):
+        if path is None:
+            path = self.xls_path
+        #end-if
+        # 打开文件;
+        wb = xlrd.open_workbook(filename=path)
+        if wb is None:
+            return
+        #通过索引获取表格;
+        sheet1 = wb.sheet_by_index(0)
+        for i in range(1, sheet1.nrows):
+            # 获取行内容;
+            rows = sheet1.row_values(i)
+            data = CExcelData()
+            data.getdata(rows)
+            self.xls_data.append(data)
+        #end-for
+    #end-fun
+
+    def get_conn(self, path = None):
+        if path is None:
+            path = self.db_path
+        #end-if
+        if os.path.exists(path) and os.path.isfile(path) :
+            self.conn = sqlite3.connect(path)
+        #end-if
+    #end-fun
+
+    def get_cursor(self) :
+        if self.conn is not None:
+            self.cursor = self.conn.cursor()
+        else:
+            self.cursor = self.get_conn().cursor()
+    #end-fun
+
+    def fetchone(self, sql, data) :
+        if self.conn is None:
+            return
+        if sql is not None and sql != '' :
+            if data is not None :
+                #Do this instead
+                d = (data, )
+                cu = self.cursor
+                cu.execute(sql, d)
+                rec = cu.fetchall()
+                return rec
+            else:
+                print('the [{}] equal None!'.format(data))
+                return None
+        else:
+            print('the [{}] is empty or equal None!'.format(sql))
+            return None
+    #end-fun
+
+    def getImgViewData(self,id):
+        sql = "SELECT ImgView.Id,"\
+        "ImgView.Name,"\
+        "ImgView.Type,"\
+        "ImgView.AirspaceArea,"\
+        "ImgView.IconArea,"\
+        "ImgView.TextArea,"\
+        "ImgView.TextContent,"\
+        "ImgView.IconContent,"\
+        "ImgView.IconArea2,"\
+        "ImgView.TextArea2,"\
+        "ImgView.TextContent2,"\
+        "ImgView.IconContent2,"\
+        "ImgView.OpenKey,"\
+        "ImgView.ReturnKey,"\
+        "ImgView.PopKey,"\
+        "ImgView.AreaId,"\
+        "ImgView.NextAreaId "\
+        "FROM ImgView WHERE id = ?"
+        # 获取记录集;
+        record = self.fetchone(sql, id) 
+        data = CImgView()
+        if record is not None:
+            if len(record) > 0:
+                data.getdata(record[0])
+                return data
+        return None
+    #end-fun
+
+    def getImgAreaData(self, id):
+        sql = "SELECT ImgArea.Id,"\
+        "ImgArea.Name,"\
+        "ImgArea.IsRoot,"\
+        "ImgArea.PopKey,"\
+        "ImgArea.ReturnKey,"\
+        "ImgArea.ExamplePicture,"\
+        "ImgArea.Coordinate,"\
+        "ImgArea.BackgroundProperties,"\
+        "ImgArea.UIdType,"\
+        "ImgArea.UIdContent,"\
+        "ImgArea.UIdArea,"\
+        "ImgArea.LayoutType,"\
+        "ImgArea.Direction,"\
+        "ImgArea.Method,"\
+        "ImgArea.refcoordFocusArea,"\
+        "ImgArea.refcoordIconArea,"\
+        "ImgArea.refcoordTextArea,"\
+        "ImgArea.MethodParam,"\
+        "ImgArea.LayoutParam,"\
+        "ImgArea.OCRParam "\
+        "FROM ImgArea WHERE id = ?"
+        # 获取记录集;
+        record = self.fetchone(sql, id) 
+        data = CImgArea()
+        if record is not None:
+            if len(record) > 0:
+                data.getdata(record[0])
+                return data
+        return None
+    #end-fun
+
+    def getParameters(self, areaid):
+        sql = "SELECT Parameter.Id,"\
+        "Parameter.AreaId,"\
+        "Parameter.Method,"\
+        "Parameter.Parameters "\
+        "FROM Parameter WHERE AreaId = ?"
+        # 获取记录集;
+        record = self.fetchone(sql, areaid) 
+        data = CParameters()
+        if record is not None:
+            if len(record) > 0:
+                data.getdata(record[0])
+                return data
+        return None
+    #end-fun
+
+    # 1、找到加返回True,否则False
+    # 2、返回当前焦点坐标;
+    def doVerifyOnTargetCtrl(self, pel_data, map_data):
+        # 截图,原图路径;
+        screenshot = os.path.join(LoggingUtil.getCaseRunLogDirPath(), "TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        self.feature.vp.takePicture(screenshot)
+
+        # 根据方法分类执行;
+        if map_data.method == u"全局二值化":
+            contour = self.feature.getGrayBinaryContour(screenshot, 
+                                                        map_data.coodinate, 
+                                                        map_data.methodParam['gray'], 
+                                                        255, 
+                                                        map_data.methodParam['inside'], 
+                                                        map_data.methodParam['min-peri'],
+                                                        map_data.methodParam['max-peri'],
+                                                        map_data.methodParam['min-area'],
+                                                        map_data.methodParam['max-area'],)
+            # 是否找到轮廓;
+            if contour is None:
+                return False, [], screenshot
+            
+            # 获取文本坐标;
+            textbox = self.feature.getObjAbsoluteBox(contour, map_data.refcoordFocusArea, map_data.refcoordTextArea)
+            # ocr识别;
+            ocrImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(), "OCR_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+            image_util.saveCropPic(screenshot, ocrImgPath, textbox)
+            result, text = self.feature.OCR.findPicStr(pel_data.textcontent, ocrImgPath, map_data.ocrParam[0], map_data.ocrParam[1], map_data.ocrParam[2])
+            # 返回结果;
+            return result, contour, screenshot
+        elif map_data.method == u"局部二值化":
+            pass
+        elif map_data.method == u"方差二值化":
+            pass
+        elif map_data.method == u"模板匹配":
+            # 模板匹配函数需要改造;
+            setting = {'method': 5, 'colorType': 0, 'thresholdVal': 0, 'thresholdMaxVal': 255, 'matchVal': map_data.methodParam['match'] }
+            match_result = self.feature.matchSingleImage(screenshot, map_data.coodinate, map_data.methodParam['tempdir'], setting)
+            if match_result is None:
+                return False, [], screenshot
+            else:
+                # 获取文本坐标;
+                textbox = self.feature.getObjAbsoluteBox(match_result['coordinate'], map_data.refcoordFocusArea, map_data.refcoordTextArea)
+                # ocr识别;
+                ocrImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(), "OCR_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+                image_util.saveCropPic(screenshot, ocrImgPath, textbox)
+                result, text = self.feature.OCR.findPicStr(pel_data.textcontent, ocrImgPath, map_data.ocrParam[0], map_data.ocrParam[1], map_data.ocrParam[2])
+                # 返回结果;
+                return result, match_result['coordinate'], screenshot
+        elif map_data.method == u"形状匹配":
+            pass
+        # 不存在的方法,直接返回False;
+        return False, [], screenshot
+    #end-fun
+
+    # 判断是否在目标图区上;
+    # 根据唯一标识内容来判断;
+    def doVerifyOnTargetMapArea(self, map_data):
+        result, coodr = False, []
+        # 截图,原图路径;
+        screenshot = os.path.join(LoggingUtil.getCaseRunLogDirPath(), "TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        self.feature.vp.takePicture(screenshot)
+        if map_data.uidtype == u"图标":
+            # 模板匹配函数需要改造;
+            setting = {'method': 5, 'colorType': 0, 'thresholdVal': 0, 'thresholdMaxVal': 255, 'matchVal': map_data.methodParam['match'] }
+            match_result = self.feature.matchSingleImage(screenshot, map_data.coodinate, map_data.methodParam['tempdir'], setting)
+            if match_result is None:
+                return False, [], screenshot
+            else:
+                return True, match_result['coordinate'], screenshot
+        elif map_data.uidtype == u"文本":
+            pass
+        elif map_data.uidtype == u"颜色":
+            pass
+        elif map_data.uidtype == u"轮廓":
+            pass
+        elif map_data.uidtype == u"形状":
+            pass
+        elif map_data.uidtype == u"":
+            pass 
+        # 返回结果;
+        return result, coodr, screenshot
+
+    # 执行事件;
+    def doCtrlEvent(self, xls_data, pel_data, map_data):
+        # 执行前截图分析;
+        screenshot = os.path.join(LoggingUtil.getCaseRunLogDirPath(), "TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        self.feature.vp.takePicture(screenshot)
+        # 类型判断;
+        if pel_data.type == u"滑块":
+            if xls_data.event == u"编辑":
+                if map_data.layouttype == u"图独":
+                    # 获取文本坐标;
+                    textbox = self.feature.getObjAbsoluteBox(map_data.coodinate, map_data.refcoordFocusArea, map_data.refcoordTextArea)
+                    # ocr识别;
+                    ocrImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(), "OCR_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+                    image_util.saveCropPic(screenshot, ocrImgPath, textbox)
+                    text = self.feature.OCR.getStrWithImgProcess(ocrImgPath, map_data.ocrParam[2], map_data.ocrParam[0], map_data.ocrParam[1])
+                    if text is not None and text is not '':
+                        count = int(xls_data.params) - int(text) 
+                        if count > 0:
+                            self.feature.redRat3.sendKey('right', count, 0.1)
+                        elif count < 0:
+                            self.feature.redRat3.sendKey('left', abs(count), 0.1)
+
+                        # 执行后截图分析;
+                        screenshot = os.path.join(LoggingUtil.getCaseRunLogDirPath(), "TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+                        self.feature.vp.takePicture(screenshot)
+                        # 验证结果是否正确;# ocr识别;
+                        ocrImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(), "OCR_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+                        image_util.saveCropPic(screenshot, ocrImgPath, textbox)
+                        text = self.feature.OCR.getStrWithImgProcess(ocrImgPath, map_data.ocrParam[2], map_data.ocrParam[0], map_data.ocrParam[1])
+                        if text is not None and text is not '':
+                            if int(xls_data.params) == int(text):
+                                return True, textbox, screenshot
+                        
+                        return False, textbox, screenshot
+                    else:
+                        print('ocr convert error!')
+                        return False, textbox, screenshot
+                #end-if
+            elif xls_data.event == u"单击":
+                self.feature.redRat3.sendKey(pel_data.openkey)
+                # 是否成功进入关联图区;
+            elif xls_data.event == u"返回":
+                self.feature.redRat3.sendKey(pel_data.returnkey)
+                # 是否成功返回上层图区;
+            elif xls_data.event == u"弹出":
+                self.feature.redRat3.sendKey(pel_data.popkey)
+                # 是否成功弹出某图区;
+            #end-if
+        elif pel_data.type == u"按钮":
+            if xls_data.event == u"单击":
+                self.feature.redRat3.sendKey(pel_data.openkey)
+                # 是否成功进入关联图区;
+            elif xls_data.event == u"返回":
+                self.feature.redRat3.sendKey(pel_data.returnkey)
+                # 是否成功返回上层图区;
+            elif xls_data.event == u"弹出":
+                self.feature.redRat3.sendKey(pel_data.popkey)
+                # 是否成功弹出某图区;
+        elif pel_data.type == u"编辑框":
+            pass
+        
+        # 默认返回True;
+        return True, [], screenshot
+    #end-fun
+
+    # 根据布局方式,到达目标控件;
+    def doMove2TargetCtrl(self, pel_data, map_data):
+        print pel_data.id, pel_data.name, pel_data.areaid, map_data.id, map_data.name
+        key, result, curbox, lastbox, screenshot = '', False, [], [], ''
+        if map_data.layouttype == u"网格":
+            pass
+        elif map_data.layouttype == u"纵向列表":
+            # 开始位置;用于循环列表;
+            beginbox = []
+            # 默认遍历方向;
+            key = 'down'
+            # 遍历计数;
+            count = 0
+            # 是否逆序遍历;
+            isAdverse = False
+            while True:
+                result, curbox, screenshot = self.doVerifyOnTargetCtrl(pel_data, map_data)
+                # 找到目标焦点;
+                if result is True:
+                    break
+               
+                # 正序遍历;
+                if isAdverse is False:
+                    # 当前焦点框与上次焦点框相同,认为到达边界端;
+                    if curbox == lastbox:
+                        key = 'up'#改变寻路方向;
+                        # 标记已开始逆序
+                        isAdverse = True
+                        # 根据count计数,直接返回原本位置;
+                        self.feature.redRat3.sendKey(key,count,0.2)
+                    
+                    # 循环列表完成一次遍历回到beginbox;
+                    if curbox == beginbox:
+                        break
+                else:
+                    # 已逆序,且再次到达另一边界端;
+                    if curbox == lastbox:
+                        break
+                
+                # 记录起始位置;
+                if beginbox == []:
+                    beginbox = curbox
+                
+                # 记录本次位置;
+                lastbox = curbox
+                # 下一位置;
+                self.feature.redRat3.sendKey(key)
+            #end-while
+        elif map_data.layouttype == u"横向列表":
+            # 开始位置;用于循环列表;
+            beginbox = []
+            # 默认遍历方向;
+            key = 'right'
+            # 遍历计数;
+            count = 0
+            # 是否逆序遍历;
+            isAdverse = False
+            while True:
+                result, curbox, screenshot = self.doVerifyOnTargetCtrl(pel_data, map_data)
+                # 找到目标焦点;
+                if result is True:
+                    break
+
+                # 正序遍历;
+                if isAdverse is False:
+                    # 当前焦点框与上次焦点框相同,认为到达边界端;
+                    if curbox == lastbox:
+                        key = 'left'  # 改变寻路方向;
+                        # 标记已开始逆序
+                        isAdverse = True
+                        # 根据count计数,直接返回原本位置;
+                        self.feature.redRat3.sendKey(key, count, 0.2)
+
+                    # 循环列表完成一次遍历回到beginbox;
+                    if curbox == beginbox:
+                        break
+                else:
+                    # 已逆序,且再次到达另一边界端;
+                    if curbox == lastbox:
+                        break
+
+                # 记录起始位置;
+                if beginbox == []:
+                    beginbox = curbox
+
+                # 记录本次位置;
+                lastbox = curbox
+                # 下一位置;
+                self.feature.redRat3.sendKey(key)
+            # end-while
+        elif map_data.layouttype == u"标签列表":
+            pass
+        elif map_data.layouttype == u"图独":
+            return True, [], screenshot
+        # 返回结果以及坐标;
+        return result, curbox, screenshot
+    #end-fun
+
+    # 结果比较;
+    def doResultComparison(self, row, xls_data):
+        result, desc = True, ''
+        # 解析json为字典;
+        try:
+            data = json.loads(row.params)
+        except Exception, e:
+            data = {}
+            desc = str(e)
+        if data is None or data == {}:
+            return False, u'Json参数解析出错=%s'%('数据有误' if desc == '' else desc)
+
+        # 根据描述选择方法;
+        if row.event == u"示例":
+            # [{"num":13},{"num":8}]
+            param1 = data[0]
+            param2 = data[1]
+            # 解析对应的参数;
+            for r in xls_data:
+                if r.num == param1['num']:
+                    param1["data"] = r.eventresult
+                if r.num == param2['num']:
+                    param2['data'] = r.eventresult
+            #end-for
+            print param1,param2
+        elif row.event == u"明暗对比":
+            pass
+        elif row.event == u"":
+            pass
+        elif row.event == u"":
+            pass
+        elif row.event == u"":
+            pass
+        elif row.event == u"":
+            pass
+        elif row.event == u"":
+            pass
+        elif row.event == u"":
+            pass
+        elif row.event == u"":
+            pass
+        
+        # 默认返回值;
+        return result, desc
+    #end-fun
+    
+    # 保存结果;
+    def doSaveResult(self, xls_data, save_path):
+        # 创建工作簿;
+        book = xlwt.Workbook()
+        # 创建sheet;
+        sheet = book.add_sheet(u'result', cell_overwrite_ok=True)
+        # 创建标题行;
+        row0 = [u'编号',u'类型', u'控件ID',u'描述',u'事件',u'事件参数',u'类型结果',u'事件结果']
+        for i in range(len(row0)):
+            sheet.write(0, i, row0[i], style = xlwt.XFStyle())
+        
+        index = 1
+        # 写入其他数据;
+        for row in xls_data:
+            l = row.tolist()
+            for j in range(len(l)):
+                sheet.write(index, j, l[j], style = xlwt.XFStyle())
+            index += 1
+        #end-for
+
+        # 保存xls;
+        book.save(save_path)
+    #end-fun
+
+    def run(self):
+        # 读取xls
+        self.read_excel()
+        # 打开数据库;
+        self.get_conn()
+        # 获取游标;
+        self.get_cursor()
+        
+        # 遍历excel数据;
+        for row in self.xls_data:
+            if row.type == u'控件操作':
+                # 获取图元信息;
+                pel_data = self.getImgViewData(row.ctrlid)
+                if pel_data is not None:
+                    # 获取图元图区信息;
+                    map_data = self.getImgAreaData(pel_data.areaid)
+                    print pel_data, map_data, row
+                    if map_data is not None:
+                        # 执行弹出按键;
+                        # 注:如果多个控件连续且都在同一个根图区时,多次执行会出问题,需完善这里的判断机制;
+                        if map_data.isroot == 1:
+                            if self.doVerifyOnTargetMapArea(map_data)[0] is False:
+                                self.feature.redRat3.sendKey(map_data.popkey)
+                                self.feature.redRat3.sendKey('down')
+                        # 到达目标图元;
+                        result, coodr, screenshot = self.doMove2TargetCtrl(pel_data, map_data)
+                        # 结果;
+                        row.typeresult = u'{"结果":%s, "坐标":%s, "图像":%s}'%('True' if result is True else 'False', str(coodr), screenshot)
+                        if result is True:
+                            # 执行图元事件;
+                            result, coodr, screenshot = self.doCtrlEvent(row, pel_data, map_data)
+                            row.eventresult = u'{"结果":%s, "坐标":%s, "图像":%s}'%('True' if result is True else 'False', str(coodr), screenshot)
+                        else:
+                            print("unable find target ctrl")
+                            break
+                    else:
+                        print('map data is none')
+                        break
+                else:
+                    print('pel data is none')
+                    break
+                #end-if
+            elif row.type == u'结果比较':
+                result, desc = self.doResultComparison(row, self.xls_data)
+                row.eventresult = u'{"结果":%s, "描述":%s}'%('True' if result is True else 'False', desc)
+            elif row.type == u"截屏":
+                # 默认截屏一张;
+                screenshot = os.path.join(LoggingUtil.getCaseRunLogDirPath(), "TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+                self.feature.vp.takePicture(screenshot)
+                row.typeresult = screenshot
+        #end-for
+        # 保存结果;
+        self.doSaveResult(self.xls_data, r'D:\result.xls')
+    #end-fun
+
+if __name__ == "__main__":
+    executor = CExecutor(r'F:\scbc-sat\command.xlsx', r'F:\scbc-sat\ui.db')
+    executor.run()

+ 60 - 0
ssat_sdk/atv_channel_table.py

@@ -0,0 +1,60 @@
+# -*- coding: UTF-8 -*-
+from utils.serial_TG39BX1 import *
+
+
+class AtvChannelTable():
+    """
+    VHF/UHF/CATV Channel Table
+    """
+    def __init__(self, port):
+        self.tg39ser = Serial_TG39BX1(port)
+        self.tg39ser.open()
+        # self.tg39ser.execmd("VCOL 1")
+        # self.tg39ser.execmd("VSIGCB 1")
+        # self.tg39ser.execmd("SUSA")
+        # self.tg39ser.execmd("SCAR 1")
+        # self.tg39ser.execmd("SMODE 2")
+        # self.tg39ser.execmd("SCH1 1")
+        # self.tg39ser.execmd("RSYS 0")
+
+        # 使用TG39BX1预制的各国的频道
+
+    def setCTYandCHN(self, countryID, channelID):
+        """
+        RFCHA-arg1      For arg1, input the country name ID to set (as a decimal number).
+        RFCHB-arg2      For arg2, input the channel ID to set (as a decimal number).
+        countryID       channelID
+        1 JAPAN         1 to 62
+        2 USA           2 to 83
+        3 EU            1 to 69
+        4 CHINA         1 to 68
+        5 UK            21 to 69
+        6 R             1 to 69
+        7 FRANCE        1 to 69
+        8 AUSTRALIA     1 to 69
+        9 JAPAN-C       1 to 63
+        10 USA-C        2 to 150
+        11 EU-C         2 to 56
+        12 CHINA-C      1 to 51
+        13 ITY          1 to 69
+        14 IRE          1 to 69
+        """
+        self.tg39ser.execmd("RFCHA" + " " + str(countryID))
+        self.tg39ser.execmd("RFCHB" + " " + str(channelID))
+
+
+    def close(self):
+        self.tg39ser.close()
+
+    def __del__(self):
+        self.tg39ser.close()
+
+
+
+#  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
+if __name__ == "__main__":
+    print "ATV USA Channel Table"
+    atvChannelTable = AtvChannelTable("COM6")
+    atvChannelTable.setCTYandCHN(2, 81)
+
+

+ 51 - 0
ssat_sdk/atv_player.py

@@ -0,0 +1,51 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+
+from ssat_sdk.device_manage.tg39_manager import *
+'''
+ATV播放器,汇总各种设备进行ATV信号输出
+'''
+class ATVPlayer():
+    def __init__(self):
+        self.tg39Mgr = TG39Manager()
+
+    def setCTYandCHN(self, countryID, channelID):
+        """
+                RFCHA-arg1      For arg1, input the country name ID to set (as a decimal number).
+                RFCHB-arg2      For arg2, input the channel ID to set (as a decimal number).
+                countryID       channelID
+                1 JAPAN         1 to 62
+                2 USA           2 to 83
+                3 EU            1 to 69
+                4 CHINA         1 to 68
+                5 UK            21 to 69
+                6 R             1 to 69
+                7 FRANCE        1 to 69
+                8 AUSTRALIA     1 to 69
+                9 JAPAN-C       1 to 63
+                10 USA-C        2 to 150
+                11 EU-C         2 to 56
+                12 CHINA-C      1 to 51
+                13 ITY          1 to 69
+                14 IRE          1 to 69
+                """
+        self.tg39Mgr.setCTYandCHN(countryID, channelID)
+
+    '''
+    freq: float, value:MHz
+    color_system:string,value:("NTSC-STD","NTSC-50","NTSC-443","PAL-STD","PAL-60","PAL-M","PAL-N","SECAM")
+    tv_system:string,value:(MN,BG,DK,I,L,L')
+    level:float, value:(29.0 to 109.0) (dBu V/75欧)
+    '''
+    def setChannel(self, freq, color_system, tv_system, level):
+        self.tg39Mgr.setChannel(freq, color_system, tv_system, level)
+
+    '''
+    command:string, value:RS-232C command
+    param:no type, value:Content
+    '''
+    def setTG39CMD(self, command, param):
+        self.tg39Mgr.setTG39CMD(command, param)
+
+    def sendCmd(self, cmdLine):
+        self.tg39Mgr.sendCmd(cmdLine)

+ 96 - 0
ssat_sdk/baseSerial.py

@@ -0,0 +1,96 @@
+# -*- coding: UTF-8 -*-
+import serial
+import time
+import binascii
+
+'''
+描述:串口同步基类
+'''
+class baseSerial():
+    # 构造函数
+    def __init__(self):
+        self.exception = None
+        self.ser = serial.Serial()
+
+    # 析构函数(停用);
+    # def __del__(self):
+    #     self.close()
+
+    def open(self, port, baudrate = 115200, bytesize = 8, parity = serial.PARITY_NONE, stopbits = 1, timeout = 2, writeTimeout = 0.5):
+        if type(port) == type(0):
+            self.ser.port = "COM"+str(port)
+        else:
+            self.ser.port = port
+        # 波特率;
+        self.ser.baudrate = baudrate
+        # 数据位;
+        self.ser.bytesize = bytesize
+        # 校验码;
+        self.ser.parity = parity # PARITY_NONE=0
+        # 停止位;
+        self.ser.stopbits = stopbits
+        # 串口等待超时值(Read)
+        self.ser.timeout = timeout  # 读超时;
+        # 串口写超时值(Read)
+        self.ser.writeTimeout = writeTimeout  # 写超时;
+        try:
+            self.ser.open()
+            # 返回结果;
+            return self.ser.is_open
+        except Exception,e:
+            self.exception = e
+            print '打开串口异常',str(e)
+            return False
+    
+    # 重新打开;
+    def reOpen(self):
+        self.exception = None
+        self.ser.close()
+        try:
+            self.ser.open()
+            # 返回结果;
+            return self.ser.is_open
+        except Exception,e:
+            self.exception = e
+            return False
+    
+    def close(self):
+        self.ser.close()
+
+    '''
+    描述:写串口
+    '''
+    def write(self, cmd):
+        try:
+            self.exception = None
+            writelen = self.ser.write(cmd)
+            # flush等待写入完成;
+            self.ser.flush()
+            print "writehex:" + binascii.hexlify(cmd) + " writelen=" + str(writelen)
+            return True if writelen == len(cmd) else False
+        except Exception,e:
+            self.exception = e
+            print '写串口异常',str(e)
+            return False
+
+    '''
+    描述:读取串口数据;
+    返回:返回16进制字符串;
+    补充:ser.in_waiting 实际是缓冲区里的数据长度,也就是要读的长度;
+    '''
+    def read(self):
+        try:
+            self.exception = None
+            if self.ser.in_waiting == 0:
+                time.sleep(self.ser.timeout)
+            bys = self.ser.read(self.ser.in_waiting)
+            print "readhex:" + binascii.hexlify(bys) + " readlen=" + str(bys.__len__())
+            return bys
+        except Exception,e:
+            self.exception = e
+            print '读串口异常',str(e)
+            return None    
+
+if __name__ == "__main__":
+    pass
+    

+ 131 - 0
ssat_sdk/chroma22293_pattern.py

@@ -0,0 +1,131 @@
+#-*- coding:utf-8 -*-
+import sys
+reload(sys)
+sys.setdefaultencoding('utf-8')
+import cv2
+import numpy as np
+
+class PatternChecker():
+    def __init__(self):
+        print "Init Pattern Checker"
+
+    def getGraySepLine(self, img):
+        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
+        edges = cv2.Canny(gray, 200, 300, apertureSize=5)
+        lines = cv2.HoughLinesP(edges,1,np.pi/180,100,minLineLength=500,maxLineGap=10)
+        destLines = []
+        for line in lines:
+            #print line #LINE:[[startX,startY,endX,endY]]
+            if (line[0][0] == line[0][2]):
+                destLines.append(line)
+        return destLines
+
+    def calcuateSep(self, img):
+        lines = self.getGraySepLine(img)
+        sepXList = []
+        for line in lines:
+            flag = False
+            X = line[0][0]
+            for sepX in sepXList:
+                if(sepX.__len__() > 0) and (abs(sepX[0] - X) < 20):
+                    sepX.append(X)
+                    flag = True
+            if flag == False:
+                newSepX = []
+                newSepX.append(X)
+                sepXList.append(newSepX)
+        #print sepXList
+        return sepXList
+
+    """
+    检测16灰阶图片是否能够分清楚
+    """
+    def check16GrayLevel(self, picPath):
+        img_src = cv2.imread(picPath)
+        height, width,  type = img_src.shape
+        print img_src.shape
+        half_height = height/2
+        img_halfup = img_src[0:half_height, 0:width]
+        XList = self.calcuateSep(img_halfup)
+        if (XList.__len__() == 15):
+            return "Pass"
+        else:
+            return "Fail"
+
+    def getSingleSortList(self, xList):
+        increList = []
+        for XItemList in xList:
+            x_total = 0
+            for x in XItemList:
+                x_total += x
+            x_avg = x_total/XItemList.__len__()
+            increList.append(x_avg)
+        print "increList:",increList
+        if increList.__len__() < 2:
+            return increList
+        for index in range(1, increList.__len__()):
+            for sindex in range(index, increList.__len__()):
+                if (increList[index - 1] > increList[sindex]):
+                    tmp = increList[index - 1]
+                    increList[index - 1] = increList[sindex]
+                    increList[sindex] = tmp
+        print "sorted increList:", increList
+        return increList
+
+    def getColorSTDList(self, img, xList, y_base):
+        increXList = self.getSingleSortList(xList)
+        img_width = img.shape[1]
+        totalXList = [0] + increXList + [img_width]
+        colorList = []
+        for index in range(1, totalXList.__len__()):
+            x_avg = totalXList[index - 1] + (totalXList[index] - totalXList[index - 1])/2
+            colorList.append(img[y_base, x_avg])
+        return colorList, totalXList
+    """
+    检测彩条图像是否有串色
+    """
+    def checkColorLevel(self, picPath, stdBide=50):
+        img_src = cv2.imread(picPath)
+        XList = self.calcuateSep(img_src)
+        y_base = 500
+        stdColorList,totalXList = self.getColorSTDList(img_src, XList, y_base)
+        BGR_list = []
+        diffBideList = []
+        for index in range(stdColorList.__len__() - 1):
+            startX = -1
+            for x in range(totalXList[index] + (totalXList[index+1] - totalXList[index])/2, totalXList[index+1]):
+                startX = x
+                if (self.cmp2Point(img_src[y_base, x], stdColorList[index] )== False):
+                    break
+            print "startX=", startX
+            endX = -1
+            for i in range((totalXList[index + 2] - totalXList[index + 1])/2):
+                x = totalXList[index + 2] - (totalXList[index + 2] - totalXList[index + 1])/2 - i
+                endX = x
+                if (self.cmp2Point(img_src[y_base,x], stdColorList[index+1]) ==False):
+                    break
+            print "endX=", endX
+            diffBideList.append(endX - startX)
+
+        print diffBideList
+        for bide in diffBideList:
+            if (bide > stdBide):
+                return "Fail"
+        return "Pass"
+
+    def cmp2Point(self, point1BGR, point2BGR):
+        if (abs(point1BGR[0]&0xFF - point2BGR[0]&0xFF) > 10) \
+            or (abs(point1BGR[1]&0xFF - point2BGR[1]&0xFF) > 10) \
+            or (abs(point1BGR[2]&0xFF - point2BGR[2]&0xFF) > 10):
+            return False
+        else:
+            return True
+
+if __name__ == "__main__":
+    DIR = r"D:/TST/22293-src/"
+    #picPath = DIR + "hdmi_std_t66_p47.jpg"
+    patChecker = PatternChecker()
+    #patChecker.check16GrayLevel(picPath)
+
+    color_path = DIR + "color.jpg"
+    print patChecker.checkColorLevel(color_path)

+ 0 - 0
ssat_sdk/client/__init__.py


+ 153 - 0
ssat_sdk/client/ota_client.py

@@ -0,0 +1,153 @@
+#-*- coding:utf-8 -*-
+
+from ssat_sdk.utils.LoggingUtil import printLog
+from ssat_sdk.device_manage import ScbcCopyKey
+from ssat_sdk.sat_environment import getOtaParamUrl_dict
+import urllib
+import urllib2
+import json
+import requests
+
+
+
+# HTTP协议传参时的自动化测试标识,string型,每次请求必带的参数,KEY为type
+SAT_TYPE = "1"
+
+
+
+
+class OtaClient():
+    def __init__(self):
+        # 各服务器getParam接口
+        self.getParamDict = getOtaParamUrl_dict()
+
+        # self.getParamDict = {
+        #     "CN": "https://cn.ota.qhmoka.com/ota-services/strategy/getParam",
+        #     "NA": "https://na.ota.qhmoka.com/ota-services/strategy/getParam",
+        #     "LA": "https://la.ota.qhmoka.com/ota-services/strategy/getParam",
+        #     "ME": "https://me.ota.qhmoka.com/ota-services/strategy/getParam",
+        #     "EU": "https://eu.ota.qhmoka.com/ota-services/strategy/getParam",
+        #     "AP": "https://ap.ota.qhmoka.com/ota-services/strategy/getParam",
+        #     "TEST": "https://test.uc.qhmoka.com/ota-services/strategy/getParam"
+        #     }
+
+        # http request中统一使用的headers
+        self.headers = {
+            "Content-Type": "application/json"
+        }
+
+    def postRequest(self, url, post_data, headers):
+        response = requests.post(url, data=post_data, headers=headers)
+        printLog("response:%s" % response)
+        printLog("response.status_code:%s" % response.status_code)
+        printLog("response.text:%s" % response.text)
+        if response.status_code != 200:
+            printLog("OtaClient.postRequest()", u"Response结果码异常。")
+            return False, response
+        else:
+            return True, response
+
+
+    def getAppKey(self, area):
+        appKeys = []
+        url = ""
+        if 1:
+            try:
+                url = self.getParamDict[area]
+            except Exception, e:
+                printLog("OtaClient.addUserId()", u"无法通过传入的area获取url地址。%s"%e)
+                return False
+        # 测试服务器地址
+        if 0:
+            url = "https://test.uc.qhmoka.com/ota-services/strategy/getParam"
+
+        post_dict = {
+            "type": SAT_TYPE,
+            "area": area
+        }
+
+        post_data = json.dumps(post_dict)
+        result, response = self.postRequest(url, post_data, self.headers)
+        if result is False:
+            return False, appKeys
+
+        resDict = json.loads(response.text)
+        if int(resDict['code']) != 1000:
+            result = False
+        else:
+            result = True
+            appKeys = resDict['data']['appKeys']
+        printLog("OtaClient.getAppKey()", u"请求返回信息:%s" % resDict['msg'])
+        return result, appKeys
+
+
+    def httpAddUserId(self, area, appKey, clientType, projectId, checkVersion, version, userId):
+        url = ""
+        if 1:
+            try:
+                url = self.getParamDict[area]
+            except Exception, e:
+                printLog("OtaClient.addUserId()", u"无法通过传入的area获取url地址。%s"%e)
+                return False
+        # 测试服务器地址
+        if 0:
+            url = "https://test.uc.qhmoka.com/ota-services/strategy/getParam"
+
+        post_dict = {
+            "type": SAT_TYPE,
+            "area": area,
+            "appKey": appKey,
+            "clientType": clientType,
+            "projectId": projectId,
+            "checkVersion": checkVersion,
+            "version": version,
+            "userId": userId
+        }
+
+        post_data = json.dumps(post_dict)
+        result, response = self.postRequest(url, post_data, self.headers)
+        if result is False:
+            return False
+
+        resDict = json.loads(response.text)
+        if int(resDict['code']) != 1000:
+            result = False
+        else:
+            result = True
+        printLog("OtaClient.addUserId()", u"请求返回信息:%s" % resDict['msg'])
+        return result
+
+    def httpGetUserId(self, deviceId, clientType, mac, forFormal=True):
+        # 测试环境:
+        testRoot = "http://test.dsp.server.qhmoka.com"
+        # 正式环境:
+        formalRoot = "https://dsp.server.qhmoka.com"
+        # 登录的账号密码
+        userName = "auto"
+        password = "123456"
+        if forFormal is True:
+            loginUrl = formalRoot + "/login"
+        else:
+            loginUrl = testRoot + "/login"
+        token = ScbcCopyKey.HTTPLogin(loginUrl, userName, password, 1)
+
+        # token长度不为0, 表示成功获取token值;
+        if token.__len__():
+            print u"login token = ", token
+            userId = ScbcCopyKey.HTTPGetUserId("http://test.dsp.server.qhmoka.com/api/automation/getUserId", token,
+                                               clientType, deviceId, mac.replace('-', ':'))
+            # userId == -1表示获取失败;
+            return userId
+
+    def getDeviceId(self):
+        return ScbcCopyKey.GetDeviceId()
+
+    def getClientType(self):
+        return ScbcCopyKey.GetClientType()
+
+    def getMAC(self):
+        return ScbcCopyKey.GetMAC()
+
+
+if __name__ == '__main__':
+    pass

+ 2 - 0
ssat_sdk/config/__init__.py

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

+ 113 - 0
ssat_sdk/config/all-result.xsl

@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" 
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+  <xsl:template match="/">
+    <html>
+      <body>
+        <h2>软件信息清单</h2>
+        <table border="1">
+          <tr>
+            <th>机芯:</th>
+            <th>
+              <xsl:value-of select="all/SW_Information/hardware"/>
+            </th>
+          </tr>
+          <tr>
+            <th>软件版本:</th>
+            <th>
+              <xsl:value-of select="all/SW_Information/SWVersion"/>
+            </th>
+          </tr>
+          <tr>
+            <th>机型:</th>
+            <th>
+              <xsl:value-of select="all/SW_Information/projectName"/>
+            </th>
+          </tr>
+          <tr>
+            <th>ProjectID:</th>
+            <th>
+              <xsl:value-of select="all/SW_Information/projectID"/>
+            </th>
+          </tr>
+          <tr>
+            <th>软件综合信息:</th>
+            <th>
+              <xsl:value-of select="all/SW_Information/SWInclude"/>
+            </th>
+          </tr>
+        </table>
+        <h2>测试结果汇总表</h2>
+        <xsl:for-each select="results/item">
+          <xsl:sort select="result"/>
+          <table border="1" table-layout="fixed" word-wrap="break-word">
+            <tr bgcolor="#9acd32">
+              <th>子项名称</th>
+              <th>子项结果</th>
+              <th>结果数据</th>
+              <th>日志路径</th>
+              <th>备注</th>
+            </tr>
+            <tr>
+              <td>
+                <xsl:value-of select="name"/>
+              </td>
+              <xsl:choose>
+                <xsl:when test="result = 'Fail'">
+                  <td bgcolor="#ff00ff">
+                    <xsl:value-of select="result"/>
+                  </td>
+                </xsl:when>
+                <xsl:otherwise>
+                  <td>
+                    <xsl:value-of select="result"/>
+                  </td>
+                </xsl:otherwise>
+              </xsl:choose>
+              <td>
+                <xsl:value-of select="data"/>
+              </td>
+              <td>
+                <xsl:variable name="loghref" select="log" />
+                <a href="{$loghref}">
+                  <xsl:value-of select="log" />
+                </a>
+              </td>
+              <td>
+                <xsl:value-of select="remark"/>
+              </td>
+            </tr>
+
+            <xsl:variable name="cols" select="4" />
+            <xsl:variable name="index" select="0" />
+            <table border="1" table-layout="fixed" word-wrap="break-word">
+              <th bgcolor="#9acd32">截图路径</th>
+              <xsl:for-each select="screen[ (position() -1) mod $cols = 0]">
+                <tr>
+                  <xsl:if test="position() = 1">
+                    <td>
+                      <xsl:variable name="screenhref" select="." />
+                      <a href="{$screenhref}">
+                        <xsl:value-of select="." />
+                      </a>
+                    </td>
+                  </xsl:if>
+                  <xsl:for-each select="following-sibling::screen[(position() - 1) &lt; $cols]">
+                    <td>
+                      <xsl:variable name="screenhref" select="." />
+                      <a href="{$screenhref}">
+                        <xsl:value-of select="." />
+                      </a>
+                    </td>
+                  </xsl:for-each>
+                </tr>
+              </xsl:for-each>
+            </table>
+            <tr border="1" bgcolor="#ff00ff"></tr>
+          </table>
+        </xsl:for-each>
+      </body>
+    </html>
+  </xsl:template>
+</xsl:stylesheet>
+

+ 9 - 0
ssat_sdk/config/baidu.cfg

@@ -0,0 +1,9 @@
+[CurStatus]
+Cur_Count = count_f142482
+Reset_Time = 12
+
+[count_f142482]
+APP_ID = f1424820a4344cc28b3d0bcaab54dd5a
+API_KEY = ae11f920308441ea81ae9041b518178e
+SECRET_KEY = 88a43a2bb42f4f4d99e2435fb6054d31
+Ret_Count = 0

+ 114 - 0
ssat_sdk/config/baidu_config.py

@@ -0,0 +1,114 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+import shutil
+
+from ssat_sdk.utils import Environment
+from ConfigParser import *
+import thread
+
+try:
+    Python_Home = sys.prefix
+    # Server_CFG_Path = Python_Home + Environment.SDK_Relative_Path + u"config/server.cfg"
+    Server_CFG_Path = Python_Home + Environment.SDK_Relative_Path + u"config/baidu_run.cfg"
+    Server_CFG_Local_Path = Python_Home + Environment.SDK_Relative_Path + u"config/baidu.cfg"
+    # Server_CFG_Path = "../config/server.cfg"
+    Home_Init = True
+except KeyError, e:
+    print "KeyError:", e.message
+
+class BaiduConfig():
+    ServiceModel = "service"
+
+    def __init__(self):
+        if not os.path.exists(Server_CFG_Path):
+            print 'server_run.cfg文件不存在,复制server.cfg文件'
+            shutil.copy(Server_CFG_Local_Path, Server_CFG_Path)
+        print u"Server配置文件:", Server_CFG_Path
+        self.CFGParser = ConfigParser()
+        self.CFGParser.read(Server_CFG_Path)
+
+    def writeCFG(self):
+        self.CFGParser.write(open(Server_CFG_Path, "w"))
+
+    def setBaiduCount(self,count):
+        self.CFGParser.set("CurStatus", "Cur_Count", count)
+        self.writeCFG()
+
+    def getBaiduCount(self):
+        return self.CFGParser.get("CurStatus", "Cur_Count")
+
+    def getAppID(self):
+        section = self.getBaiduCount()
+        return self.CFGParser.get(section, "APP_ID")
+    def getAPIKey(self):
+        section = self.getBaiduCount()
+        return self.CFGParser.get(section, "API_KEY")
+    def getSecretKey(self):
+        section = self.getBaiduCount()
+        return self.CFGParser.get(section, "SECRET_KEY")
+
+    def getRetCount(self):
+        retCount = self.resetTime()
+        if retCount is not None:
+            return retCount
+        section = self.getBaiduCount()
+        retCount = self.CFGParser.getint(section, "ret_count")
+        return retCount
+
+    def resetTime(self):
+        prevTime = self.CFGParser.getfloat("CurStatus", "reset_time")
+        if prevTime == 0:
+            timeLong = time.time()
+            self.CFGParser.set("CurStatus", "reset_time", timeLong)
+            self.writeCFG()
+        prevGMT = time.gmtime(prevTime)
+        prevYear = prevGMT.tm_year
+        prevMonth = prevGMT.tm_mon
+        prevDay = prevGMT.tm_mday
+        curTime = time.time()
+        curGMT = time.gmtime(curTime)
+        curYear = curGMT.tm_year
+        curMonth = curGMT.tm_mon
+        curDay = curGMT.tm_mday
+        if prevYear <> curYear or prevMonth <> curMonth or prevDay <> curDay:
+            print "resetTime,prevGMT:",prevGMT
+            print "resetTime,curGMT:",curGMT
+            self.CFGParser.set("CurStatus", "reset_time", curTime)
+            self.resetRetCount()
+            return 500
+
+    def resetRetCount(self):
+        countList = self.getBaiduList()
+        for section in countList:
+            self.CFGParser.set(section, "ret_count", 500)
+        self.writeCFG()
+
+    def subRetCount(self, count):
+        section = self.getBaiduCount()
+        retCount = self.getRetCount()
+        retCount = retCount - count
+        self.CFGParser.set(section, "ret_count", retCount)
+        self.writeCFG()
+
+    def getBaiduList(self):
+        sections = self.CFGParser.sections()
+        countList = []
+        for section in sections:
+            if section.__contains__("count_"):
+                countList.append(section)
+        return countList
+
+def testCFG():
+    print "testCFG:"
+    cfg = BaiduConfig()
+    # print "getBaiduCount:", cfg.getBaiduCount()
+    print "getRetCount:", cfg.getRetCount()
+
+    # print cfg.getBaiduList()
+
+if __name__ == "__main__":
+    thread.start_new_thread(testCFG,())
+    # thread.start_new_thread(testCFG,())
+    # thread.start_new_thread(testCFG,())
+
+    time.sleep(3)

+ 81 - 0
ssat_sdk/config/case_result_detail.xsl

@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" 
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+  <xsl:template match="/">
+    <html>
+      <h2>Case详细步骤结果</h2>
+      <body>
+        <div>
+          <xsl:for-each select="results/item">
+            <xsl:sort select="result"/>
+            <table border="1" table-layout="fixed" word-wrap="break-word">
+              <tr bgcolor="#9acd32">
+                <th>子项名称</th>
+                <th>子项结果</th>
+                <th>结果数据</th>
+                <th>日志路径</th>
+                <th>备注</th>
+              </tr>
+              <tr>
+                <td>
+                  <xsl:value-of select="name"/>
+                </td>
+                <xsl:choose>
+                  <xsl:when test="result = 'Fail'">
+                    <td bgcolor="#ff00ff">
+                      <xsl:value-of select="result"/>
+                    </td>
+                  </xsl:when>
+                  <xsl:otherwise>
+                    <td>
+                      <xsl:value-of select="result"/>
+                    </td>
+                  </xsl:otherwise>
+                </xsl:choose>
+                <td>
+                  <xsl:value-of select="data"/>
+                </td>
+                <td>
+                  <xsl:variable name="loghref" select="log" />
+                  <a href="{$loghref}">
+                    <xsl:value-of select="log" />
+                  </a>
+                </td>
+                <td>
+                  <xsl:value-of select="remark"/>
+                </td>
+              </tr>
+
+              <xsl:variable name="cols" select="4" />
+              <xsl:variable name="index" select="0" />
+              <table border="1" table-layout="fixed" word-wrap="break-word">
+                <th bgcolor="#9acd32">截图路径</th>
+                <xsl:for-each select="screen[ (position() -1) mod $cols = 0]">
+                  <tr>
+                    <xsl:if test="position() = 1">
+                      <td>
+                        <xsl:variable name="screenhref" select="." />
+                        <a href="{$screenhref}">
+                          <xsl:value-of select="." />
+                        </a>
+                      </td>
+                    </xsl:if>
+                    <xsl:for-each select="following-sibling::screen[(position() - 1) &lt; $cols]">
+                      <td>
+                        <xsl:variable name="screenhref" select="." />
+                        <a href="{$screenhref}">
+                          <xsl:value-of select="." />
+                        </a>
+                      </td>
+                    </xsl:for-each>
+                  </tr>
+                </xsl:for-each>
+              </table>
+              <tr border="1" bgcolor="#ff00ff"></tr>
+            </table>
+          </xsl:for-each>
+        </div>
+      </body>
+    </html>
+  </xsl:template>
+</xsl:stylesheet>

+ 44 - 0
ssat_sdk/config/resource.cfg

@@ -0,0 +1,44 @@
+[COMM]
+env_ocr_ip_addr = 10.118.158.198
+env_ocr_port = 3000
+serial_communicator_port = COM4
+tg39_port = COM23
+sat_home = D:/SAT/
+sat_result_dir = D:/SAT/results/
+chroma22293 = {"default":"Device1","devices":[{"name":"Device1","port":"COM8"},{"name":"Device2","port":"COM5"},{"name":"Device3","port":"COM10"}]}
+runner_tcp_port = 9958
+tester_listen_tcp_port = 20000
+isrestart = False
+issendkeytakepicture = False
+
+[devices]
+rcudevice_list = ['redrat3','redrat4']
+rcudevice_selected = redrat4
+rcu_selected = RC311
+atv_list = ['TG39','54200']
+atv_selected = TG39
+dtv_list = ['DTA2115', 'DTU215', 'DTA115', 'JH5500']
+dtv_selected = Dektec
+ccard_list = ['CD750','TC-UB530','TC-UB530EX']
+ccard_selected = TC-UB530EX
+voicecard_list = ['default']
+voice_selected = default
+signalcard_list = ['Chroma22293/4']
+signalcard_select = Chroma22293/4
+serialport_list = ['Mstar']
+serialport_select = Mstar
+voicecards = {"default":"pickup","devices":[{"name":"pickup","type":0},{"name":"USB","type":1}]}
+
+[baidu_ocr]
+app_id = 11704335
+api_key = Kb5sasEAbWGXc9CcvsIdmnIA
+secret_key = vIdDB83xAh0q4zp5DmNWM9yLQhPo9ykZ
+
+[MenuTree]
+menutreeselectedchip = MS3663
+menutreeselectedstyle = colorfuB_UI
+menutreeselectedchannel = 
+
+[Sound]
+sound_list = [1000, 1000]
+

+ 4 - 0
ssat_sdk/config/server.cfg

@@ -0,0 +1,4 @@
+[service]
+ccard_port = 30000
+ub530_port =30300
+c22293_port = 30100

+ 0 - 0
ssat_sdk/core/__init__.py


+ 24 - 0
ssat_sdk/core/ibinder_listener.py

@@ -0,0 +1,24 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+
+from multiprocessing.connection import Listener,Client
+
+class ListenerBinder():
+    def __init__(self, address="localhost", port=30000):
+        self.address = address
+        self.port = port
+        self.listener = None
+        self.createListener()
+
+    def createListener(self):
+        tryMax = 10
+        for i in range(tryMax):
+            try:
+                self.listener = Listener((self.address, self.port), authkey="sat")
+
+                break
+            except Exception, e:
+                print u"构建Listener失败:", self.address, self.port
+                self.port = self.port + 20
+
+        return self.listener, self.port

+ 14 - 0
ssat_sdk/core/ibinder_pipe.py

@@ -0,0 +1,14 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+
+from multiprocessing.connection import Pipe
+
+class NamedPipe():
+    def __init__(self, filePath):
+        self.filePath = filePath
+
+
+
+if __name__ == "__main__":
+    fpipe = os.mkfifo("namedpipe")
+    print fpipe

+ 125 - 0
ssat_sdk/core/process_sync.py

@@ -0,0 +1,125 @@
+# -*- coding:utf-8 -*-
+import sys,os,time
+reload(sys)
+sys.setdefaultencoding("utf-8")
+from multiprocessing import Process, Queue, Pipe
+from multiprocessing.connection import  Listener,Client
+import json
+
+class ProcessSync():
+    HEAD = "[ProcessCMD]"
+    def __init__(self):
+        pass
+
+    def notifyMessage(self, msg):
+        sendMsg = ProcessSync.HEAD + msg
+        print sendMsg
+
+class QueueBinder():
+    def __init__(self, sender, receiver):
+        self.sender = sender
+        self.receiver = receiver
+
+    def setQueue(self, sendQue, recvQue):
+        self.sender = sendQue
+        self.receiver = recvQue
+
+    """
+    参数:action:字符串
+          data:字典,存放基本数据类型
+    """
+    def sendData(self, action, data):
+        packet = [action,data]
+        self.sender.put(packet)
+
+    """
+    返回值:数组[action, data]
+            data:字典,存放基本数据类型
+    """
+    def accept(self):
+        return self.receiver.get()
+
+queBinder = None
+def getQueueBinder(sendQue, recvQue):
+    global queBinder
+    if (queBinder == None):
+        queBinder = QueueBinder(sendQue, recvQue)
+    else:
+        queBinder.setQueue(sendQue,recvQue)
+    return queBinder
+
+def testP1():
+    binder = HttpConsoleBinder()
+    binder.initConsoleListener()
+    print "Wait http"
+    action,data = binder.acceptHttp()
+    print "testP1,action:",action,";data:",data
+    print "Send to http"
+    binder.sendToHttp("test", {"keyP1": "valueP1"})
+
+def testP2():
+    binder = HttpConsoleBinder()
+    binder.initHttpListener()
+    print "Send to console"
+    binder.sendToConsole("test", {"keyP2":"valueP2"})
+    print "Wait console"
+    action,data = binder.acceptConsole()
+    print "testP2,action:",action,";data:",data
+
+class HttpConsoleBinder():
+    HttpServer_Port = 6000
+    Console_Port = HttpServer_Port + 1
+    def __init__(self):
+        self.httpAddress = ("localhost", HttpConsoleBinder.HttpServer_Port)
+        self.consoleAddress = ("localhost", HttpConsoleBinder.Console_Port)
+
+    def initHttpListener(self):
+        self.httpListener = Listener(self.httpAddress, authkey="password")
+
+    def initConsoleListener(self):
+        self.consoleListener = Listener(self.consoleAddress, authkey="password")
+
+    def sendToHttp(self,action, data):
+        self.httpClient = Client(self.httpAddress, authkey="password")
+        data["action"] = action
+        jsonStr = json.dumps(data)
+        try:
+            self.httpClient.send(jsonStr)
+        except Exception,e:
+            print e
+        finally:
+            self.httpClient.close()
+
+    def sendToConsole(self, action, data):
+        self.consoleClient = Client(self.consoleAddress, authkey="password")
+        data["action"] = action
+        jsonStr = json.dumps(data)
+        try:
+            self.consoleClient.send(jsonStr)
+        except Exception,e:
+            print e
+        finally:
+            self.consoleClient.close()
+
+    def acceptHttp(self):
+        conn = self.consoleListener.accept()
+        data = conn.recv()
+        dataDict = json.loads(data)
+        action = dataDict["action"]
+        conn.close()
+        return action,dataDict
+
+    def acceptConsole(self):
+        conn = self.httpListener.accept()
+        data = conn.recv()
+        dataDict = json.loads(data)
+        action = dataDict["action"]
+        conn.close()
+        return action,dataDict
+
+if __name__ == "__main__":
+    p1 = Process(target=testP1, args=())
+    p1.start()
+    time.sleep(3)
+    p2 = Process(target=testP2, args=())
+    p2.start()

+ 64 - 0
ssat_sdk/debug_serial.py

@@ -0,0 +1,64 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+import thread
+
+'''
+工程师调试的串口客户端
+
+
+'''
+from ssat_sdk.utils.serialboard_manager import TCLSerial
+
+
+class DebugSerial():
+    def __init__(self):
+        self.serialTool = TCLSerial()
+        self.showLogTag = True
+
+    def __del__(self):
+        self.serialTool.close()
+
+    '''
+    在电视端执行串口命令
+    :param command:指令
+    :return 无
+    '''
+    def excuteCmd(self, command):
+        self.serialTool.sendCommand(command)
+        ret = self.serialTool.readData(1024)
+        print "excuteCmd, return:",ret
+
+    '''
+    另起一个线程,在终端显示日志。
+    '''
+    def showLogThread(self):
+        thread.start_new_thread(self.showLog,())
+    def showLog(self):
+        self.showLogTag = True
+        while self.showLogTag is True:
+            ret = self.serialTool.readLine()
+            if ret.__len__() > 0:
+                print ret
+
+    '''
+    关闭日志,无论终端显示,或者保存到文件
+    '''
+    def closeLog(self):
+        self.showLogTag = False
+
+    '''
+    另起一个线程,保存日志到指定文件
+    :param filePath: 日志文件路径
+    '''
+    def saveLogToFileThread(self, filePath):
+        thread.start_new_thread(self.saveLogToFile,(filePath,))
+    def saveLogToFile(self, filePath):
+        with open(filePath, "w") as logFile:
+            while self.showLogTag is True:
+                retLine = self.serialTool.readLine()
+                logFile.write(retLine)
+                logFile.flush()
+            logFile.close()
+if __name__ == "__main__":
+    serial = DebugSerial()
+    serial.showLogThread()

+ 158 - 0
ssat_sdk/device_manage/Chroma22293.py

@@ -0,0 +1,158 @@
+# -*- coding:utf-8 -*-
+import os
+import sys
+import time
+import json
+from baseClient import BaseClient
+import numpy as np
+
+'''
+    注意:
+        所有函数中,
+        
+        1、参数device为整型,起始值从1开始,表示第几台设备;
+        device默认为1,如果要使用第2台设备,device=2;
+
+        2、参数timeout单位:毫秒;
+'''
+class C22293Manager(BaseClient):
+    def __init__(self):
+        BaseClient.__init__(self)  # python 继承方式  
+        self.device_name = "Chroma22293" 
+
+    def __parse__(self, result, params = 1):
+        if result is None:
+            return False
+        
+        # print result
+        if params == 1:
+            if result['device_cmd_result'] == u'ok;\r\n':
+                return True
+        
+        if params == 2:
+            if result['device_cmd_result'] == u'ok;\r\nok;\r\n':
+                return True
+        
+        return False
+
+    def setPattern(self, pattern, device = 1, timeout = 300):
+        self.device_id = device
+        self.device_timeout = timeout
+        self.device_cmd = 'run ptn %s;\r' % pattern
+        return self.__parse__(self.sendmsg())
+
+    def setTiming(self, timing, device = 1, timeout = 300):
+        self.device_id = device
+        self.device_timeout = timeout
+        self.device_cmd = 'run tim %s;\r' % timing
+        return self.__parse__(self.sendmsg())
+
+    def setTimingPattern(self, timing, pattern, device = 1, timeout = 300):
+        self.device_id = device
+        self.device_timeout = timeout
+        # self.device_cmd = 'run tim %s;run ptn %s;\r' % (timing, pattern)
+        # return self.__parse__(self.sendmsg(), 2)        
+        if self.setTiming(timing) and self.setPattern(pattern):
+            return True
+        
+        return False
+
+    def getDeviceName(self, device = 1, timeout = 300):
+        self.device_id = device
+        self.device_cmd = 'report model;\r'
+        self.device_timeout = timeout
+        result = self.sendmsg()
+        if result is None:
+            return None
+        return result['device_cmd_result']
+
+    def getDeviceSoft(self, device = 1, timeout = 300):
+        self.device_id = device
+        self.device_cmd = 'report ver;\r'
+        self.device_timeout = timeout
+        result = self.sendmsg()
+        if result is None:
+            return None
+        return result['device_cmd_result']
+
+    def getStatus(self, device = 1, timeout = 300):
+        self.device_id = device
+        self.device_cmd = 'dummy;\r'
+        self.device_timeout = timeout
+        return self.__parse__(self.sendmsg())
+
+    def setBlueOFF(self, device = 1, timeout = 300):
+        self.device_id = device
+        self.device_cmd = 'b off;\r'
+        self.device_timeout = timeout
+        return self.__parse__(self.sendmsg())
+
+    def setBlueON(self, device = 1, timeout = 300):
+        self.device_id = device
+        self.device_cmd = 'b on;\r'
+        self.device_timeout = timeout
+        return self.__parse__(self.sendmsg())
+
+    def setGreenOFF(self, device = 1, timeout = 300):
+        self.device_id = device
+        self.device_cmd = 'g off;\r'
+        self.device_timeout = timeout
+        return self.__parse__(self.sendmsg())
+
+    def setGreenON(self, device = 1, timeout = 300):
+        self.device_id = device
+        self.device_cmd = 'g on;\r'
+        self.device_timeout = timeout
+        return self.__parse__(self.sendmsg())
+
+    def setRedOFF(self, device = 1, timeout = 300):
+        self.device_id = device
+        self.device_cmd = 'r off;\r'
+        self.device_timeout = timeout
+        return self.__parse__(self.sendmsg())
+
+    def setRedON(self, device = 1, timeout = 300):
+        self.device_id = device
+        self.device_cmd = 'r on;\r'
+        self.device_timeout = timeout
+        return self.__parse__(self.sendmsg())
+
+    def setKeyBoardLock(self, device = 1, timeout = 300):
+        self.device_id = device
+        self.device_cmd = 'kb lock on;\r'
+        self.device_timeout = timeout
+        return self.__parse__(self.sendmsg())
+
+    def setKeyBoardUnLock(self, device = 1, timeout = 300):
+        self.device_id = device
+        self.device_cmd = 'kb lock off;\r'
+        self.device_timeout = timeout
+        return self.__parse__(self.sendmsg())
+
+if __name__ == "__main__":
+    while(True):
+        print u'测试开始\n'
+        c2 = C22293Manager()
+        print c2.getDeviceName()
+        print c2.getDeviceSoft()
+        print c2.getStatus()
+
+        print c2.setPattern(11)
+        print c2.setTiming(11)
+        print c2.setTimingPattern(11,11)
+
+        print c2.setBlueOFF()
+        print c2.setBlueON()
+        
+        print c2.setGreenOFF()
+        print c2.setGreenON()
+
+        print c2.setRedOFF()
+        print c2.setRedON()
+
+        print c2.setKeyBoardLock()
+        print c2.setKeyBoardUnLock()
+
+        time.sleep(2)
+        print u'测试结束\n'
+

+ 174 - 0
ssat_sdk/device_manage/Model_3116A.py

@@ -0,0 +1,174 @@
+# -*- coding: UTF-8 -*-
+import io,sys,time
+reload(sys)
+sys.setdefaultencoding('utf-8')
+sys.path.append("..")
+import binascii
+import serial
+from ssat_sdk.baseSerial import baseSerial
+from ssat_sdk import sat_environment
+from ssat_sdk.device_manage.Model_3116A_CmdParse import C3316A_CmdParse
+from ssat_sdk.utils.AbnormalClient import abnormal_client
+
+class C3316A(baseSerial):
+    def __init__(self, port):
+        self.port = port
+        baseSerial.__init__(self)
+
+    def Open(self):
+        return self.open(self.port, baudrate=19200, bytesize=8, parity=serial.PARITY_NONE, stopbits=1, timeout=2, writeTimeout=0.5)
+
+    def __del__(self):
+        self.close()
+    
+    # 用于发命令前,检测串口是否打开;
+    def checkport(self):
+        if not self.ser.is_open:
+            self.reOpen()
+        
+        return self.ser.is_open
+
+    '''
+        描述:发送命令;
+        返回:True表示命令成功执行;
+    '''
+    def sendCmd(self, command, data, ctrl = True):
+        cmd = C3316A_CmdParse()
+        package = cmd.parseCommand(command, data, ctrl)
+        if self.write(package):
+            package = self.read()
+            if ctrl == True:
+                return cmd.parseResult(package)
+            else:
+                ret = cmd.parseResult(package)
+                # 获取设置信息;
+                print 'read_info=',cmd.Result
+                return ret
+        
+        if self.exception is not None:
+            abnormal_client.sendAbnormal('ATV', str(self.exception))
+
+        return False
+    
+    def sendCmdline(self, cmdline):
+        cmd = C3316A_CmdParse()
+        package = cmd.parseCmdLine(cmdline)
+        if package is None:
+            return False
+        if self.write(package):
+            package = self.read()
+            if cmd.CmdType == '=':
+                return cmd.parseResult(package)
+            else:
+                ret = cmd.parseResult(package)
+                # 获取设置信息;
+                print 'read_info=',cmd.Result
+                return ret
+        
+        if self.exception is not None:
+            abnormal_client.sendAbnormal('ATV', str(self.exception))
+            
+        return False
+    
+    def sendCmds(self, cmdlines):
+        comma = cmdlines.find(',')
+        if comma == -1:
+            return self.sendCmdline(cmdlines)
+        else:
+            result = True
+            while comma != -1:
+                cmd = cmdlines[:comma]
+                if self.sendCmdline(cmd) == False:
+                    result = False
+                cmdlines = cmdlines[comma+1:]
+                comma = cmdlines.find(',')
+            # 最后一条命令;
+            if self.sendCmdline(cmdlines) == False:
+                result = False
+            
+            return result
+
+
+if __name__ == "__main__":
+    scbc = C3316A(5)
+    if scbc.Open() == False:
+        print 'open false'
+    
+    if scbc.checkport() == True:
+        if 0:
+            scbc.sendCmd("SYSTV", '6')
+            scbc.sendCmd('SYSFR', '49.75')
+            scbc.sendCmd('SYSLV', '-10.00')
+        if 0:
+            # 设置频率:对应TG39中的RFFREQ命令:'RFFREQ 600.00'
+            scbc.sendCmd('SYSFR', '600.00')#6个字符:***.**
+        # 设置系统:对应TG39中的VCOL命令和RSYS命令 ???;
+        SYSTV = {
+            "NTSC M":       '0',  #TV Standard
+            "NTSC4.43 B/G": '1',
+            "NNTSC4.43 D/K":'2',
+            "NTSC4.43 I":   '3',
+            "PAL B/G":      '4',
+            "PAL I":        '5',
+            "PAL D":        '6',
+            "PAL-N":        '7',
+            "PAL-M":        '8',
+            "PAL D(CHINA)": '9',
+            "PAL B(AUS)":   '10',
+            "SECAM B/G":    '11',
+            "SECAM D/K/K1": '12',
+            "SECAM L":      '13',
+        }
+        if 0:
+            for item in SYSTV:
+                print 'cmd=',item
+                scbc.sendCmd("SYSTV", SYSTV[item])
+                time.sleep(0.9)
+
+        if 0:
+            # 设置信号强度:对应TG39中的RFLEV ???
+            scbc.sendCmd('SYSLV','90.0', False)#5个字符:***.*
+            scbc.sendCmd('SYSLV','-80.0')#5个字符:***.*
+
+        if 0:
+            # 先设置到DSVPS Programmable1~4.
+            scbc.sendCmd('DSVPS','1')#1-4
+            # 设置国家id:对应TG39中的RFCHA; ???
+            scbc.sendCmd('DSVCT','000')#3个字符:***
+             # 设置频道id:对应TG39中的RFCHB ???
+            scbc.sendCmd('DSVPT','000')#3个字符:***
+
+            # 先设置DSPDC Programmable1~4.
+            scbc.sendCmd('DSPDC','1')#1-4
+            # 设置国家id:对应TG39中的RFCHA; ???
+            scbc.sendCmd('DSPCT','000')
+            # 设置频道id:对应TG39中的RFCHB ???          
+            scbc.sendCmd('DSPPT','000')
+        
+        if 0:
+            # 设置彩条:0000~003
+            scbc.sendCmd('VDOVP','0000')#4字符0000~003
+            # 设置白场;
+            scbc.sendCmd('VDOVP','0500')#0500~0504
+            # 设置黑场???
+            # 设置方格信号开关???
+            # 重显率关闭、开启???
+            # 复合信号????
+
+        if 0:
+            # 不能在closed caption
+            scbc.sendCmd("SYSTV", '5')
+            # teletext off;
+            scbc.sendCmd('DSSYS','00')
+            # teletext Bxx:01-08
+            scbc.sendCmd('DSSYS','01')
+            # teletext Auto;
+            scbc.sendCmd('DSSYS', '09')
+            if 1:
+                # closed caption,要在NTSC M状态下才能关闭;
+                scbc.sendCmd("SYSTV", '0')
+                scbc.sendCmd('DSSYS', '10')
+                # CC Number1-Number9:00-08
+                scbc.sendCmd('DSCCS','1')
+
+    del scbc

+ 112 - 0
ssat_sdk/device_manage/Model_3116A_CmdParse.py

@@ -0,0 +1,112 @@
+# -*- coding: utf-8 -*-
+import sys,os,time
+reload(sys)
+sys.setdefaultencoding('utf-8')
+import struct,binascii
+
+class C3316A_CmdParse():
+    def __init__(self):
+        # 命令头;
+        self.Command = ''
+        # 数据体;
+        self.Data = ''
+        # 命令类型符;
+        self.CmdType = ''
+        # 结束符;
+        self.EOF = '\r'
+        # 分隔符;
+        self.Separator = ','
+        # 结果值;
+        self.Result = ''
+
+    '''
+        描述:将参数根据协议格式转化为协议指令;
+        参数:
+            command:协议命令码;
+            data:协议命令码数据;
+            ctrl:True表示是控制命令, False表示是设置命令;
+        注意:command, data传递的是字符串类型;
+        返回:返回要发送到串口中的数据包;
+    '''
+    def parseCommand(self, command, data, ctrl = True):
+        self.Command = command
+        if ctrl == True:        #控制设备命令;
+            self.CmdType = '='
+            self.Data = data
+        else:                   #读取配置信息命令;
+            self.CmdType = '?'
+            self.Data = ''
+
+        package = self.Command + self.CmdType + self.Data + self.EOF
+        return package
+    
+    def parseCmdLine(self, cmdline):
+        chIndex = cmdline.find('=')
+        if chIndex == -1:
+            chIndex = cmdline.find('?')
+            if chIndex == -1:
+                return None
+            self.CmdType = '?'
+        else:
+            self.CmdType = '='
+        # 命令符;
+        self.Command = cmdline[:chIndex]
+        # 数据;
+        self.Data = cmdline[chIndex+1:cmdline.find(self.EOF)]
+        # 返回数据包;
+        package = self.Command + self.CmdType + self.Data + self.EOF
+        return package
+
+
+    '''
+        描述:解析读串口数据是否正确返回;
+        参数:data:串口返回的数据(设备响应数据);
+        返回:命令有效返回True,否则返回False
+    '''
+    def parseResult(self, data):
+        if data.__len__() == 0:
+            return False
+        # 查找类型符;
+        index = data.find('=')
+        if index == -1:
+            return False
+        # 根据类型分析;
+        if self.CmdType == '?':
+            header = data[:index]
+            if header.lower() == self.Command.lower():
+                self.Result = data[index+1:data.find(self.EOF)]
+                return True
+            # read setting info类型,不根据rvope来返回;
+            self.parseResponse(data[index+1:])
+            return False
+        else:
+            return self.parseResponse(data[index+1:])
+    
+    '''
+        描述:解析设备RVOPE返回值;
+        参数:rvope;
+        返回:命令有效返回True,否则返回False
+    '''
+    def parseResponse(self, rvope):
+        # Command Error
+        if rvope[0] == '1':
+            print 'Command Error'
+            return False
+        
+        # Parameter Error
+        if rvope[1] == '1':
+            print 'Parameter Error'
+            return False
+
+        # Busy Error
+        if rvope[2] == '1':
+            print 'Busy Error'
+            return False    
+        
+        # Normal
+        return True
+    
+if __name__ == "__main__":
+    pass
+
+    

+ 226 - 0
ssat_sdk/device_manage/RedRatHub3.py

@@ -0,0 +1,226 @@
+# coding=utf-8
+#
+# Simple Python test program to use the RedRatHub.
+#
+# Ethan Johnson, David Blight, Chris Dodge - RedRat Ltd.
+#
+import sys
+import time
+import socket
+from ssat_sdk.service import service_config
+from ssat_sdk.utils import LoggingUtil
+from ssat_sdk.utils.AbnormalClient import abnormal_client
+
+
+class RedRat3():
+    # 构造函数
+    def __init__(self):
+        LoggingUtil.printLog("初始化RedRat3客户端")
+        self.sock = socket.socket()
+        self.socketOpen = False
+        self.Port = service_config.ServiceConfig().getRedRatHubPort()
+        self.deviceName = ""
+        print u'\n\n\n \t\t Init RedRat3 \n\n\n'.encode('gbk')
+        # self.__OpenSocket("127.0.0.1")
+        # LoggingUtil.printLog("初始化RedRat3客户端")
+        # LoggingUtil.printLog("初始化RedRat3客户端,完成")
+
+    # 析构函数
+    def __def__(self):
+        print u'\n\n\n \t\t Close RedRat3 \n\n\n'.encode('gbk')
+        self.__CloseSocket()
+
+    # 打开连接
+    def __OpenSocket(self, ip="127.0.0.1"):
+        print "RedRatHub3.__OpenSocket,ip and port:", ip,self.Port
+        try:
+            status = self.sock.connect((ip, self.Port))
+            self.socketOpen = True
+            return status != 0
+        except Exception, e:
+            abnormal_client.sendAbnormal('RCU', str(e))
+            self.socketOpen = False
+            print u'\n\n\n \t\t Init RedRat3 Error \n\n\n'.encode('gbk')
+            # LoggingUtil.printLog(u"连接RedRatHubCmd.exe服务端失败")
+            print u"连接RedRatHubCmd.exe服务端失败"
+            return False
+
+    # 关闭链接
+    def __CloseSocket(self):
+        if self.socketOpen:
+            self.sock.close()
+            self.socketOpen = False
+        else:
+            print("\n\n\n \t\t Socket was to close.\n\n\n")
+
+    # 发送消息
+    def __ReadData(self, message):
+        if not self.socketOpen:
+            self.__CloseSocket()
+            self.__OpenSocket()
+            if not self.socketOpen:
+                print("\tRedratHub3 Socket has not been opened. Call 'OpenSocket()' first.")
+                return
+        try:
+            # Send message
+            self.sock.settimeout(100)
+            self.sock.send((message + '\n').encode())
+            received = ""
+
+            # Check response. This is either a single line, e.g. "OK\n", or a multi-line response with
+            # '{' and '}' start/end delimiters.
+            count = 0
+            while True:
+                count += 1
+                # Receives data
+                received += self.sock.recv(64).decode()
+                if self.__CheckEOM(received):
+                    return received
+                if count > 10:
+                    return received
+        except Exception, e:
+            abnormal_client.sendAbnormal('RCU', str(e))
+
+    # 检查是否到消息末端
+    def __CheckEOM(self, message):
+        # Multi-line message
+        if ('{' in message):
+            return ('}' in message)
+
+        # Single line message
+        if ("\n" in message):
+            return True
+
+    # 添加信号数据集xml文件,相同数据集名称的将会被覆盖
+    def addSignalDataSet(self, file):
+        self.__ReadData('hubquery="add ir-file",file="' + file + '"')
+        return
+
+        # 加载信号数据集xml文件,将会删除以前所有加载过的xml文件
+
+    def loadSignalDataSet(self, file):
+        self.__ReadData('hubquery="load ir-file",file="' + file + '"')
+        return
+
+    # 返回遥控设备名称
+    def getDeviceName(self):
+        devs = self.__ReadData('hubquery="list redrats"')
+        if devs is not None and devs.strip():
+            devs = devs.split('\n')[1]
+            self.deviceName = devs[devs.find('] ') + 2:]
+            return self.deviceName
+        return ""
+
+    def getCurrentDeviceName(self):
+        if not self.deviceName.strip():
+            self.getDeviceName()
+        return self.deviceName
+
+    # 返回遥控键值数据文件名
+    def getScriptName(self):
+        list = self.__ReadData('hubquery="list datasets"')
+        if list is not None and list.strip():
+            list = list.lstrip('{\n')
+            list = list.rstrip('\n}\n')
+            return list.split('\n')
+        return ""
+
+    # 返回遥控键值数据文件中包含的所有按键名称
+    def getKeyName(self):
+        list = self.__ReadData(
+            'hubquery="list signals in dataset",dataset="' + self.getScriptName()[0] + '"')
+        if list is not None and list.strip():
+            list = list.lstrip('{\n')
+            list = list.rstrip('\n}\n')
+            return list.split('\n')
+        return ""
+
+    # 返回遥控键值对应的键名称
+    def getKeyNumber(self):
+        list = self.__ReadData(
+            'hubquery="list signals in dataset",dataset="' + self.getScriptName()[0] + '"')
+        if list is not None and list.strip():
+            list = list.lstrip('{\n')
+            list = list.rstrip('\n}\n')
+            return list.split('\n')
+        return ""
+
+    # 发送指定的按键;成功返回0,失败返回失败次数
+    def sendKey(self, KeyName, Times=1, sleep_time=1):
+        status = 0
+        if not self.deviceName.strip():
+            self.getDeviceName()
+        while Times > 0:
+            Times -= 1
+            try:
+                result = self.__ReadData(
+                    'name="' + self.deviceName + '",dataset="' + self.getScriptName()[0] + '",signal="' + KeyName + '"')
+                print "RedRatHub3.sendKey, result:",result
+            except Exception, e:
+                print u"执行按键:%s异常!error:%s" % (str(KeyName), str(e))
+                result = ""
+            time.sleep(sleep_time)
+            if result != "OK":
+                status += 1
+        return status
+
+    # 发送指定的按键(多个),所有按钮都发送成功返回0;失败返回失败次数
+    def sendKeys(self, KeyNames, sleep_time=0.1):
+        print "redRatHub3:sendKeys:", KeyNames, sleep_time
+        status = 0
+        for key in KeyNames:
+            result = self.sendKey(key, sleep_time=sleep_time)
+            if result != "OK":
+                status += 1
+        return status
+
+    def sendrepeats(self, KeyName, repeats=2):
+        status = 0
+        if not self.deviceName.strip():
+            self.getDeviceName()
+        result = self.__ReadData('name="' + self.deviceName + '",dataset="' + self.getScriptName()[
+            0] + '",signal="' + KeyName + '",repeats="' + str(repeats) + '"')
+        time.sleep(1)
+        if result != "OK":
+            status += 1
+        return status
+
+    def sendduration(self, KeyName, duration=1500, sleep_time=0.1):
+        status = 0
+        if not self.deviceName.strip():
+            self.getDeviceName()
+        result = self.__ReadData('name="' + self.deviceName + '",dataset="' + self.getScriptName()[
+            0] + '",signal="' + KeyName + '",duration="' + str(duration) + '"')
+        time.sleep(sleep_time)
+        if result != "OK":
+            status += 1
+        return status
+
+    def sendrepeats2(self, KeyName, repeats=2, duration=1000):
+        status = 0
+        if not self.deviceName.strip():
+            self.getDeviceName()
+        result = self.__ReadData('name="' + self.deviceName + '",dataset="' + self.getScriptName()[
+            0] + '",signal="' + KeyName + '",repeats="' + str(repeats) + '",duration="' + str(duration) + '"')
+        time.sleep(1)
+        if result != "OK":
+            status += 1
+        return status
+
+    # 关闭socket通讯
+    def close(self):
+        self.__CloseSocket()
+        return
+
+
+# 单例模块;
+redrat3_singleton = RedRat3()
+
+if __name__ == "__main__":
+    redrat = RedRat3()
+    # 双击效果;
+    keyList = redrat.getKeyName()
+    print "keyList:",keyList
+    redrat.sendKey('power')
+    redrat.sendKey('home', 2, 0.1)
+    # redrat.sendrepeats('home', 2)

+ 232 - 0
ssat_sdk/device_manage/RedRatHub4.py

@@ -0,0 +1,232 @@
+# coding=utf-8
+#
+# Simple Python test program to use the RedRatHub.
+#
+# Ethan Johnson, David Blight, Chris Dodge - RedRat Ltd.
+#
+import sys
+import time
+import socket
+from ssat_sdk.service import service_config
+from ssat_sdk.utils import LoggingUtil
+from ssat_sdk.utils.AbnormalClient import abnormal_client
+
+
+class RedRat4():
+    # 构造函数
+    def __init__(self):
+        LoggingUtil.printLog("初始化RedRat4客户端")
+        self.sock = socket.socket()
+        self.socketOpen = False
+        self.Port = service_config.ServiceConfig().getRedRatHubPort()
+        self.deviceName = ""
+        print u'\n\n\n \t\t Init RedRat3 \n\n\n'.encode('gbk')
+        # self.__OpenSocket("127.0.0.1")
+        # LoggingUtil.printLog("初始化RedRat3客户端")
+        # LoggingUtil.printLog("初始化RedRat4客户端 完成")
+
+    # 析构函数
+    def __def__(self):
+        print u'\n\n\n \t\t Close RedRat3 \n\n\n'.encode('gbk')
+        self.__CloseSocket()
+
+    # 打开连接
+    def __OpenSocket(self, ip="127.0.0.1"):
+        # print "RedRatHub4.__OpenSocket,ip and port:", ip,self.Port
+        try:
+            status = self.sock.connect((ip, self.Port))
+            self.socketOpen = True
+            return status != 0
+        except Exception, e:
+            abnormal_client.sendAbnormal('RCU', str(e))
+            self.socketOpen = False
+            print u'\n\n\n \t\t Init RedRat3 Error \n\n\n'.encode('gbk')
+            # LoggingUtil.printLog(u"连接RedRatHubCmd.exe服务端失败")
+            print u"连接RedRatHubCmd.exe服务端失败"
+            return False
+
+    # 关闭链接
+    def __CloseSocket(self):
+        if self.socketOpen:
+            self.sock.close()
+            self.socketOpen = False
+        else:
+            print("\n\n\n \t\t Socket was to close.\n\n\n")
+
+    # 发送消息
+    def __ReadData(self, message):
+        # print "RedRatHub3.__ReadData, message:",message
+        if not self.socketOpen:
+            self.__CloseSocket()
+            self.__OpenSocket()
+            if not self.socketOpen:
+                print("\tRedratHub4 Socket has not been opened. Call 'OpenSocket()' first.")
+                return
+        try:
+            # Send message
+            self.sock.settimeout(100)
+            self.sock.send((message + '\n').encode())
+            received = ""
+
+            # Check response. This is either a single line, e.g. "OK\n", or a multi-line response with
+            # '{' and '}' start/end delimiters.
+            count = 0
+            while True:
+                count += 1
+                # Receives data
+                received += self.sock.recv(64).decode()
+                if self.__CheckEOM(received):
+                    return received
+                if count > 10:
+                    return received
+        except Exception, e:
+            abnormal_client.sendAbnormal('RCU', str(e))
+
+    # 检查是否到消息末端
+    def __CheckEOM(self, message):
+        # Multi-line message
+        if ('{' in message):
+            return ('}' in message)
+
+        # Single line message
+        if ("\n" in message):
+            return True
+
+    # 添加信号数据集xml文件,相同数据集名称的将会被覆盖
+    def addSignalDataSet(self, file):
+        self.__ReadData('hubquery="add ir-file",file="' + file + '"')
+        return
+
+        # 加载信号数据集xml文件,将会删除以前所有加载过的xml文件
+
+    def loadSignalDataSet(self, file):
+        self.__ReadData('hubquery="load ir-file",file="' + file + '"')
+        return
+
+    # 返回遥控设备名称
+    def getDeviceName(self):
+        devs = self.__ReadData('hubquery="list redrats"')
+        if devs is not None and devs.strip():
+            devs = devs.split('\n')[1]
+            # print "getDeviceName,devs:",devs
+            index1 = devs.find('] ') + 2
+            index2 = devs.find('(connected)') - 1
+            if index2 < 0:
+                index2 = None
+            self.deviceName = devs[index1:index2]
+            return self.deviceName
+        return ""
+
+    def getCurrentDeviceName(self):
+        if not self.deviceName.strip():
+            self.getDeviceName()
+        return self.deviceName
+
+    # 返回遥控键值数据文件名
+    def getScriptName(self):
+        list = self.__ReadData('hubquery="list datasets"')
+        if list is not None and list.strip():
+            list = list.lstrip('{\n')
+            list = list.rstrip('\n}\n')
+            return list.split('\n')
+        return ""
+
+    # 返回遥控键值数据文件中包含的所有按键名称
+    def getKeyName(self):
+        list = self.__ReadData(
+            'hubquery="list signals in dataset",dataset="' + self.getScriptName()[0] + '"')
+        if list is not None and list.strip():
+            list = list.lstrip('{\n')
+            list = list.rstrip('\n}\n')
+            return list.split('\n')
+        return ""
+
+    # 返回遥控键值对应的键名称
+    def getKeyNumber(self):
+        list = self.__ReadData(
+            'hubquery="list signals in dataset",dataset="' + self.getScriptName()[0] + '"')
+        if list is not None and list.strip():
+            list = list.lstrip('{\n')
+            list = list.rstrip('\n}\n')
+            return list.split('\n')
+        return ""
+
+    # 发送指定的按键;成功返回0,失败返回失败次数
+    def sendKey(self, KeyName, Times=1, sleep_time=1):
+        status = 0
+        if not self.deviceName.strip():
+            self.getDeviceName()
+        while Times > 0:
+            Times -= 1
+            try:
+                result = self.__ReadData(
+                    'name="' + self.deviceName + '",dataset="' + self.getScriptName()[0] + '",signal="' + KeyName + '"')
+                print "RedRatHub4.sendKey:%s, result:%s"%(KeyName,result)
+            except Exception, e:
+                print u"执行按键:%s异常!error:%s" % (str(KeyName), str(e))
+                result = ""
+            time.sleep(sleep_time)
+            if result != "OK":
+                status += 1
+        return status
+
+    # 发送指定的按键(多个),所有按钮都发送成功返回0;失败返回失败次数
+    def sendKeys(self, KeyNames, sleep_time=0.1):
+        print "RedRatHub4:sendKeys:", KeyNames, sleep_time
+        status = 0
+        for key in KeyNames:
+            result = self.sendKey(key, sleep_time=sleep_time)
+            if result != "OK":
+                status += 1
+        return status
+
+    def sendrepeats(self, KeyName, repeats=2):
+        status = 0
+        if not self.deviceName.strip():
+            self.getDeviceName()
+        result = self.__ReadData('name="' + self.deviceName + '",dataset="' + self.getScriptName()[
+            0] + '",signal="' + KeyName + '",repeats="' + str(repeats) + '"')
+        time.sleep(1)
+        if result != "OK":
+            status += 1
+        return status
+
+    def sendduration(self, KeyName, duration=1500, sleep_time=0.1):
+        status = 0
+        if not self.deviceName.strip():
+            self.getDeviceName()
+        result = self.__ReadData('name="' + self.deviceName + '",dataset="' + self.getScriptName()[
+            0] + '",signal="' + KeyName + '",duration="' + str(duration) + '"')
+        time.sleep(sleep_time)
+        if result != "OK":
+            status += 1
+        return status
+
+    def sendrepeats2(self, KeyName, repeats=2, duration=1000):
+        status = 0
+        if not self.deviceName.strip():
+            self.getDeviceName()
+        result = self.__ReadData('name="' + self.deviceName + '",dataset="' + self.getScriptName()[
+            0] + '",signal="' + KeyName + '",repeats="' + str(repeats) + '",duration="' + str(duration) + '"')
+        time.sleep(1)
+        if result != "OK":
+            status += 1
+        return status
+
+    # 关闭socket通讯
+    def close(self):
+        self.__CloseSocket()
+        return
+
+
+# 单例模块;
+redrat4_singleton = RedRat4()
+
+if __name__ == "__main__":
+    redrat = RedRat4()
+    # 双击效果;
+    keyList = redrat.getKeyName()
+    print "keyList:",keyList
+    # redrat.sendKey('power')
+    # redrat.sendKey('home', 2, 0.1)
+    # redrat.sendrepeats('home', 2)

+ 2 - 0
ssat_sdk/device_manage/__init__.py

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

+ 113 - 0
ssat_sdk/device_manage/baseClient - 副本.py

@@ -0,0 +1,113 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+import abc
+import socket
+import json
+import numpy as np
+import struct
+import binascii
+
+#创建ProHeader数据类型
+# 定义:
+DataHeader = np.dtype({'names': ['protocol', 'len', 'cmd'], 'formats': ['u1', 'u4', 'u1']}, align=False)
+UserInfo = np.dtype({'names': ['userName', 'password'], 'formats': ['|S260', '|S260']}, align=False)
+
+class BaseClient(object):
+    __metaclass__ = abc.ABCMeta
+
+    def __init__(self):
+        """设备id"""
+        self.device_id = 1
+        '''设备名称'''
+        self.device_name = "name"
+        '''设备命令'''
+        self.device_cmd = "rtn 11;"
+        '''设备通讯超时值:毫秒'''
+        self.device_timeout = 300
+
+        '''通信sock'''
+        self.sock = None
+        '''通信端口号'''
+        self.port = 5588
+        '''通信状态'''
+        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("2222")
+            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
+
+        '''拼装要发送的数据包'''
+        userinfo = np.array([("superAdmin", "123456")], dtype=UserInfo)
+
+        '''发送请求数据'''
+        try:
+            self.sock.settimeout(5)
+            phead = np.array([(0xAA, userinfo.itemsize + np.array([(0xAA, 0, 0x01)], dtype=DataHeader).itemsize, 0x00)], dtype=DataHeader)
+            '''一次性发送完整包'''
+            # self.sock.sendall(bytearray(phead.tobytes() + bytes(msg_json)))
+            '''分包发送:头、体'''
+            self.sock.sendall(phead.tobytes())
+            self.sock.sendall(userinfo.tobytes())
+            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))
+            print "recv:" + binascii.hexlify(data)
+            self.sock.settimeout(None)
+
+            phead = data[0: np.array([(0xAA, 0, 0x01)], dtype=DataHeader).itemsize]
+            respone = data[np.array([(0xAA, 0, 0x01)], dtype=DataHeader).itemsize:]
+            print "respone:" + binascii.hexlify(respone)
+            '''小端<I, 大端>I, 网络端 !'''
+            protocol, len, cmd = struct.unpack('<BIB', phead)
+            loginresult = int(struct.unpack('<c', respone))
+            '''返回头是否有效'''
+            if protocol != 0xAA and len != data.__len__():
+                return None
+            
+            return loginresult
+        except Exception, e:
+            print "BaseClient=> recv Error:", e
+            return None
+
+    '''断开连接'''
+    def disconnect(self):
+        try:
+            self.sock.shutdown(2)
+            self.sock.close()
+        except Exception, e:
+            print "BaseClient=> socket disconnect error:", e
+
+
+if __name__ == "__main__":
+    bc = BaseClient()
+    bc.sendmsg()
+
+    print "ok!"

+ 113 - 0
ssat_sdk/device_manage/baseClient.py

@@ -0,0 +1,113 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+import abc
+import socket
+import json
+import numpy as np
+import struct
+
+#创建ProHeader数据类型
+ProHead = np.dtype({'names': ['version', 'len'], 'formats': ['u1', 'u4']})
+
+class BaseClient(object):
+    __metaclass__ = abc.ABCMeta
+
+    def __init__(self):
+        """设备id"""
+        self.device_id = 0
+        '''设备名称'''
+        self.device_name = ""
+        '''设备命令'''
+        self.device_cmd = ""
+        '''设备通讯超时值:毫秒'''
+        self.device_timeout = 300
+
+        '''通信sock'''
+        self.sock = None
+        '''通信端口号'''
+        self.port = 5566
+        '''通信状态'''
+        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 disconnect(self):
+        try:
+            self.sock.shutdown(2)
+            self.sock.close()
+        except Exception, e:
+            print "BaseClient=> socket disconnect error:", e

+ 267 - 0
ssat_sdk/device_manage/c22293_manager.py

@@ -0,0 +1,267 @@
+# -*- coding:utf-8 -*-
+import os
+import sys
+import time
+import json
+from ssat_sdk.utils import LoggingUtil
+# from ssat_sdk.utils.LoggingUtil import printLog
+from device_manager import *
+import inspect
+
+pyFileName = os.path.split(__file__)[-1]
+
+
+def get_current_function_name():
+    return inspect.stack()[1][3]
+
+
+class C22293Manager():
+    def __init__(self):
+        self.className = self.__class__.__name__
+        self.devManager = DeviceManager.getInstance()
+        self.dataDict = {"command": "", "param": [], "device": ""}
+        # LoggingUtil.printLog("初始化22293设备")
+        # LoggingUtil.getDebugLogger().info(
+        #     pyFileName,
+        #     self.className,
+        #     get_current_function_name(),
+        #     '初始化22293设备')
+        LoggingUtil.printLog('Chroma22293', '初始化22293设备')
+
+    def setPattern(self, pattern, device=''):
+        self.client = self.devManager.getDevService("c22293")
+        self.dataDict["command"] = "setPattern"
+        self.dataDict["param"] = [pattern]
+        self.dataDict["device"] = device
+        self.client.send(json.dumps(self.dataDict))
+        result = self.client.recv()
+        # print u"设置pattern结果:", result
+        # LoggingUtil.getDebugLogger().info(
+        #     pyFileName,
+        #     self.className,
+        #     get_current_function_name(),
+        #     '设置pattern结果:'+str(result))
+        LoggingUtil.printLog('Chroma22293', '设置pattern结果:'+str(result))
+        self.client.close()
+        return result
+
+    def setTiming(self, timing, device=''):
+        self.client = self.devManager.getDevService("c22293")
+        self.dataDict["command"] = "setTiming"
+        self.dataDict["param"] = [timing]
+        self.dataDict["device"] = device
+        self.client.send(json.dumps(self.dataDict))
+        result = self.client.recv()
+        # print u"设置timing结果:", result
+        # LoggingUtil.getDebugLogger().info(
+        #     pyFileName,
+        #     self.className,
+        #     get_current_function_name(),
+        #     '设置timing结果:'+str(result))
+        LoggingUtil.printLog('Chroma22293', '设置timing结果:'+str(result))
+        self.client.close()
+        return result
+
+    def setTimingPattern(self, timing, pattern, device=''):
+        self.client = self.devManager.getDevService("c22293")
+        self.dataDict["command"] = "setTimingPattern"
+        self.dataDict["param"] = [timing, pattern]
+        self.dataDict["device"] = device
+        self.client.send(json.dumps(self.dataDict))
+        result = self.client.recv()
+        # print u"设置TimingPattern结果:", result
+        # LoggingUtil.getDebugLogger().info(
+        #     pyFileName,
+        #     self.className,
+        #     get_current_function_name(),
+        #     '设置TimingPattern结果:'+str(result))
+        LoggingUtil.printLog('Chroma22293', '设置TimingPattern结果:'+str(result))
+        self.client.close()
+        return result
+
+    def getDeviceName(self, device=''):
+        self.client = self.devManager.getDevService("c22293")
+        self.dataDict["command"] = "getDeviceName"
+        self.dataDict["param"] = []
+        self.dataDict["device"] = device
+        self.client.send(json.dumps(self.dataDict))
+        result = self.client.recv()
+        # print u"getDeviceName结果:", result
+        # LoggingUtil.getDebugLogger().info(
+        #     pyFileName,
+        #     self.className,
+        #     get_current_function_name(),
+        #     'getDeviceName结果:'+str(result))
+        LoggingUtil.printLog('Chroma22293', 'getDeviceName结果:'+str(result))
+        self.client.close()
+        return result
+
+    def getDeviceSoft(self, device=''):
+        self.client = self.devManager.getDevService("c22293")
+        self.dataDict["command"] = "getDeviceSoft"
+        self.dataDict["param"] = []
+        self.dataDict["device"] = device
+        self.client.send(json.dumps(self.dataDict))
+        result = self.client.recv()
+        # print u"getDeviceSoft结果:", result
+        # LoggingUtil.getDebugLogger().info(
+        #     pyFileName,
+        #     self.className,
+        #     get_current_function_name(),
+        #     'getDeviceSoft结果:'+str(result))
+        LoggingUtil.printLog('Chroma22293', 'getDeviceSoft结果:'+str(result))
+        self.client.close()
+        return result
+
+    def getStatus(self, device=''):
+        self.client = self.devManager.getDevService("c22293")
+        self.dataDict["command"] = "getStatus"
+        self.dataDict["param"] = []
+        self.dataDict["device"] = device
+        self.client.send(json.dumps(self.dataDict))
+        result = self.client.recv()
+        # print u"getStatus结果:", result
+        # LoggingUtil.getDebugLogger().info(
+        #     pyFileName,
+        #     self.className,
+        #     get_current_function_name(),
+        #     'getStatus结果:'+str(result))
+        LoggingUtil.printLog('Chroma22293', 'getStatus结果:'+str(result))
+        self.client.close()
+        return result
+
+    def setBlueOFF(self, device=''):
+        self.client = self.devManager.getDevService("c22293")
+        self.dataDict["command"] = "setBlueOFF"
+        self.dataDict["param"] = []
+        self.dataDict["device"] = device
+        self.client.send(json.dumps(self.dataDict))
+        result = self.client.recv()
+        # print u"setBlueOFF结果:", result
+        # LoggingUtil.getDebugLogger().info(
+        #     pyFileName,
+        #     self.className,
+        #     get_current_function_name(),
+        #     'setBlueOFF结果:'+str(result))
+        LoggingUtil.printLog('Chroma22293', 'setBlueOFF结果:'+str(result))
+        self.client.close()
+        return result
+
+    def setBlueON(self, device=''):
+        self.client = self.devManager.getDevService("c22293")
+        self.dataDict["command"] = "setBuleON"
+        self.dataDict["param"] = []
+        self.dataDict["device"] = device
+        self.client.send(json.dumps(self.dataDict))
+        result = self.client.recv()
+        # print u"setBuleON结果:", result
+        # LoggingUtil.getDebugLogger().info(
+        #     pyFileName,
+        #     self.className,
+        #     get_current_function_name(),
+        #     'setBuleON结果:'+str(result))
+        LoggingUtil.printLog('Chroma22293', 'setBuleON结果:'+str(result))
+        self.client.close()
+        return result
+
+    def setGreenOFF(self, device=''):
+        self.client = self.devManager.getDevService("c22293")
+        self.dataDict["command"] = "setGreenOFF"
+        self.dataDict["param"] = []
+        self.dataDict["device"] = device
+        self.client.send(json.dumps(self.dataDict))
+        result = self.client.recv()
+        # print u"setGreenOFF结果:", result
+        # LoggingUtil.getDebugLogger().info(
+        #     pyFileName,
+        #     self.className,
+        #     get_current_function_name(),
+        #     'setGreenOFF结果:'+str(result))
+        LoggingUtil.printLog('Chroma22293', 'setBuleON结果:'+str(result))
+        self.client.close()
+        return result
+
+    def setGreenON(self, device=''):
+        self.client = self.devManager.getDevService("c22293")
+        self.dataDict["command"] = "setGreenON"
+        self.dataDict["param"] = []
+        self.dataDict["device"] = device
+        self.client.send(json.dumps(self.dataDict))
+        result = self.client.recv()
+        # print u"setGreenON结果:", result
+        # LoggingUtil.getDebugLogger().info(
+        #     pyFileName,
+        #     self.className,
+        #     get_current_function_name(),
+        #     'setGreenON结果:'+str(result))
+        LoggingUtil.printLog('Chroma22293', 'setBuleON结果:'+str(result))
+        self.client.close()
+        return result
+
+    def setRedOFF(self, device=''):
+        self.client = self.devManager.getDevService("c22293")
+        self.dataDict["command"] = "setRedOFF"
+        self.dataDict["param"] = []
+        self.dataDict["device"] = device
+        self.client.send(json.dumps(self.dataDict))
+        result = self.client.recv()
+        # print u"setRedOFF结果:", result
+        # LoggingUtil.getDebugLogger().info(
+        #     pyFileName,
+        #     self.className,
+        #     get_current_function_name(),
+        #     'setRedOFF结果:'+str(result))
+        LoggingUtil.printLog('Chroma22293', 'setBuleON结果:'+str(result))
+        self.client.close()
+        return result
+
+    def setRedON(self, device=''):
+        self.client = self.devManager.getDevService("c22293")
+        self.dataDict["command"] = "setRedON"
+        self.dataDict["param"] = []
+        self.dataDict["device"] = device
+        self.client.send(json.dumps(self.dataDict))
+        result = self.client.recv()
+        # print u"setRedON结果:", result
+        # LoggingUtil.getDebugLogger().info(
+        #     pyFileName,
+        #     self.className,
+        #     get_current_function_name(),
+        #     'setRedON结果:'+str(result))
+        LoggingUtil.printLog('Chroma22293', 'setRedON结果:'+str(result))
+        self.client.close()
+        return result
+
+    def setKeyBoardLock(self, device=''):
+        self.client = self.devManager.getDevService("c22293")
+        self.dataDict["command"] = "setKeyBoardLock"
+        self.dataDict["param"] = []
+        self.dataDict["device"] = device
+        self.client.send(json.dumps(self.dataDict))
+        result = self.client.recv()
+        # print u"setKeyBoardLock结果:", result
+        # LoggingUtil.getDebugLogger().info(
+        #     pyFileName,
+        #     self.className,
+        #     get_current_function_name(),
+        #     'setKeyBoardLock结果:'+str(result))
+        LoggingUtil.printLog('Chroma22293', 'setKeyBoardLock结果:'+str(result))
+        self.client.close()
+        return result
+
+    def setKeyBoardUnLock(self, device=''):
+        self.client = self.devManager.getDevService("c22293")
+        self.dataDict["command"] = "setKeyBoardUnLock"
+        self.dataDict["param"] = []
+        self.dataDict["device"] = device
+        self.client.send(json.dumps(self.dataDict))
+        result = self.client.recv()
+        # print u"setKeyBoardUnLock结果:", result
+        # LoggingUtil.getDebugLogger().info(
+        #     pyFileName,
+        #     self.className,
+        #     get_current_function_name(),
+        #     'setKeyBoardUnLock结果:'+str(result))
+        LoggingUtil.printLog('Chroma22293', 'setKeyBoardUnLock结果:'+str(result))
+        self.client.close()
+        return result

+ 242 - 0
ssat_sdk/device_manage/capturecard_manager.py

@@ -0,0 +1,242 @@
+# -*- coding:utf-8 -*-
+import os
+import sys
+import time
+# from device_manager import *
+from ssat_sdk.utils import LoggingUtil
+from ssat_sdk import sat_environment
+from ssat_sdk.utils import string_util
+from ssat_sdk.service.service_manager import ServiceManager
+from ssat_sdk.device_manage.ub530_manager import UB530Manager
+import CD750SSDK
+import UB530SDK
+UB530 = 'TC-UB530'
+UB530EX = 'TC-UB530EX'
+CD750 = 'CD750'
+
+
+class CCardManager():
+    status = False
+    exe_path = sys.prefix + r"/Tools/CD750/AVerCapSDKDemo.exe"
+    sManager = ServiceManager()
+    DEVICE_NAME = sat_environment.getCCard_Selected()
+    print u"视频采集卡名称:", DEVICE_NAME
+
+    def __init__(self):
+        if string_util.strcmp(UB530, self.DEVICE_NAME):     # Python版本;
+            self.ccard = UB530Manager()
+        elif string_util.strcmp(UB530EX, self.DEVICE_NAME):  # VC++版本;
+            CCardManager.exe_path = sys.prefix + r"/Tools/SATHelper/SATHelper.exe"
+        else:
+            CCardManager.exe_path = sys.prefix + r"/Tools/CD750/AVerCapSDKDemo.exe"
+
+    def start(self):
+        print "CCardManager ssdk"
+        if string_util.strcmp(self.DEVICE_NAME, CD750):
+            self.status = CD750SSDK.StartApp(CCardManager.exe_path)
+            time.sleep(3)
+            if self.status == True:
+                LoggingUtil.printLog("打开CD750 AVerCapSDKDemo成功")
+                # 连接设备
+                CD750SSDK.ConnectDevice(0)
+                CCardManager.status = True
+                # 开始流操作
+                time.sleep(1)
+                CD750SSDK.StreamOption(True)
+            else:
+                LoggingUtil.printLog("打开CD750 AVerCapSDKDemo失败")
+            return self.status
+        elif string_util.strcmp(self.DEVICE_NAME, UB530):  # Python版本;
+            self.sManager.startServiceThread("ub530")
+            time.sleep(2)
+            return self.sManager.getServiceStatus("ub530")
+        elif string_util.strcmp(self.DEVICE_NAME, UB530EX):  # VC++版本;
+            self.status = UB530SDK.StartApp(CCardManager.exe_path)
+            time.sleep(3)
+            if self.status == True:
+                LoggingUtil.printLog("打开UB530 VideoCapture成功")
+                # 连接设备
+                UB530SDK.ConnectDevice()
+                CCardManager.status = True
+            else:
+                LoggingUtil.printLog("打开UB530 VideoCapture失败")
+            return self.status
+
+    def isExeRunning(self):
+        if string_util.strcmp(self.DEVICE_NAME, CD750):
+            return CD750SSDK.IsAppRunning(CCardManager.exe_path)
+        elif string_util.strcmp(self.DEVICE_NAME, UB530EX):  # VC++版本;
+            return UB530SDK.IsAppRunning(CCardManager.exe_path)
+        elif string_util.strcmp(self.DEVICE_NAME, UB530):  # Python版本;
+            return self.sManager.getServiceStatus("ub530")
+
+    # start app
+    def startApp(self):
+        self.status = CD750SSDK.StartApp(CCardManager.exe_path)
+        return self.status
+
+    # close app;
+    def stopApp(self):
+        self.status = not CD750SSDK.StopApp(CCardManager.exe_path)
+        return self.status
+
+    # connect device
+    def connect(self, index=0):
+        return CD750SSDK.ConnectDevice(index)
+
+    # disconnect device
+    def disconnect(self, index=0):
+        return CD750SSDK.DisconnectDevice(index)
+
+    # show app
+    def showApp(self):
+        CD750SSDK.ShowApp()
+
+    # hide app
+    def hideApp(self):
+        CD750SSDK.HideApp()
+
+    # stream option,开始/停止流操作
+    def streamOption(self, enable=True):
+        return CD750SSDK.StreamOption(enable)
+
+    # 截图单张
+    def captureSingleImage(self, picpath):
+        return CD750SSDK.CaptureSingleImage(picpath, 3, True, 0, 0, 0, 0)
+
+    # 按张数截图:picpath图片路径,count截图张数
+    def captureImageByCount(self, count, picpath, prefix="CD750"):
+        return CD750SSDK.CaptureImageByCount(count, picpath, prefix, 3, True, 0, 0, 0, 0)
+
+    # 按时间截图:times持续时间,单位毫秒; perCount每秒截图的张数; picpath图片保存路径;
+    def captureImageByTime(self, times, perCount, picpath, prefix="CD750"):
+        if string_util.strcmp(CD750, self.DEVICE_NAME):
+            return CD750SSDK.CaptureImageByTime(times, perCount, picpath, prefix, 3, True, 0, 0, 0, 0)
+        elif string_util.strcmp(UB530EX, self.DEVICE_NAME):  # VC++版本;
+            return UB530SDK.CaptureImageByTime(times, 0, picpath, prefix, 2)
+        elif string_util.strcmp(UB530, self.DEVICE_NAME):  # Python版本;
+            count = 0
+            runTime = time.time()
+            endWork = False
+            lastIndex = 1
+            times = times/1000
+            while True:
+                lastCount = lastIndex + perCount
+                for i in range(lastIndex, lastCount):
+                    path = "%s%s_%d.jpg" % (picpath, prefix, i)
+                    if self.ccard.takePicture(path) == 0:
+                        count += 1
+                    time.sleep(0.2)  # 回调响应时间;
+                    if (time.time() - runTime >= times):
+                        endWork = True
+                        break
+                lastIndex += perCount
+                if endWork:
+                    break
+            # print '实际生成数量%d,目标生成数量%d'%(count,perCount*times)
+            return True if count == perCount*times else False
+
+    # 抓单张图;
+    def takePicture(self, picpath):
+        status = -1
+        if string_util.strcmp(CD750, self.DEVICE_NAME):
+            status = CD750SSDK.CaptureSingleImage(picpath, 3, True, 0, 0, 0, 0)
+            time.sleep(0.5)
+        elif string_util.strcmp(UB530EX, self.DEVICE_NAME):  # VC++版本;
+            status = UB530SDK.CaptureSingleImage(picpath, 2)
+            count = 0
+            while os.path.exists(picpath) is False:
+                time.sleep(0.1) # 等待磁盘完成响应;
+                if count == 5:
+                    break
+                count = count + 1
+            # 超过5次认为截图失败;
+            status = True if count < 5  else False
+        elif string_util.strcmp(UB530, self.DEVICE_NAME):  # Python版本;
+            status = self.ccard.takePicture(picpath)
+            if status == 0:
+                status = 1
+            else:
+                status = 0
+            time.sleep(1)
+        return status
+
+    # 返回保存的文件列表;
+    def getCaptureImageList(self, picpath):
+        list = []
+        if string_util.strcmp(CD750, self.DEVICE_NAME):
+            list = CD750SSDK.GetCaptureImageList(picpath)
+        elif string_util.strcmp(UB530EX, self.DEVICE_NAME):  # VC++版本;
+            list = UB530SDK.GetCaptureImageList(picpath)
+        elif string_util.strcmp(UB530, self.DEVICE_NAME):  # Python版本;
+            list = []
+            # 过滤掉多余的'/'
+            newpath = ''
+            for i in range(0, picpath.__len__(), 2):
+                char1 = picpath[i]
+                if i < picpath.__len__()-1:
+                    char2 = picpath[i+1]
+                    newpath += char1
+                    if char1 == char2 and char1 == '/':
+                        continue
+                    newpath += char2
+                else:
+                    newpath += char1
+            picpath = newpath.replace('/', '\\')
+            index = picpath.rfind('\\')
+            # 路径;
+            dir = picpath[:index]
+            # 文件名;
+            name = picpath[index+1:]
+            # 前缀;
+            preFix = name[:name.rfind("_")]
+            for dirpath, dirname, filenames in os.walk(dir):
+                for filepath in filenames:
+                    list.append(os.path.join(dirpath, filepath))
+        # endif
+        list.sort()
+        return list
+
+    # 停止抓图;
+    def stopCaptureImage(self):
+        CD750SSDK.StopCaptureImage()
+
+    # 开始录制视频;
+    def startCaptureAudio(self, audiopath):
+        CD750SSDK.StartCaptureAudio(audiopath)
+
+    # 停止录制视频;
+    def stopCaptureAudio(self):
+        CD750SSDK.StopCaptureAudio()
+
+    # 异步录制视频;
+    def asyStartCaptureAudio(self, keeptime, audiopath):
+        CD750SSDK.AsyCaptureAudio(keeptime, audiopath)
+
+
+if __name__ == "__main__":
+    cardManager = CCardManager()
+    print cardManager.start()
+    # time.sleep(1)
+    # print u"开始截图"
+    time_start = time.time()
+    cardManager.takePicture("D:\\SAT\\test1.png")
+    time_end = time.time()
+    print('totally cost', time_end-time_start)
+
+    # print u"按时间截图"
+    # cardManager.captureImageByTime(5000, 5, "D:/SAT/aaa/")
+    # time.sleep(5)
+    # print u"遍历设备"
+    # list = CD750SSDK.GetCaptureImageList("D:/SAT/aaa/")
+    # if list != None:
+    #     for file in list:
+    #         print file
+    # time.sleep(2)
+    # # 断开连接
+    # print u"断开设备"
+    # cardManager.disconnect(0)
+    # time.sleep(3)
+    # # 关闭app
+    # print u"关闭程序(宿主程序需要管理员权限)"
+    # cardManager.stopApp()

+ 196 - 0
ssat_sdk/device_manage/dektec_manager.py

@@ -0,0 +1,196 @@
+# -*- coding: UTF-8 -*-
+import sys
+import os
+from os import path, access, R_OK
+import subprocess
+from subprocess import CalledProcessError
+import delegator
+import time
+
+from ssat_sdk import sat_environment
+from ssat_sdk.utils import string_util
+
+__version__ = '0.0.1'
+__author__ = 'Red Leaf'
+
+class DektecManager():
+    DtPlay_exe = sys.prefix + r"/Tools/DtPlay/DtPlay.exe"
+    child = None
+    cmdline = ""
+
+    def __init__(self):
+        self.deviceType = self.getTypeNO()
+        print "DektecManager() : " + sys.prefix, self.deviceType
+        print "DektecManager() : " + DektecManager.DtPlay_exe
+        if path.exists(DektecManager.DtPlay_exe) and path.isfile(DektecManager.DtPlay_exe) and access(
+                DektecManager.DtPlay_exe, R_OK):
+            print "DtPlay_exe : exists and is readable"
+        else:
+            print "DtPlay_exe : file is missing or is not readable, please check if " \
+                   "Python27\Tools\DtPlay\DtPlay.exe is exists"
+
+        # 强制结束所有DtPlay.exe
+        self.taskkill()
+        print "DtPlay_exe start!!!"
+
+    def getTypeNO(self):
+        type = sat_environment.getDTVSelected()
+        if type.__contains__("2115"):
+            return "2115"
+        elif type.__contains__("115"):
+            return "115"
+        elif type.__contains__("215"):
+            return "215"
+        else:
+            return "215"
+
+    def playStream(self, streamFile, times, type, channelPoint):
+        self.interrupt()
+        DektecManager.child = subprocess.Popen(DektecManager.DtPlay_exe + " " + streamFile + " -l " + str(times) +
+                              " -mt " + type + " -mf " + channelPoint)
+
+        DektecManager.child.wait()
+
+    def playStreamAtThread(self, streamFile,  times,  type,  channelPoint ):
+        self.interrupt()
+        DektecManager.child = subprocess.Popen(DektecManager.DtPlay_exe + " " + streamFile + " -l " + str(times) +
+                              " -mt " + type + " -mf " + channelPoint)
+
+
+    def isPlaying(self):
+        if DektecManager.child is not None and DektecManager.child.poll() is None:
+            return True
+        else:
+            return False
+
+    def interrupt(self):
+        if DektecManager.child is not None and DektecManager.child.poll() is None:
+            DektecManager.child.kill()
+
+    def taskkill(self):
+        # 强制结束任务管理器所有 DtPlay.exe 进程
+        try:
+            command = 'taskkill /F /IM DtPlay.exe'
+            os.system(command)
+        except Exception,e:
+            print u"没有杀死DtPlay进程:", e
+
+    def set_cmd_clear(self):
+        DektecManager.cmdline = ""
+
+    def set_playfile(self, playfile):
+        # 码流文件
+        DektecManager.cmdline = str(playfile) + " " + DektecManager.cmdline
+
+    def set_Device_type(self, type=''):
+        # 设备选择
+        if type.__len__() > 0:
+            DektecManager.cmdline = DektecManager.cmdline + " -t " + str(type)
+        else:
+            DektecManager.cmdline = DektecManager.cmdline + " -t " + str(self.deviceType)
+
+    def set_Modulation_carrier_frequency(self, frequency):
+        # 频点
+        DektecManager.cmdline = DektecManager.cmdline + " -mf " + str(frequency)
+
+    def set_Modulation_type(self, Modulation_type):
+        # 制式选择
+        DektecManager.cmdline = DektecManager.cmdline + " -mt " + str(Modulation_type)
+
+    def set_Modulation_bandwidth(self, Modulation_bandwidth):
+        # 带宽
+        DektecManager.cmdline = DektecManager.cmdline + " -mB " + str(Modulation_bandwidth)
+
+    def set_Modulation_transmission_mode(self, transmission_mode):
+        # 载波数
+        DektecManager.cmdline = DektecManager.cmdline + " -mT " + str(transmission_mode)
+
+    def set_Modulation_constellation (self, constellation ):
+        # 调制方式
+        DektecManager.cmdline = DektecManager.cmdline + " -mC " + str(constellation )
+
+    def set_Modulation_guard_interval(self, guard_interval):
+        # 保护间隔
+        DektecManager.cmdline = DektecManager.cmdline + " -mG " + str(guard_interval)
+
+    def set_Modulation_Convolutional_rate(self, Convolutional_rate):
+        # 信号纠错码
+        DektecManager.cmdline = DektecManager.cmdline + " -mc " + str(Convolutional_rate)
+
+    def set_Modulation_Output_level(self, Output_level):
+        # 信号强度
+        DektecManager.cmdline = DektecManager.cmdline + " -ml " + str(Output_level)
+
+    def set_loop_times(self, loop_times):
+        # 播放次数
+        DektecManager.cmdline = DektecManager.cmdline + " -l " + str(loop_times)
+
+    def set_rate(self, rate):
+        # 输出码率
+        DektecManager.cmdline = DektecManager.cmdline + " -r " + str(rate)
+
+    def play_out(self):
+        self.DtPlay_exe_CMD(DektecManager.cmdline)
+
+    def play_out_wait(self):
+        self.DtPlay_exe_CMD_wait(DektecManager.cmdline)
+
+    def DtPlay_exe_CMD(self, str):
+        self.interrupt()
+        DektecManager.child = subprocess.Popen(DektecManager.DtPlay_exe + " " + str, stdout=subprocess.PIPE)
+        print DektecManager.child.stdout.read()
+
+    def DtPlay_exe_CMD_wait(self, str):
+        self.interrupt()
+        DektecManager.child = subprocess.Popen(DektecManager.DtPlay_exe + " " + str)
+        DektecManager.child.wait()
+
+
+    def help(self):
+        output = subprocess.check_output(DektecManager.DtPlay_exe + " " + "-?")
+        print "\n*******DtPlay_exe CMD help**********" + output
+
+    def __del__(self):
+        self.__class__.cmdline = ""
+        # if self.__class__.child is not None and self.__class__.child.poll() is None:
+        #     self.__class__.child.kill()
+        # print "DtPlay_exe exit!!!"
+        print "StreamCard_SCBC() exit!!!"
+
+        #for debug
+        # for i in range(0, 100):
+        #     print "testing..."
+        #     time.sleep(1)
+
+
+# ------------------------------------------------------------------------------
+if __name__ == "__main__":
+    scscbc = DektecManager()
+    scscbc.help()
+    streamFile = r"D:\test\482_1218_105425.ts"
+    times = 1
+    type = "DTMB"
+    channelPoint = "52.5MHz"
+    # scscbc.playStream( streamFile,  times,  type,  channelPoint)
+    # scscbc.help()
+    # scscbc.DtPlay_exe_CMD( streamFile + " -mt DTMB -mf 52.5MHz")
+
+    # 按照 SteamXpress 界面的设置
+    scscbc.set_playfile(streamFile)
+    scscbc.set_Device_type("")
+    scscbc.set_Modulation_carrier_frequency("578MHz")
+    scscbc.set_Modulation_type("DVBT")
+    # scscbc.set_Modulation_bandwidth("8")
+    # scscbc.set_Modulation_transmission_mode("8k")
+    # # scscbc.set_Modulation_constellation("QAM64")
+    # scscbc.set_Modulation_constellation("QPSK")
+    # scscbc.set_Modulation_guard_interval("1/32")
+    # scscbc.set_Modulation_Convolutional_rate("7/8")
+    # scscbc.set_Modulation_Output_level("-90dBm")#单位可以不写,前面的负号"-"一定要写
+    # scscbc.set_loop_times("0")
+
+    # scscbc.set_rate("18661765")
+
+    print scscbc.cmdline
+    # 频繁操作注意延迟3秒,画面才播放出来。
+    scscbc.play_out()

+ 39 - 0
ssat_sdk/device_manage/device_manager.py

@@ -0,0 +1,39 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+
+from ssat_sdk.service.service_config import *
+from multiprocessing.connection import Client
+
+class DeviceManager():
+    __instance = None
+    def __init__(self):
+        self.serverParser = ServiceConfig()
+        self.ccardClient = None
+        self.tg39Client = None
+        self.ub530Clinet = None
+
+    @staticmethod
+    def getInstance():
+        if DeviceManager.__instance == None:
+            DeviceManager.__instance = DeviceManager()
+        return DeviceManager.__instance
+
+    def getDevService(self,service_name):
+        if ("ccard" == service_name):
+            port = self.serverParser.getCCardListenerPort()
+            self.ccardClient = Client(("localhost", port), authkey="sat")
+            return self.ccardClient
+        elif ("tg39" == service_name):
+            port = self.serverParser.getTG39ListenerPort()
+            self.tg39Client = Client(("localhost", port), authkey="sat")
+            return self.tg39Client
+        elif ("c22293" == service_name):
+            port = self.serverParser.getC22293ListenerPort()
+            self.c22293Client = Client(("localhost", port), authkey="sat")
+            return self.c22293Client
+        elif ("ub530" == service_name):
+            port = self.serverParser.getUB530ListenerPort()
+            self.ub530Clinet = Client(("localhost", port), authkey="sat")
+            return self.ub530Clinet
+        else:
+            return None

+ 189 - 0
ssat_sdk/device_manage/sound_manager.py

@@ -0,0 +1,189 @@
+# -*- coding:utf-8 -*-
+import os
+import sys
+import time
+# from device_manager import *
+from ssat_sdk.utils import LoggingUtil
+from ssat_sdk.device_manage import AudioCapture
+
+
+class SoundManager():
+    # 设备是否已打开,成功为0;
+    IsOpen = -1
+    Service_Name = "sound_collection"
+
+    # 初始化
+    def __init__(self):
+        self.status = 0
+        if SoundManager.IsOpen <> 0:
+            SoundManager.IsOpen = AudioCapture.Initialize()
+
+        if SoundManager.IsOpen <> 0:
+            LoggingUtil.printLog(u"打开声音采集器失败")
+            self.status = 0
+            # return 0,u"打开声音采集器失败"
+        else:
+            LoggingUtil.printLog(u"打开声音采集器成功")
+            self.status = 1
+
+    # 是否静音(开始通道、采集时长、静音电压)
+    def IsMute(self, collectionTime, muteVoltage):
+        if SoundManager.IsOpen <> 0:
+            SoundManager.IsOpen = AudioCapture.Initialize()
+
+        percentMute = 0.0
+        if SoundManager.IsOpen <> 0:
+            LoggingUtil.printLog(u"声音采集器未打开")
+        else:
+            percentMute = AudioCapture.IsMute(
+                0, collectionTime, muteVoltage)
+        print u'IsMute:静音百分比:',percentMute
+        # 返回静音百分比;
+        return percentMute
+
+    # 检测声音是否有中断;
+    def IsInterrupt(self, collectionTime, muteVoltage, interruptTime):
+        if SoundManager.IsOpen <> 0:
+            SoundManager.IsOpen = AudioCapture.Initialize()
+
+        isInterrupt = False
+        if SoundManager.IsOpen <> 0:
+            LoggingUtil.printLog(u"声音采集器未打开")
+        else:
+            isInterrupt = AudioCapture.IsInterrupt(
+                0, collectionTime, muteVoltage, interruptTime)
+        print u'IsInterrupt:是否中断:',isInterrupt
+        # 返回是否中断;
+        return isInterrupt
+
+    # 检测左右声道是否正常;
+    def SoundTrackDetection(self, collectionTime, muteVoltage, interruptTime):
+        if SoundManager.IsOpen <> 0:
+            SoundManager.IsOpen = AudioCapture.Initialize()
+
+        nResult = 0
+        if SoundManager.IsOpen <> 0:
+            LoggingUtil.printLog(u"声音采集器未打开")
+        else:
+            nResult = AudioCapture.SoundTrackDetection(
+                0, collectionTime, muteVoltage, interruptTime)
+        print u'SoundTrackDetection:结果值',nResult
+        # 返回结果值;
+        return nResult
+
+    # 退出初始化环境
+    def __def__(self):
+        if SoundManager.IsOpen == 0:
+            SoundManager.IsOpen = AudioCapture.ExitInitialize()
+
+
+# 测试用例
+if __name__ == "__main__":
+    # 原接口调用测试
+    if 0:
+        result = AudioCapture.Initialize()
+        if result == 0:
+            print u"初始化成功"
+        else:
+            print u"初始化失败"
+
+        if result == 0:
+            print u"\n#是否静音->"
+            time.sleep(1)
+            percentMute = AudioCapture.IsMute( 0, 8000, 1.0)
+            print u"静音百分比:", percentMute
+
+            time.sleep(2)
+            print u"\n#是否有停顿->"
+            time.sleep(1)
+            bInterrupt = AudioCapture.IsInterrupt(0, 8000, 1.0, 400)
+            if bInterrupt == True:
+                print u"有停顿"
+            else:
+                print u"无停顿"
+
+    # 类接口调用;
+    if 0:
+        soundcollection = SoundManager()
+        if SoundManager.IsOpen == 0:
+            # 静音测试
+            if 1:
+                print u"\n#是否静音->"
+                time.sleep(1)
+                percentMute = soundcollection.IsMute( 3600000, 1.0)
+                print u"静音百分比:", percentMute
+
+            # 停顿测试
+            if 1:
+                time.sleep(2)
+                print u"\n#是否有停顿->"
+                time.sleep(1)
+                bInterrupt = soundcollection.IsInterrupt( 3600000, 1.0, 400)
+                if bInterrupt == True:
+                    print u"有停顿"
+                else:
+                    print u"无停顿"
+
+            # 声道检测
+            if 1:
+                print u"\n#声道检测"
+                nRet = soundcollection.SoundTrackDetection( 3600000, 1.0, 400)
+                if nRet == 0:
+                    print u"声道正常"
+                elif nRet == 1:
+                    print u"采集失败"
+                elif nRet == -1:
+                    print u"左右声道完全无音"
+                elif nRet == -2:
+                    print u"左声道无音"
+                elif nRet == -3:
+                    print u"右声道无音"
+                elif nRet == -4:
+                    print u"左声道停顿"
+                elif nRet == -5:
+                    print u"右声道停顿"
+                elif nRet == -6:
+                    print u"左右声道都停顿"
+    
+    # 压测;
+    if 1:
+        soundcollection = SoundManager()
+        if SoundManager.IsOpen == 0:
+            while True:
+                # 静音测试
+                if 1:
+                    print u"\n#是否静音->"
+                    percentMute = soundcollection.IsMute( 10000, 1.0)
+                    print u"静音百分比:", percentMute
+
+                # 停顿测试
+                if 1:
+                    print u"\n#是否有停顿->"
+                    bInterrupt = soundcollection.IsInterrupt( 10000, 1.0, 400)
+                    if bInterrupt == True:
+                        print u"有停顿"
+                    else:
+                        print u"无停顿"
+
+                # 声道检测
+                if 1:
+                    print u"\n#声道检测"
+                    nRet = soundcollection.SoundTrackDetection( 10000, 1.0, 400)
+                    if nRet == 0:
+                        print u"声道正常"
+                    elif nRet == 1:
+                        print u"采集失败"
+                    elif nRet == -1:
+                        print u"左右声道完全无音"
+                    elif nRet == -2:
+                        print u"左声道无音"
+                    elif nRet == -3:
+                        print u"右声道无音"
+                    elif nRet == -4:
+                        print u"左声道停顿"
+                    elif nRet == -5:
+                        print u"右声道停顿"
+                    elif nRet == -6:
+                        print u"左右声道都停顿"
+            #end while
+        #end if

+ 110 - 0
ssat_sdk/device_manage/tg39_manager.py

@@ -0,0 +1,110 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+from device_manager import *
+from ssat_sdk.utils import LoggingUtil
+from ssat_sdk import sat_environment
+from ssat_sdk.utils import string_util
+
+TV_SYSTEM_DICT = {"MN":0,"BG":1,"DK":2,"I":3,"L":4,"L'":5}
+COLOR_SYSTEM_DICT = {"NTSC-STD":0,"NTSC-50":1,"NTSC-443":2,"PAL-STD":3,"PAL-60":4,"PAL-M":5,"PAL-N":6,"SECAM":7}
+SYSTV = {
+            "NTSC M":       '0',  #TV Standard
+            "NTSC4.43 B/G": '1',
+            "NNTSC4.43 D/K":'2',
+            "NTSC4.43 I":   '3',
+            "PAL B/G":      '4',
+            "PAL I":        '5',
+            "PAL D":        '6',
+            "PAL-N":        '7',
+            "PAL-M":        '8',
+            "PAL D(CHINA)": '9',
+            "PAL B(AUS)":   '10',
+            "SECAM B/G":    '11',
+            "SECAM D/K/K1": '12',
+            "SECAM L":      '13',
+        }
+
+class TG39Manager():
+    # 当前选择的ATV码流仪;
+    ATV_SELECTIVE = sat_environment.getATVSelected()
+    def __init__(self):
+        self.devManager = DeviceManager.getInstance()
+        if string_util.strcmp(TG39Manager.ATV_SELECTIVE,'TG39'):
+            LoggingUtil.printLog("初始化TG39设备")
+        elif string_util.strcmp(TG39Manager.ATV_SELECTIVE,'54200'):
+            LoggingUtil.printLog("初始化54200设备")
+        elif string_util.strcmp(TG39Manager.ATV_SELECTIVE,'3116'):
+            LoggingUtil.printLog("初始化3116设备")
+
+    def setCTYandCHN(self, countryID, channelID):
+        """
+                RFCHA-arg1      For arg1, input the country name ID to set (as a decimal number).
+                RFCHB-arg2      For arg2, input the channel ID to set (as a decimal number).
+                countryID       channelID
+                1 JAPAN         1 to 62
+                2 USA           2 to 83
+                3 EU            1 to 69
+                4 CHINA         1 to 68
+                5 UK            21 to 69
+                6 R             1 to 69
+                7 FRANCE        1 to 69
+                8 AUSTRALIA     1 to 69
+                9 JAPAN-C       1 to 63
+                10 USA-C        2 to 150
+                11 EU-C         2 to 56
+                12 CHINA-C      1 to 51
+                13 ITY          1 to 69
+                14 IRE          1 to 69
+                """
+        if string_util.strcmp(TG39Manager.ATV_SELECTIVE,'TG39'):
+            self.setTG39CMD("RFCHA", countryID)
+            self.setTG39CMD("RFCHB", channelID)
+        elif string_util.strcmp(TG39Manager.ATV_SELECTIVE,'3116'):
+            pass
+
+    '''
+    freq: float, value:MHz
+    color_system:string,value:("NTSC-STD","NTSC-50","NTSC-443","PAL-STD","PAL-60","PAL-M","PAL-N","SECAM")
+    tv_system:string,value:(MN,BG,DK,I,L,L')
+    level:float, value:(29.0 to 109.0) (dBu V/75欧)
+    '''
+    def setChannel(self, freq, color_system, tv_system, level):
+        if string_util.strcmp(TG39Manager.ATV_SELECTIVE,'TG39'):
+            self.setTG39CMD("RFFREQ", freq) #freq: float MHz
+            colorCode = COLOR_SYSTEM_DICT[color_system]
+            self.setTG39CMD("VCOL", colorCode)
+            systemCode = TV_SYSTEM_DICT[tv_system]
+            self.setTG39CMD("RSYS", systemCode)
+            self.setTG39CMD("RFLEV", level)
+        elif string_util.strcmp(TG39Manager.ATV_SELECTIVE,'3116'):
+            self.setTG39CMD("SYSFR", freq)
+            self.setTG39CMD('SYSTV',SYSTV[color_system])
+            # 信号强度要与TG39使用同范围
+            # 3116:-80.00~0.00
+            # TG39:29.0~109.00
+            flevel = float(level)
+            if flevel <= 29.00:
+                level = '-80.0'
+            elif flevel >= 109.00:
+                level = '000.0'
+            else:
+                level = str(flevel - 109.0)
+            self.setTG39CMD('SYSLV',level)
+
+    '''
+    command:string, value:RS-232C command
+    param:no type, value:Content
+    '''
+    def setTG39CMD(self, command, param):
+        if string_util.strcmp(TG39Manager.ATV_SELECTIVE,'TG39'):
+            cmdLine = str(command) + " " + str(param)
+            self.sendCmd(cmdLine)
+        elif string_util.strcmp(TG39Manager.ATV_SELECTIVE,'3116'):
+            cmdLine = str(command) + "=" + str(param) + '\r'
+            self.sendCmd(cmdLine)
+
+    def sendCmd(self, cmdLine):
+        self.client = self.devManager.getDevService("tg39")
+        self.client.send(cmdLine)
+        print u"TG39指令执行结果:", self.client.recv()
+        self.client.close()

+ 27 - 0
ssat_sdk/device_manage/ub530_manager.py

@@ -0,0 +1,27 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+from device_manager import *
+from ssat_sdk.utils import LoggingUtil
+
+class UB530Manager():
+    def __init__(self):
+        self.devManager = DeviceManager.getInstance()
+        LoggingUtil.printLog("初始化UB530设备")
+
+    def takePicture(self, picPath):
+        command = "snapshoot::"
+        cmd = command + picPath
+        return self.sendCmd(cmd)
+
+    def sendCmd(self, cmdLine):
+        self.client = self.devManager.getDevService("ub530")
+        self.client.send(cmdLine)
+        ret = self.client.recv()
+        print u"UB530指令执行结果:", ret
+        self.client.close()
+        return ret
+
+if __name__ == "__main__":
+    ub530 = UB530Manager()
+    ub530.takePicture("D:/11.bmp")
+    ub530.takePicture("D:/12.jpg")

+ 543 - 0
ssat_sdk/ocr_convert.py

@@ -0,0 +1,543 @@
+# -*- coding:utf-8 -*-
+import sys, os, time
+
+reload(sys)
+sys.setdefaultencoding("utf-8")
+from TST.NetOCR import *
+from ssat_sdk.picture import image_util
+from ssat_sdk.utils import LoggingUtil
+from sat_environment import *
+from utils import string_util
+from picture.ocr_baidu import OCRBaidu
+from picture.ocr_tesseract import OCRTes
+from picture import ocr_tesseract
+import json
+import cv2 as cv
+import numpy as np
+
+
+EXP_Info = "ERR<Exp>"
+
+#所有语言定义
+OCR_LanDIC = ["chineseprc+english", "chinesetaiwan+english", "spanish", "chineseprc","chinesetaiwan","russian","french"
+              ,"english","vietnamese","hebrew","thai","arabic","portuguese","german","italian","japanese","korean"]
+
+'''
+OCR Type定义:
+abbyy:0~999
+tesseract:1000~9999
+baidu:10000~10100
+'''
+#泰彼OCR定义
+Abbyy_LanDIC = {"chineseprc+english": "ChinesePRC+English", "chinesetaiwan+english": "ChineseTaiwan+English",
+                "spanish": "Spanish",
+                "chineseprc": "ChinesePRC", "chinesetaiwan": "ChineseTaiwan", "russian": "Russian", "french": "French",
+                "english": "English", "vietnamese": "Vietnamese", "hebrew": "Hebrew", "thai": "Thai"
+                }
+ABBYY_BASIC = 0
+ABBYY_MAX = 999
+ABBYY_CONTRAST_ENABLE = ABBYY_BASIC
+ABBYY_CONTRAST_DISABLE = ABBYY_BASIC+1
+ABBYY_NORMAL = ABBYY_BASIC+2
+ABBYY_CONVERSION_ENGINE_ENABLE = ABBYY_BASIC+3
+ABBYY_INVERT_IMAGE_ENABLE = ABBYY_BASIC+4
+ABBYY_CONVERSION_AND_CONTRAST_ENABLE = ABBYY_BASIC+5
+ABBYY_ENGLISH_RECOMMENDATION_MODE = 253
+Abbyy_TypeList = [ABBYY_CONTRAST_ENABLE,ABBYY_CONTRAST_DISABLE,ABBYY_NORMAL,ABBYY_CONVERSION_ENGINE_ENABLE
+                  ,ABBYY_INVERT_IMAGE_ENABLE,ABBYY_CONVERSION_AND_CONTRAST_ENABLE,ABBYY_ENGLISH_RECOMMENDATION_MODE]
+
+#Tesseract OCR定义。没有Type
+Tes_LanDIC = ocr_tesseract.Tes_LanDIC
+Tes_BASIC = 1000
+Tes_MAX = 9999
+Tes_TypeList = [Tes_BASIC]
+
+#百度 OCR定义。
+Baidu_LanDIC = {"chineseprc+english": "CHN_ENG",
+                "spanish": "SPA","russian": "RUS",
+                "french": "FRE",
+                "english": "ENG", "portuguese": "POR", "german": "GER", "italian": "ITA", "japanese": "JAP", "korean": "KOR"}
+Baidu_BASIC = 10000
+Baidu_MAX = 10100
+Baidu_General = Baidu_BASIC
+Baidu_Accurate = Baidu_BASIC + 1
+Baidu_TypeList=[Baidu_General,Baidu_Accurate]
+
+OCR_TMP_DIR = getOCRTmpDir()
+OCR_ERR_DIR = getOCRErrDir()
+if not os.path.exists(OCR_TMP_DIR):
+    os.mkdir(OCR_TMP_DIR)
+if not os.path.exists(OCR_ERR_DIR):
+    os.mkdir(OCR_ERR_DIR)
+
+class OCRConvert():
+    OCR_PRODUCT_BAIDU = "baidu"
+    OCR_PRODUCT_ABBYY = "abbyy"
+    OCR_PRODUCT_TES = "tesseract"
+    def __init__(self):
+        self.timeout = 3
+        self.ocrBaidu = OCRBaidu()
+        self.ocrTes = OCRTes()
+
+    def setTimeOut(self, seconds):
+        self.timeout = seconds
+
+    '''
+    根据自然语言描述,和OCR Type,返回OCR识别语言的字符串定义
+    :param language: chineseprc+english/chinesetaiwan+english/spanish/chineseprc/chinesetaiwan/russian/french/english/vietnamese/hebrew
+                    /portuguese/german/italian/japanese/korean
+    :param type: OCR识别类型编号
+    :return OCR语言类别定义的字符串.如果在字典库找不到,直接返回传入的language。
+    '''
+    def getOCRLaParam(self, language, type):
+        if type <ABBYY_MAX and type >= ABBYY_BASIC:
+            if Abbyy_LanDIC.has_key(language.lower()):
+                return Abbyy_LanDIC[language.lower()]
+            else:
+                return language
+        elif type <Tes_MAX and type >= Tes_BASIC:
+            if Tes_LanDIC.has_key(language.lower()):
+                return Tes_LanDIC[language.lower()]
+            else:
+                return language
+        elif type < Baidu_MAX and type>=Baidu_BASIC:
+            if Baidu_LanDIC.has_key(language.lower()):
+                return Baidu_LanDIC[language.lower()]
+            else:
+                return language
+        else:
+            return language
+
+    """
+    根据传入的语言类型(无论自然语言类型,或者ocr语言类型),转换成目标自然语言类型
+    """
+    def getHumanLan(self, language, type):
+        if type < ABBYY_MAX and type >= ABBYY_BASIC:
+            if Abbyy_LanDIC.has_key(language):
+                return language
+            for humanLan in Abbyy_LanDIC:
+                # print "getHumanLan:", humanLan
+                if string_util.strcmp(Abbyy_LanDIC[humanLan], language):
+                    return humanLan
+            return ""
+        elif type < Tes_MAX and type >= Tes_BASIC:
+            if Tes_LanDIC.has_key(language):
+                return language
+            for humanLan in Tes_LanDIC:
+                # print "getHumanLan:",  humanLan
+                if string_util.strcmp(Tes_LanDIC[humanLan], language):
+                    return humanLan
+            return ""
+        elif type < Baidu_MAX and type >= Baidu_BASIC:
+            if Baidu_LanDIC.has_key(language):
+                return language
+            for humanLan in Baidu_LanDIC:
+                # print "getHumanLan:",  humanLan
+                if string_util.strcmp(Baidu_LanDIC[humanLan], language):
+                    return humanLan
+            return ""
+        else:
+            return ""
+
+    '''
+    根据传入的OCR 类型,返回厂家名字
+    :param type:OCR识别类型编号
+    :return OCR厂家名称
+    '''
+    def getOCRProductByType(self, type):
+        if type < ABBYY_MAX and type >= ABBYY_BASIC:
+            return self.OCR_PRODUCT_ABBYY
+        elif type < Tes_MAX and type >= Tes_BASIC:
+            return self.OCR_PRODUCT_TES
+        elif type < Baidu_MAX and type >= Baidu_BASIC:
+            return self.OCR_PRODUCT_BAIDU
+        else:
+            return ""
+
+    def sendPicToServer(self, picPath, lan, type):
+        try:
+            ocr = NetOCR(getOCRIpAddr(), getOCRPort(), picPath, lan, type)
+            # ocr.setTimeOut(self.timeout)
+            return ocr
+        except Exception, e:
+            print "OCR", u"SCBC OCR连接失败,Err:" ,e
+            return None
+
+    '''
+    在getStr函数基础上,添加了图片处理参数。处理图片后,再使用图片识别。
+    :param imgProcParams: 字典:{"Threshold":[127,250, 阈值类型], "contrast":[], "LaplaceSharp":[], "Noisy":[]}
+    :param picPath: 图片路径
+    :param lan :OCR识别用的语言类别,不同OCR产品的定义不一样
+    :param type:OCR识别的类型编号
+    :return 识别后的字符串。同getStr函数
+    '''
+    def getStrWithImgProcess(self, picPath, imgProcParams, lan, type, reconTimes = 5):
+        print "getStrWithImgProcess, param:", picPath, imgProcParams,lan,type,reconTimes
+        destPicPath = os.path.join(OCR_TMP_DIR, "ocr_"+str(time.time())+".png")
+        img = self.handleImage(picPath, imgProcParams)
+        cv.imwrite(destPicPath, img)
+        return self.getStr(destPicPath, lan, type)
+
+    '''
+    根据图片处理参数,处理图片,返回image对象
+    :param imgProcParams: 字典:{"Threshold":[127,250, 阈值类型], "contrast":[], "LaplaceSharp":[], "Noisy":[]}
+    :param picPath: 图片路径
+    :return 返回图片image对象
+    '''
+    def handleImage(self, picPath, imgProcParams):
+        print u"handleImage,imgProcParams:", imgProcParams
+        srcImg = cv.imread(picPath)
+        destPicPath = picPath
+        img = srcImg
+        if imgProcParams.has_key("Threshold"):
+            img = image_util.saveThresholdPicImg(srcImg, \
+                        imgProcParams["Threshold"][0], imgProcParams["Threshold"][1], imgProcParams["Threshold"][2])
+        return img
+
+    '''
+    根据传入的图片路径,获取图片的字符串。
+    :param picPath.图片的绝对路径
+    :param lan. OCR识别的文字语言类别。
+        type<10000:
+            ChinesePRC+English
+            ChineseTaiwan+English
+            Spanish
+            ChinesePRC
+            ChineseTaiwan
+            Russian
+            French
+            English
+            Vietnamese
+            Hebrew
+            Thai
+    :param type. OCR识别文字时的识别模型编号。
+    :param reconTimes. OCR识别文字异常时,重新尝试识别的次数,默认为5次,最高为10次。
+    :return str. 返回识别的字符串。未识别到,返回"",如果type未10000,10001表示采用百度OCR,返回字符串数组
+                如果属于字符识别异常:返回字符串"ERR<Exp>"
+    '''
+    def getStr(self, picPath, lan, type, reconTimes = 5):
+        print "getStr, param:", picPath, lan, type, reconTimes
+        lan = self.getOCRLaParam(lan, type)
+        if type < ABBYY_MAX and type >= ABBYY_BASIC:
+            ocr = self.sendPicToServer(picPath, lan, type)
+            if (ocr == None):
+                return ""
+            try:
+                LoggingUtil.printLog("OCR", u"泰彼 OCR")
+                string = ocr.getStr()
+                # print string
+                return string_util.toUTF8Str(string)
+            except Exception, e:
+                print u"OCR", u"获取文字失败.error:" ,e
+                return EXP_Info
+            finally:
+                ocr.close()
+        elif type >=Tes_BASIC and type < Tes_MAX:
+            return self.ocrTes.getStr(picPath, lan, type)
+        else:
+            # 超出范围的重连次数,都默认为10次;
+            if reconTimes > 10 or reconTimes < 0:
+                reconTimes = 10
+            # by zippo 把百度的返回数组转换成字符串,统一返回值
+            allStr = self.ocr_BaiduGS(picPath, type - 10000, lan)
+            while allStr is None or allStr == EXP_Info and reconTimes > 0:
+                LoggingUtil.printLog("OCR", u'百度OCR重连')
+                time.sleep(1)
+                reconTimes -= 1
+                allStr = self.ocr_BaiduGS(picPath, type - 10000, lan)
+            
+            return EXP_Info if allStr is None else allStr
+            # return self.ocr_Baidu(picPath, type-10000, lan)
+
+    # 字符串比对
+    def cmpOcrStr(self, ocrStr, stdStr, erase = [], picPath=None):
+        # print u"OCR_Convert:cmpOcrStr start param:", ocrStr, stdStr, erase, picPath if type(picPath) == type('') else 'path is imgObj'
+        if type(ocrStr) == type(u''):
+            ocrStr = str(ocrStr).encode('utf-8')
+        else:
+            try:
+                ocrStr = str(ocrStr).encode('utf-8')
+            except Exception:
+                pass
+
+        if type(stdStr) == type(u''):
+            stdStr = str(stdStr).encode('utf-8')
+        else:
+            try:
+                stdStr = str(stdStr).encode('utf-8')
+            except Exception:
+                pass
+        
+        # 去除空格;
+        ocrStr = ocrStr.replace(' ', '').lower()
+        stdStr = stdStr.replace(' ', '').lower()
+        # 移除指定字符;
+        for char in erase:
+            ocrStr = ocrStr.replace(char, '').lower()
+            stdStr = stdStr.replace(char, '').lower()
+            
+        #长度判断
+        if len(ocrStr) != len(stdStr):
+            self.saveOCRErr(ocrStr, stdStr, picPath)
+            return False
+
+        # 遍历字符串
+        result = True
+        # 忽略的相似字符;
+        ignore = [{'i','l','1','t','I','T'},{'o','0','O'}]
+        cnt = len(stdStr)
+        for i in range(0, cnt):
+            if stdStr[i] == ocrStr[i]:
+                continue
+            elif stdStr[i] in ignore[0] and ocrStr[i] in ignore[0]:
+                continue
+            elif stdStr[i] in ignore[1] and ocrStr[i] in ignore[1]:
+                continue
+            else:
+                result = False
+                break
+        #endfor
+        if result is False:
+            self.saveOCRErr(ocrStr,stdStr, picPath)
+        return result
+    #end
+
+    def saveOCRErr(self, ocrStr, stdStr, pic):
+        if pic is None or pic == '':
+            print u"Warn:Save OCR Error picture fail. <pic> is None or Empty"
+            return
+        destPicName = unicode(ocrStr + "_" + stdStr + ".png")
+        destPic = os.path.join(OCR_ERR_DIR, destPicName)
+        try:
+            if type(pic) == type(""):# 如果是路径;
+                shutil.copyfile(pic, destPic)
+            else:# 如果是图像数组;numpy.ndarray
+                cv.imwrite(destPic,pic)
+        except Exception,e:
+            print u"Warn:Save  OCR Error picture fail.",e.message
+
+    '''
+    根据传入的目标字符串stdStr,如果指定的OCR type可以识别到文字,则返回True和OCR字符串。
+    如果指定的OCR type识别不到文字,遍历所有OCR type方式识别文字,返回最终遍历结果。
+    :param stdStr : 目标字符串
+    :param picPath: 图片路径
+    :param lan :OCR识别用的语言类别,不同OCR产品的定义不一样
+    :param type:OCR识别的类型编号
+    :param imgProcParams: 字典:{"Threshold":[127,250, 阈值类型], "contrast":[], "LaplaceSharp":[], "Noisy":[]}
+    :param erase:erase 字符数组,文字比对时,需过滤掉的字符。
+    :return boolean,string: boolean标识是否成功(ture/false),string 是指定的OCR type识别到的文字。
+    '''
+    def findPicStr(self, stdStr, picPath, lan, type, imgProcParams={}, erase = []):
+        #匹配自然语言类型
+        humanLan = self.getHumanLan(lan, type)
+        ocrStr = self.getStrWithImgProcess(picPath, imgProcParams, lan, type, reconTimes = 1)
+        ret = self.cmpOcrStr(ocrStr, stdStr,erase, picPath)
+        return ret,ocrStr
+        if ret is True:
+            return True,ocrStr
+        for typeA in Abbyy_TypeList:
+            if type <> typeA:
+                ocrLan = self.getOCRLaParam(humanLan, typeA)
+                ocrStr = self.getStrWithImgProcess(picPath, imgProcParams, ocrLan, typeA, reconTimes=1)
+                ret = self.cmpOcrStr(ocrStr, stdStr, erase, "")
+                if ret is True:
+                    return True, ocrStr
+        for typeB in Baidu_TypeList:
+            if type <> typeB:
+                ocrLan = self.getOCRLaParam(humanLan, typeB)
+                ocrStr = self.getStrWithImgProcess(picPath, imgProcParams, ocrLan, typeB, reconTimes=1)
+                ret = self.cmpOcrStr(ocrStr, stdStr, erase, "")
+                if ret is True:
+                    return True, ocrStr
+        return False, ocrStr
+
+    # 指定ocr列表,遍历查找;
+    def findPicStrEx(self, stdStr, picPath, list_ocr, imgProcParams={}, erase = []):
+        result,ocrStr = False,''
+        for item in list_ocr:
+            ocrStr = self.getStrWithImgProcess(picPath, imgProcParams, item["lan"], item["type"], reconTimes = 1)
+            if stdStr.lower() in ocrStr.lower():
+                result = True
+                break
+        return result, ocrStr
+    '''
+    根据传入的图片路径,获取图片的字符串。
+    :param picPath.图片的绝对路径
+    :param lan. OCR识别的文字语言类别。
+    :param type. OCR识别文字时的识别模型编号。
+    :param area. OCR识别的图片区域。要求是图片刚好适配的矩形框区域。
+    :return str. 返回识别的字符串。未识别到,返回"".
+    '''
+
+    def getPositionStr(self, picPath, lan, type, area):
+        ocr = self.sendPicToServer(picPath, lan, type)
+        if (ocr == None):
+            return ""
+        try:
+            startX, startY, endX, endY = area
+            string = ocr.getPositionStr(startX, startY, endX, endY)
+            return string_util.toUTF8Str(string)
+        except Exception, e:
+            print "OCR", u"获取文字失败.error:",e
+            return EXP_Info
+        finally:
+            ocr.close()
+
+    '''
+   根据传入的图片路径,获取图片的字符串。
+   :param picPath.图片的绝对路径
+   :param lan. OCR识别的文字语言类别。
+   :param type. OCR识别文字时的识别模型编号。
+   :param keyword. OCR需要寻找的文字。
+   :return str. 返回识别的字符串。未识别到,返回[-1,-1,-1,-1],
+   '''
+
+    def getStrPosition(self, picPath, lan, type, keyword):
+        ocr = self.sendPicToServer(picPath, lan, type)
+        if (ocr == None):
+            return [-1, -1, -1, -1]
+        try:
+            area = ocr.getStrPosition(keyword)
+            return area
+        except Exception, e:
+            print "OCR", u"获取文字位置失败.error:",e
+            return [-1, -1, -1, -1]
+        finally:
+            ocr.close()
+
+    '''
+   根据传入的图片路径,获取图片的字符串。
+   :param picPath.图片的绝对路径
+   :param lan. OCR识别的文字语言类别。
+   :param type. OCR识别文字时的识别模型编号。
+   :param area. OCR识别的图片区域。
+   :return str. 返回识别的字符串。未识别到,返回"",
+   '''
+
+    def getAreaStr(self, picPath, lan, type, area):
+        ocr = self.sendPicToServer(picPath, lan, type)
+        if (ocr == None):
+            return ""
+        try:
+            startX, startY, endX, endY = area
+            string = ocr.getAreaStr(startX, startY, endX, endY)
+            return string_util.toUTF8Str(string)
+        except Exception, e:
+            print "OCR", u'获取文字失败.error:',e
+            return EXP_Info
+        finally:
+            ocr.close()
+
+    def close(self):
+        pass
+
+    '''
+    :param language:
+        识别语言类型,默认为CHN_ENG。可选值包括:
+        - CHN_ENG#中英文混合;
+        - ENG#英文;
+        - POR#葡萄牙语;
+        - FRE#法语;
+        - GER#德语;
+        - ITA#意大利语;
+        - SPA#西班牙语;
+        - RUS#俄语;
+        - JAP#日语;
+        - KOR#韩语
+    :param type:
+        0 basicGeneral;1 basicAccurate 高精度通用文字; 选择高精度时,language参数失效。
+    :return :
+        正常返回字符串数组,如果异常,返回['ERR<Exp>']
+    '''
+
+    def ocr_Baidu(self, pic_path, type, language="CHN_ENG"):
+        if type == 0:
+            ret = self.ocrBaidu.basicGeneral(pic_path, language)
+            if ret is None:
+                return [EXP_Info]
+            return ret
+        elif type == 1:
+            ret = self.ocrBaidu.basicAccurate(pic_path, language)
+            if ret is None:
+                return [EXP_Info]
+            return ret
+
+    '''
+    :param language:
+        识别语言类型,默认为CHN_ENG。可选值包括:
+        - CHN_ENG#中英文混合;
+        - ENG#英文;
+        - POR#葡萄牙语;
+        - FRE#法语;
+        - GER#德语;
+        - ITA#意大利语;
+        - SPA#西班牙语;
+        - RUS#俄语;
+        - JAP#日语;
+        - KOR#韩语
+    :param type:
+        0 basicGeneral;1 basicAccurate 高精度通用文字; 选择高精度时,language参数失效。
+    '''
+    def ocr_BaiduGS(self, pic_path, type, language="CHN_ENG"):
+        str1 = ""
+        strList = self.ocr_Baidu(pic_path, type, language="CHN_ENG")
+        for index in range(strList.__len__()):
+            strT = strList[index]
+            if index == 0:
+                str1 = strT
+                continue
+            str1 += " "+strT
+        return str1
+
+
+if __name__ == "__main__":
+    if 0:
+        # DIR = r"D:\ocr_err\\"
+        DIR = r"D:\temp-pic\gbk\\"
+        # picPath = DIR + "mi_4.png"
+        # picPath = DIR + "ocr_2.png"
+        picPath = DIR + "546473243246047508.png"
+        # DIR = u"D:/temp-pic/"
+        # picPath = DIR + "20180604113525.png"
+        # pic_path = r"D:\ocr_err\checkChannelList_crop.jpg"
+        pic_path = r"D:\ocr_err\11.png"
+        pic_path = r"D:\ocr_err\mi_1.png"
+        pic_path = r"D:\ocr_err\mi\5.png"
+        ocrCon = OCRConvert()
+        # allStr = ocrCon.getStr(pic_path,"english", 1000, -1)
+        allStr = ocrCon.getStr(pic_path,"chineseprc", 1000, -1)
+        print "ocr:",allStr
+        sys.exit(0)
+    if 0:
+        # s = u'林'
+        # print s.encode('utf-8')
+        # print s.encode('gbk')
+        ocrCon = OCRConvert()
+        ocrStr = u"1你好T0i"
+        stdStr = u"i你好io1"
+        result = ocrCon.cmpOcrStr(ocrStr, stdStr)
+        print u'结果',result
+
+        ocrStr = "1你好T0i"
+        stdStr = "i你好io1"
+        result = ocrCon.cmpOcrStr(ocrStr, stdStr)
+        print u'结果',result
+
+        ocrStr = "1你好T0i"
+        stdStr = u"i你好io1"
+        result = ocrCon.cmpOcrStr(ocrStr, stdStr)
+        print u'结果',result
+
+        # ocrStr = unicode("1你好T0i",'gbk')
+        # stdStr = u"i你好io1"
+        # print ocrStr,stdStr,type(ocrStr),type(stdStr)
+        # result = ocrCon.cmpOcrStr(ocrStr, stdStr)
+        # print u'结果',result
+    if 1:
+        ocr = OCRConvert()
+
+        pic = r'D:\temp-pic\UI\NT72-1\home.png'
+        # strAll = ocr.getStr(pic, "english", 253)
+        # print "strall:",strAll
+        position = ocr.getStrPosition(pic, "english", 253, "hdmi")
+        print "position:",position

+ 326 - 0
ssat_sdk/pic_tool.py

@@ -0,0 +1,326 @@
+#-*- coding:utf-8 -*-
+import sys
+reload(sys)
+sys.setdefaultencoding('utf-8')
+from PIL import Image
+from PIL import ImageChops
+from PIL import ImageDraw
+
+from picture import PreImageJudge
+
+from picture.DoubleImage import DoubleImage
+from picture import CrossColor
+from picture import RGB
+from picture.feature_detect import *
+from ssat_sdk.picture.color_space import *
+from ssat_sdk.picture.pq_detect import PQDetect
+import ssat_sdk.picture.image_util
+
+import cv2 as cv
+import numpy as np
+
+'''
+用于向脚本提供图片、图像内容处理接口,提供图像检测接口、图像比对接口
+'''
+class ImageCMP():
+    def __init__(self):
+        print "Init Image Compare tool"
+        self.featureDetect = FeatureDetect()
+        self.CIECaculator = CIEluvCaculator()
+        self.doubleImg = DoubleImage()
+        self.pq = PQDetect()
+
+
+
+    def getBlendImageCount(self, img):
+        print "getBlendImageCount,img1.size", img.size, ";format:", img.format, ";model:", img.mode
+        imgL = img.convert("L")
+        pixSum = imgL.histogram()
+        nonZeroCount = 0
+        for i in range(pixSum.__len__()):
+            if (pixSum[i] <> 0):
+                nonZeroCount = nonZeroCount + 1
+
+        print "getImageLArr,nonZeroCount=", nonZeroCount
+        print pixSum
+        return nonZeroCount
+
+    def checkLikePic(self, im1, im2):
+        im3 = ImageChops.invert(im2)
+        im4 = Image.blend(im1, im3, 0.5)
+        return self.getBlendImageCount(im4)
+
+    '''
+    参数,传入两张需要比对的图片的地址
+    返回值:True代表图片一致,False代表图片不一致
+    '''
+    def cmpPicTotal(self, picPath1, picPath2, threshold=20, expDotRate=0.001):
+        return self.doubleImg.cmpPicTotal(picPath1,picPath2, threshold, expDotRate)
+
+    '''
+    参数,传入两张需要比对的图片的对象
+    返回值:True代表图片一致,False代表图片不一致
+    '''
+    def cmpImgTotal(self, img1, img2, threshold=20, expDotRate=0.001):
+        print "cmpImgTotal,",img1.shape, img2.shape
+        return self.doubleImg.cmpImgTotal(img1, img2, threshold, expDotRate)
+
+    '''
+    # 参数,传入两张需要比对的图片的地址,采用颜色空间LUV进行颜色判断。
+    :param picPath1:第一张图片路径
+    :param picPath2:第二张图片路径
+    :param lineStd:luv差距值,相似度的阈值
+    :return 判断颜色是否一样,True 或者False
+    '''
+    def cmpPicLuv(self, picPath1, picPath2, lineStd = 5):
+        img1 = cv.imread(picPath1)
+        luv1 = self.CIECaculator.getAverageLUV(cv.cvtColor(img1, cv.COLOR_BGR2LUV))
+        img2 = cv.imread(picPath2)
+        luv2 = self.CIECaculator.getAverageLUV(cv.cvtColor(img2, cv.COLOR_BGR2LUV))
+        L, U, diff = self.CIECaculator.getDiffLevel(luv1,luv2)
+        print L, U, diff
+        if diff <= lineStd:
+            return True
+        else:
+            return False
+
+    '''
+    #裁剪原图片文件的area区域,并保存到destPath文件中
+    :param srcPic:图片文件路径
+    :param destPath:裁剪图片保存路径
+    :param area:(start_pointX,start_pointY,end_pointX, end_pointY). 原点:左上角,高为y轴,宽为x轴
+    :return True/False
+    '''
+    def saveCropPic(self,srcPic, destPath, area):
+        return image_util.saveCropPic(srcPic, destPath, area)
+
+    '''
+    #解决image的area区域,并保存到destPath文件中。srcImage是PIL里的Image对象。
+    :param srcImg:Opencv图片对象
+    :param area:(start_pointX,start_pointY,end_pointX, end_pointY). 原点:左上角,高为y轴,宽为x轴
+    :return True/False
+    '''
+    def saveCropImage(self, srcImg, destPath, area):
+        return image_util.saveCropImg(srcImg, destPath, area)
+
+
+    '''
+    裁剪非选中区域,返回image对象列表
+    :param srcPic:图片文件路径
+    :param area:选中区域
+    :return 列表:OpenCV图像对象,BGR颜色空间
+    '''
+    def saveRetPic(self,srcPic, area):
+        return image_util.saveRetCropPic(srcPic,area)
+
+    """
+    参数:
+    cv_img:数组,OpenCV常用的图像3维数组。例如:imread返回的值
+    area:元组,被检测的图片区域,例如:(start_pointX,start_pointY,end_pointX, end_pointY)
+    targetBGR:元组,应该达成的BGR数值。例如:(Blue,Green,Red)
+    返回值:
+    True:代表直线与目标BGR值符合。
+    False:代表直线与目标BGR值不符合。
+    """
+    def checkLineByImg(self, cv_img, area, targetBGR):
+        if cv_img == None:
+            return False
+        for x in range(area[0],area[2]):
+            for y in range(area[1], area[3]):
+                print x,y, cv_img[x,y]
+                if (abs(cv_img[x,y][0] - targetBGR[0]) > 20) \
+                        or (abs(cv_img[x,y][1] - targetBGR[1]) > 20) \
+                        or (abs(cv_img[x,y][2] <> targetBGR[3]) > 20):
+                    return False
+        return True
+    """
+    参数:
+    picpath  字符串,图片文件的路径
+    area:元组,被检测的图片区域,例如:(start_pointX,start_pointY,end_pointX, end_pointY)
+    targetBGR:元组,应该达成的BGR数值。例如:(Blue,Green,Red)
+    返回值:
+    True:代表直线与目标BGR值符合。
+    False:代表直线与目标BGR值不符合。
+    """
+    def checkLineByPath(self, picpath, area, targetBGR):
+        areaDXRY = self.coordinateRXDY2DXRY(area)
+        return self.checkLineByImg(cv.imread(picpath), areaDXRY, targetBGR)
+
+
+
+    def coordinateRXDY2DXRY(self, areaRXDY):
+        areaDXRY = []
+        areaDXRY.append(areaRXDY[1])
+        areaDXRY.append(areaRXDY[0])
+        areaDXRY.append(areaRXDY[3])
+        areaDXRY.append(areaRXDY[2])
+        return areaDXRY
+
+    def isDoubleImage(self,std_picpath,test_picpath):
+        return not (DoubleImage.cmpPicTotal(std_picpath,test_picpath))
+
+    '''
+    ::接口异常
+    '''
+    def isCrossColor(self,picpath):
+        return not (CrossColor.cmpPicTotal(picpath))
+
+    '''
+    判断一张图片是否是纯黑图片。
+    :param picpath:需要判断的图片路径
+    :param grayTH : 黑色判断的灰阶阈值
+    :param splitCount: 图片拆分的行列次数
+    :param expTh:黑色计算的容错区域比例。
+    '''
+    def isBlack(self,picpath, black_th=15,splitCount=10, expTh = 0.01):
+        #print "pic_tool.isBlack, picpath=", picpath
+        rgb = RGB.RGBColor()
+        img = cv.imread(picpath)
+        return rgb.isBlack(img, black_th, splitCount, expTh)
+
+    '''
+    获取区域的最大RGB值
+    :param picpath:图片路径。 area:图片区域坐标。
+    '''
+    def getMaxRGB(self,picpath,area):
+        return PreImageJudge.getMaxRGB(picpath,area)
+
+    '''
+    获取区域的BGR平均值
+    :param picpath:图片路径。 area:图片区域坐标。
+    '''
+    def getAverageRGB(self,picpath,area):
+        return PreImageJudge.getAverageRGB(picpath,area)
+
+    '''
+   获取区域的平均灰阶值
+   :param picpath:图片路径。 area:图片区域坐标。
+   '''
+    def getAvgGray(self, picpath, area):
+        return self.CIECaculator.getAvgGray(picpath, area)
+
+    '''
+    返回图片指定坐标的BGR颜色值
+    :param img: cv.imread()返回的图片对象
+    :param point坐标:(高,宽)
+    :return BGR颜色值:(blue,green,red)
+    '''
+    def getDotBGR(self,img, point):
+        return img[point[0], point[1]]
+
+    '''
+    检测图像是否有马赛克
+    '''
+    def hasMosaic(self,std_pic,test_pic):
+        return PreImageJudge.hasMosaic(std_pic,test_pic)
+
+    '''
+    ::接口废弃
+    '''
+    def cmpPicWithRGB(self,std_pic,test_pic):
+        return RGB.cmpPicWithRGB(std_pic,test_pic)
+
+    '''
+    用途:加载一张图片文件,获取指定长度的直线。可以指定图片区域,获取指定区域的直线
+    :param file:文件路径
+    :param lineMinLen:直线最短界限
+    :param lineMaxLen:直线最长界限
+    :return line数组:[[x1,y1,x2,y2],[...],...]
+    '''
+    def getLinesByFile(self, file, lineMinLen=20, lineMaxLen=1000, ableArea=None, threshold1=50, threshold2=100,
+                       apertureSize=5):
+        return self.featureDetect.getLinesByFile(file,lineMinLen,lineMaxLen,ableArea,threshold1,threshold2,apertureSize)
+
+    '''
+        用途:传入OpenCV读取的image图片对象,获取指定长度的直线。可以指定图片区域,获取指定区域的直线
+        :param file:文件路径
+        :param lineMinLen:直线最短界限
+        :param lineMaxLen:直线最长界限
+        :return line数组:[[x1,y1,x2,y2],[...],...]
+        '''
+    def getLinesByImg(self, img, lineMinLen=20, lineMaxLen=1000, ableArea=None, threshold1=50, threshold2=100,
+                 apertureSize=5):
+        return self.featureDetect.getLines(img,lineMinLen,lineMaxLen,ableArea,threshold1,threshold2,apertureSize)
+
+    '''
+    读取传入的图片路径,加载图片。
+    检测中心区域外的四周颜色是否为纯色图片,且四周纯色一致。
+    :param picPath:图片路径
+    :rate :四周在宽、高上的比例。
+    :return :检测结果。Ture/False
+    '''
+    def checkAroundSSColor(self, picPath, rate=0.2):
+        srcImg = cv.imread(picPath)
+        height,width,color = srcImg.shape
+        # print "checkAroundSSColor,height,width,color:",height,width,color
+        centerArea = [int(width*rate), int(height*rate), int(width*(1-rate)), int(height*(1-rate))]
+        imgList = image_util.saveRetCropImg(srcImg, centerArea)
+        for index in range(1,imgList.__len__()):
+            prevImg = imgList[index-1]
+            currentImg = imgList[index]
+            prevSingle = self.isSingleColor(prevImg)
+            currentSingle = self.isSingleColor(prevImg)
+            isSimilar = self.rgbColor.isColorSimilar(prevSingle[1],currentSingle[1])
+            # print "checkAroundSSColor:",prevSingle,currentSingle,isSimilar
+            self.rgbColor = None
+            if prevSingle[0] and currentSingle[0] and isSimilar:
+                continue
+            else:
+                return False
+        return True
+
+    '''
+    比较bgr1和bgr2颜色是否一样
+    '''
+    def isColorSimilar(self, bgr1, bgr2):
+        rgbColor = RGB.RGBColor()
+        return rgbColor.isColorSimilar(bgr1,bgr2)
+
+    '''
+    判断图片对象是否为纯色图片
+    :param img: OpenCV的mat对象
+    :return 检测结果:[Ture/False, [BGR平均值]]
+    '''
+    def isSingleColor(self, img):
+        self.rgbColor = RGB.RGBColor()
+        return self.rgbColor.isSingleColor(img)
+
+    '''
+    根据传入的图片路径,计算图片色温.图片必须是单色图片
+    :param picPath,图片路径
+    '''
+    def calculateCCT(self, picPath):
+        return self.pq.calculateCCT(picPath)
+
+    '''
+    根据传入的图片路径,计算图片清晰度。
+    :param picPath,图片路径
+    '''
+    def detectSharpness(self, picPath):
+        return self.pq.detSharpTenengrad(picPath)
+
+if __name__ == "__main__":
+    imgCmp = ImageCMP()
+    #bled = r"D:\TST\test1\blend.jpg"
+    #diffPath = r"D:\TST\test1\diff.jpg"
+    #pic1 = r"D:\TST\test1\contrast-10.jpg"
+    #pic2 = r"D:\TST\test1\brightness+10.jpg"
+
+    #imgCmp.cmpPicTotal(pic1,pic2)
+
+    #pic3= r"D:\TST\image\65-601.jpg"
+    #pic4= r"D:\TST\image\65-603.jpg"
+    #imgCmp.cmpPicTotal(pic3,pic4)
+
+    picPath = r"D:\1.png"
+    imgCmp.calulateCCT(picPath)
+    # mat = np.array(cv.imread(picPath))
+    # print mat
+    # PreImageJudge.getMaxRGB(picPath,(0,0,100,200))
+    # cv.imshow("test",np.array(mat))
+    # rxdyArea = imgCmp.coordinateRXDY2DXRY([0,0,200,300])
+    # mat5 = mat[rxdyArea[0]:rxdyArea[2],rxdyArea[1]:rxdyArea[3]]
+    # cv.imshow("test5",np.array(mat5))
+    # cv.waitKey(0)
+    # cv.destroyAllWindows()
+    # print imgCmp.checkLineByPath(mat5, (507,200,798,200), (255,0,0))

+ 89 - 0
ssat_sdk/picture/CrossColor.py

@@ -0,0 +1,89 @@
+#coding=utf-8
+'''颜色串色检测'''
+import cv2 as cv
+import numpy as np
+
+#裁剪图片
+def cutImage(img_addr):
+    img = cv.imread(img_addr)
+    img_arr = np.array(img)
+    hight = img_arr.shape[0]
+    wight = img_arr.shape[1]
+    dest_img = img_arr[hight/2+20:hight,0:wight,:]
+    return dest_img
+
+#得到横坐标点
+def getSpot(dest_img):
+    edge_img_rgb  = cv.convertScaleAbs(cv.Sobel(dest_img,cv.CV_16S,1,0))
+    edge_img_gray = cv.cvtColor(edge_img_rgb,cv.COLOR_BGR2GRAY)
+    _,edge_img = cv.threshold(edge_img_gray,50,255,cv.THRESH_BINARY)
+    #做一条水平线
+    tumple1=[]
+    tumple = []
+    for y in range(0,edge_img.shape[1],1):
+        if edge_img[70][y] == 255:
+            tumple1.append(y)
+            tumple.append(y)
+    count = 0
+    for i in range(0,len(tumple1)):
+        if i != len(tumple1)-1:
+            if tumple1[i+1] - tumple1[i]>15:
+                if count == 0:
+                    tumple.insert(i+1,".")
+                else :
+                    tumple.insert(count+i+1,".")
+                count= count +1
+    tumple.insert(len(tumple),".")
+    lines =[]
+    recode = 0
+    for i in range(0,len(tumple),1):
+        if tumple[i] == "." :
+            if recode == 0:
+                temp_arr = tumple[recode:i]
+                recode = i
+            else:
+                temp_arr = tumple[recode+1:i]
+                recode = i
+            lines.append(sum(temp_arr)/len(temp_arr))
+    return lines
+
+#输入图片地址,返回拆分图片
+def  getPartImage(img_addr):
+    dest_img = cutImage(img_addr)
+    lines = getSpot(dest_img)
+    images = []
+    for i in range(0,len(lines)):
+        if i ==0:
+            images.append(dest_img[0:dest_img.shape[0],0:lines[i]-5,:])
+        else:
+            if i == len(lines)-1:
+                images.append(dest_img[0:dest_img.shape[0],lines[i]+5:dest_img.shape[1]-5,:])
+            images.append(dest_img[0:dest_img.shape[0],lines[i-1]+5:lines[i]-5,:])
+    return images
+# 对外提供方法
+def cmpPicTotal(picpath1):
+    images = getPartImage(picpath1)
+    if len(images) != 8:
+        print "彩色分离异常"
+    else:
+        for image in images:
+            wight = image.shape[1]
+            hight = image.shape[0]
+            B, G, R = cv.split(image)
+            sum_B = 0
+            sum_G = 0
+            sum_R = 0
+            for x in range(0, hight, 1):
+                for y in range(0, wight, 1):
+                    sum_B = sum_B + B[x][y]
+                    sum_G = sum_G + G[x][y]
+                    sum_R = sum_R + R[x][y]
+                    avg_B = sum_B / (wight * hight)
+                    avg_G = sum_G / (wight * hight)
+                    avg_R = sum_R / (wight * hight)
+            for x in range(0, hight, 1):
+                for y in range(0, wight, 1):
+                    if abs(image[x][y][0] - avg_B)>20 or abs(image[x][y][1] - avg_G) >20 or abs(image[x][y][2] -avg_R) >20:
+                        return False
+                    else:
+                        return True

+ 94 - 0
ssat_sdk/picture/DoubleImage.py

@@ -0,0 +1,94 @@
+# encoding: utf-8
+'''
+用于两张图片比对检测
+'''
+import cv2 as cv
+import numpy as np
+import random
+# from matplotlib import pyplot as plt
+
+GRAY_LEVEL = 255
+
+class DoubleImage():
+    def __init__(self):
+        pass
+
+    #裁剪图片
+    def cutImage(self, img_addr):
+        img = cv.imread(img_addr)
+        img_arr = np.array(img)
+        hight = img_arr.shape[0]
+        wight = img_arr.shape[1]
+        dest_img = img_arr[hight-280:hight-180,wight-280:wight-180]
+        return dest_img
+
+
+    '''
+    比较两张图片,如果根据threshold设定的容错值,有1%的点超过范围,则图片不一致。
+    :param picPath1:第一个参数为为标准图地址,
+    :param picPath2:第二个参数为测试图地址
+    :param threshold: RGB颜色偏差容错范围
+    '''
+    def cmpPicTotal(self, picPath1, picPath2, threshold=20, expDotRate=0.01):
+        srcImg1 = cv.imread(picPath1)
+        srcImg2 = cv.imread(picPath2)
+        return self.cmpImgTotal(srcImg1, srcImg2, threshold, expDotRate)
+
+    '''
+    比较两张图片,如果根据threshold设定的容错值,有1%的点超过范围,则图片不一致。
+    :param picPath1:第一个参数为为标准图对象,
+    :param picPath2:第二个参数为测试图对象
+    :param threshold: RGB颜色偏差容错范围
+    '''
+    def cmpImgTotal(self, srcImg1, srcImg2, threshold=20, expDotRate=0.01):
+        shape = srcImg2.shape
+        dstImg = srcImg1 - srcImg2
+        cv.imwrite("D:/dstImg.png", dstImg)
+        bArr = dstImg[:,:,0].ravel()
+        gArr = dstImg[:,:,1].ravel()
+        rArr = dstImg[:,:,2].ravel()
+        dotSum = shape[0]*shape[1]+0.0
+        bCount = np.where((bArr>threshold)&(bArr<=255))[0].__len__()
+        gCount = np.where((gArr>threshold)&(gArr<=255))[0].__len__()
+        rCount = np.where((rArr>threshold)&(rArr<=255))[0].__len__()
+        print "cmpImgTotal BGR点异常个数比例:", bCount/dotSum,gCount/dotSum,rCount/dotSum
+        if bCount/dotSum>expDotRate or gCount/dotSum > expDotRate or rCount/dotSum>expDotRate:
+            return False
+        else:
+            return True
+
+    '''
+    缺少衡量等级数字量化
+    '''
+    def cmpORB(self, im1, im2):
+        orb = cv.ORB_create()
+        kp = orb.detect(im1, None)
+        kp, des1 = orb.compute(im1,kp)
+        print "des1:",des1.__len__()
+        # draw only keypoints location,not size and orientation
+        # img1 = cv.drawKeypoints(im1, kp, None, color=(0, 255, 0), flags=0)
+        # plt.imshow(img1), plt.show()
+
+        orb = cv.ORB_create()
+        kp2 = orb.detect(im2, None)
+        kp2, des2 = orb.compute(im2, kp2)
+        print "des2:",des2.__len__()
+        # draw only keypoints location,not size and orientation
+        # img2 = cv.drawKeypoints(im2, kp, None, color=(0, 255, 0), flags=0)
+        # plt.imshow(img2), plt.show()
+
+if __name__ == '__main__':
+
+    dImg = DoubleImage()
+    # dImg.cmpPicTotal('flower.jpg', 'flower.jpg')
+    im1 = cv.imread('std.jpg')
+
+    im2 = cv.imread('test.jpg')
+    # psnr = dImg.psnr(im1, im2)
+    # print "im1,im2 psnr:", psnr
+    # dImg.cmpHist(im1 , im2)
+    dImg.cmpORB(im1,im2)
+
+
+
+

+ 155 - 0
ssat_sdk/picture/PreImageJudge.py

@@ -0,0 +1,155 @@
+# encoding: utf-8
+import cv2 as cv
+import numpy as np
+import random
+
+from ssat_sdk.picture.RGB import RGBColor
+
+'''
+用于图片初级检测,例如:颜色、噪声
+此类已经废弃
+'''
+def getMaxRGB(imgFile,area):
+    img = cv.imread(imgFile,1)
+    if (area is None):
+        area = (0,0,img.shape[1], img.shape[0])#mat shape:高、宽、颜色值个数
+    img = cv.cvtColor(img,cv.COLOR_BGR2RGB)
+    img = np.array(img)
+    if len(area) == 4:
+        rgbColor = RGBColor()
+        x1 = area[0]
+        y1 = area[1]
+        x2 = area[2]
+        y2 = area[3]
+        img = img[y1:y2+1,x1:x2+1]
+        return rgbColor.getMaxBGR(img)
+
+    else:
+        return None,None,None
+
+'''
+截取图片区域RGB平均值。
+:param area:二维区域。坐标系:x轴:水平向右,y轴:纵向向下
+'''
+def getAverageRGB(img_addr, area = None):
+    img = cv.imread(img_addr)
+    return getImgAverageRGB(img, area)
+
+'''
+截取图片区域RGB平均值。
+:param area:二维区域。坐标系:x轴:水平向右,y轴:纵向向下
+'''
+def getImgAverageRGB(img, area):
+    if (area is None):
+        area = (0,0,img.shape[1], img.shape[0])#mat shape:高、宽、颜色值个数
+    print "getImgAverageRGB", img.shape, area
+    # print img.shape
+    img = cv.cvtColor(img,cv.COLOR_BGR2RGB)
+    img = np.array(img)
+    if len(area) == 4:
+        rgbColor = RGBColor()
+        x1 = area[0]
+        y1 = area[1]
+        x2 = area[2]
+        y2 = area[3]
+        img = img[y1:y2, x1:x2]
+        return rgbColor.getAvgBGR(img)
+
+    else:
+        return None, None, None
+
+'''@废弃'''
+# 判断黑屏
+def isBlack(img_addr, black_th=15):
+    gray = cv.imread(img_addr,0)
+    width = gray.shape[1]
+    hight = gray.shape[0]
+
+    for i in range(0,50):
+        random_y = random.randint(0,width-1)
+        random_x = random.randint(0,hight-1)
+        if gray[random_x][random_y] >= black_th:
+            return False
+        else:
+            continue
+    return True
+
+
+ #判断噪点
+def hasNoise(img_addr):
+    pass
+
+
+
+def getPoints(img_addr):
+    kernel = np.ones((11, 11), dtype=np.uint8)
+    img = cv.imread(img_addr, 1)
+    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
+    gray = gray[gray.shape[0]/2:gray.shape[0],gray.shape[1]/2:gray.shape[1]]
+    x = cv.Sobel(gray, cv.CV_16S,1, 0)
+    y = cv.Sobel(gray, cv.CV_16S, 0, 1)
+    absX = cv.convertScaleAbs(x)   # 转回uint8
+    absY = cv.convertScaleAbs(y)
+    dst = cv.addWeighted(absX, 0.5, absY, 0.5,0)
+    _, dst_2 = cv.threshold(dst, 100, 255, cv.THRESH_BINARY)
+    img_close = cv.erode(cv.dilate(dst_2, kernel), kernel)
+    image, contours, hierarchy = cv. findContours(img_close, 1, 2)
+    true_rectang = []
+    for i in range(0,len(contours)):
+        rectang = []
+        temp_arr = []
+        for j in range(0,len(contours[i])):
+            for points_index in range(0, len(contours[i][j])):
+                temp_arr.append(contours[i][j][0])
+        for temp_arr_index in range(0, len(temp_arr)):
+            if temp_arr_index == 0:
+                # 第一个点加入数组
+                rectang.append(temp_arr[temp_arr_index])
+            else:
+                # 遍历所有点
+                flag = 0
+                for rectang_index in range(0, len(rectang)):
+                    if (abs(temp_arr[temp_arr_index][0]-rectang[rectang_index][0]) >= 15) or (abs(temp_arr[temp_arr_index][1]-rectang[rectang_index][1]) >= 15):
+                       flag = flag + 1
+                    if flag == len(rectang):
+                        rectang.append(temp_arr[temp_arr_index])
+        if len(rectang) == 4:
+            # 判断是否我们需要的矩形 ,如果是 则返回到true_rectang
+            true_rectang.append(rectang)
+    return true_rectang
+
+
+'''
+@废弃
+判断马赛克
+'''
+def hasMosaic(std_pic,test_pic,num):
+
+
+    std_pic = getPoints(std_pic)
+    test_pic = getPoints(test_pic)
+
+    print len(std_pic)
+    print len(test_pic)
+
+    if len(test_pic) != len(std_pic):
+        return True
+    else:
+        return False
+
+class PreImageJudge():
+    def __init__(self):
+        self.BGR = RGBColor()
+
+    '''
+    判断一张图片是否是纯黑图片。
+    :param img:需要判断的图片
+    :param grayTH : 黑色判断的灰阶阈值
+    :param splitCount: 图片拆分的行列次数
+    :param expTh:黑色计算的容错区域比例。
+    '''
+    def isBlack(self, img, grayTH = 70,splitCount=10, expTh = 0.01):
+        return self.BGR.isBlack()
+
+if __name__ == '__main__':
+    print hasMosaic('std.jpg','test.jpg',num=None)

+ 193 - 0
ssat_sdk/picture/RGB.py

@@ -0,0 +1,193 @@
+# encoding: utf-8
+
+import cv2 as cv
+import numpy as np
+import math
+
+#裁剪图片
+def cutImage(img_addr):
+    img = cv.imread(img_addr)
+    img_arr = np.array(img)
+    hight = img_arr.shape[0]
+    wight = img_arr.shape[1]
+    dest_img = img_arr[hight/2+20:hight,wight/2:wight,:]
+
+    return dest_img
+
+
+def cmpPicWithRGB(picPath1,picPath2):
+    std_img = cutImage(picPath1)
+
+    test_img = cutImage(picPath2)
+
+    width = std_img.shape[0]
+    hight = test_img.shape[1]
+
+    std_B, std_G, std_R = cv.split(std_img)
+    test_B, test_G, test_R = cv.split(std_img)
+
+    sum_std_B = 0
+    sum_std_G = 0
+    sum_std_R = 0
+    for x in range(0,width,1):
+        for y in range(0,hight,1):
+            sum_std_B = sum_std_B + std_B[x][y]
+            sum_std_G = sum_std_G + std_G[x][y]
+            sum_std_R = sum_std_R + std_R[x][y]
+    avg_std_B = sum_std_B / (width*hight)
+    avg_std_G = sum_std_G / (width*hight)
+    avg_std_R = sum_std_R / (width*hight)
+    for x in range(0,width,1):
+        for y in range(0,hight,1):
+            if abs(test_B[x][y] - avg_std_B) >10 or abs(test_G[x][y] - avg_std_G) > 10 or abs(test_R[x][y] - avg_std_R[x][y] > 10):
+                print test_B[x][y]
+                print test_G[x][y]
+                print test_R[x][y]
+                print avg_std_B
+                print avg_std_R
+                print avg_std_G
+                return False
+            else:
+                return True
+
+class RGBColor():
+    def __init__(self):
+        pass
+
+    '''
+    BGR颜色差值小于offset值时,定性为灰阶色调。
+    '''
+    def isGray(self, bgr, offset = 10):
+        # print "RGB isGray:", bgr,abs(bgr[0] - bgr[1])
+        if abs(bgr[0] - bgr[1]) < offset \
+                and abs(bgr[1] - bgr[2]) < offset\
+                and abs(bgr[2] - bgr[0]) < offset:
+            return True
+        else:
+            return False
+
+    '''
+    传入的图片对象,必须是BGR颜色空间。
+    根据BGR颜色值,判断是否为纯色图片。同时忽略极少的噪点干扰。
+    :param img:传入的图片对象, BGR颜色空间
+    :param expTh: 颜色平均值偏差范围,> expTh 则是False。
+    :return: [判断结果True/False,颜色值]
+    '''
+    def isSingleColor(self, img, expTh=0.01, splitCount=10):
+        height, width, color = img.shape
+        heightStep = height/splitCount
+        widthStep = width/splitCount
+        totalAvg = self.getAvgBGR(img)
+        expCount = 0.0
+        for heightCount in range(0,splitCount):
+            for widthCount in range(0, splitCount):
+                height0 = heightStep*heightCount
+                width0 = widthStep*widthCount
+                height1 = height0+heightStep
+                width1 = width0+widthStep
+                scaleAvg = self.getAvgBGR(img[height0:height1, width0:width1])
+                ret = self.isColorSimilar(totalAvg, scaleAvg, faultTolerance=40)
+                if ret is False:
+                    expCount += 1
+        expRate = expCount/(splitCount*splitCount)
+        print "isSingleColor,expRate:",expRate,expCount
+        if expRate > expTh:
+            return False, totalAvg
+        else:
+            return True, totalAvg
+
+    def getAvgBGR(self, img):
+        imgB = img[:,:,0]
+        imgG = img[:,:,1]
+        imgR = img[:,:,2]
+
+        avgB = np.average(imgB)
+        avgG = np.average(imgG)
+        avgR = np.average(imgR)
+        return avgB,avgG,avgR
+
+    def getMaxBGR(self, img):
+        imgB = img[:, :, 0]
+        imgG = img[:, :, 1]
+        imgR = img[:, :, 2]
+
+        maxB = np.max(imgB)
+        maxG = np.max(imgG)
+        maxR = np.max(imgR)
+        return maxB, maxG, maxR
+
+    '''
+    判断一张图片是否是纯黑图片。
+    :param img:需要判断的图片
+    :param grayTH : 黑色判断的灰阶阈值
+    :param splitCount: 图片拆分的行列次数
+    :param expTh:黑色计算的容错区域比例。> expTh 则是False。
+    '''
+    def isBlack(self, img, grayTH = 70,splitCount=10, expTh = 0.01):
+        height, width, color = img.shape
+        heightStep = height / splitCount
+        widthStep = width / splitCount
+        totalAvg = self.getAvgBGR(img)
+        expCount = 0.0
+        for heightCount in range(0, splitCount):
+            for widthCount in range(0, splitCount):
+                height0 = heightStep * heightCount
+                width0 = widthStep * widthCount
+                height1 = height0 + heightStep
+                width1 = width0 + widthStep
+                scaleAvg = self.getAvgBGR(img[height0:height1, width0:width1])
+                ret = self.isGray(scaleAvg)
+                #print "rgb isBlack,gray ret:", ret
+                if ret is False or scaleAvg[1] > grayTH:
+                    expCount += 1
+        expRate = expCount / (splitCount * splitCount)
+        print "isBlack,expRate:", expRate, expCount
+        if expRate > expTh:
+            return False
+        else:
+            return True
+
+
+    '''
+            # 描述:bgr色差计算。ΔE=( ΔL^2 + ΔA^2 + ΔB^2 ) ^ (1/2) ΔE
+            # 参数:color1、color2: bgr格式
+            # 返回值:
+            #   相差值,越小越相近。
+            #   0~50(微小色差),感觉极微;
+            #   50~150(小色差),感觉轻微;
+            #   150~300(较小色差),感觉明显;
+            #   300~600(较大色差),感觉很明显;
+            #   600以上(大色差),感觉强烈。
+            # '''
+
+    def colorDistance(self, color1, color2):
+        b1, g1, r1 = color1
+        b2, g2, r2 = color2
+        rmean = (r1 + r2) / 2
+        R = int(r1) - int(r2)
+        G = int(g1) - int(g2)
+        B = int(b1) - int(b2)
+        return math.sqrt((2 + rmean / 256) * (R ** 2) + 4 * (G ** 2) + (2 + (255 - rmean) / 256) * (B ** 2))
+
+    '''
+        # 描述:颜色是否相近。
+        # 参数:
+        #   color1、color2: bgr格式
+        #   faultTolerance: 容错值大小,表示两颜色的色差值
+        # 返回值:<= faultTolerance返回True,否则返回False.
+        #
+        # '''
+
+    def isColorSimilar(self, color1, color2, faultTolerance=100):
+        if 0:
+            b1 = color1[0] in range(color2[0] - faultTolerance, color2[0] + faultTolerance)
+            b2 = color1[1] in range(color2[1] - faultTolerance, color2[1] + faultTolerance)
+            b3 = color1[2] in range(color2[2] - faultTolerance, color2[2] + faultTolerance)
+
+            if b1 == True and b2 == True and b3 == True:
+                return True
+            else:
+                return False
+        if 1:
+            val = self.colorDistance(color1, color2)
+            return True if val <= faultTolerance else False

+ 1 - 0
ssat_sdk/picture/__init__.py

@@ -0,0 +1 @@
+# encoding: utf-8

+ 241 - 0
ssat_sdk/picture/cie_luv.py

@@ -0,0 +1,241 @@
+# -*- coding:utf-8 -*-
+'''
+颜色空间CIE LUV
+'''
+import os, sys, time
+import cv2 as cv
+import numpy as np
+import colorsys
+import math
+class LUV():
+    def __init__(self, stdimg = None, testimg=None):
+        self.stdimg = stdimg
+        self.testimg = testimg
+
+    def setSTDImage(self, img):
+        self.stdimg = img
+
+    def setTestImage(self ,img):
+        self.testimg = img
+
+    '''
+    计算单个指定区域的LUV平均值
+    '''
+    def getAverageLUV(self, img, area=None):
+        if (area is None):
+            area = (0, 0, img.shape[1], img.shape[0])  # mat shape:高、宽、颜色值个数
+        # print "getAverageLUV", img.shape, area
+        # print img.shape
+        img = np.array(img)
+        if len(area) == 4:
+            x1 = area[0]
+            y1 = area[1]
+            x2 = area[2]
+            y2 = area[3]
+            img = img[y1:y2+1, x1:x2+1]
+            # img = img[0:80, 0:100]
+            # print img.shape
+            total_L = 0
+            total_U = 0
+            total_V = 0
+            pixel_total = img.shape[0] * img.shape[1]
+            # print "pixel_total:",pixel_total
+
+            for x in range(0, img.shape[0]):
+                for y in range(0, img.shape[1]):
+                    total_L = total_L + img[x][y][0]
+                    total_U = total_U + img[x][y][1]
+                    total_V = total_V + img[x][y][2]
+            return total_L / pixel_total, total_U / pixel_total, total_V / pixel_total
+
+        else:
+            return None, None, None
+
+    '''
+    计算所有区域的LUV平均值
+    '''
+    def getMAverageLUV(self, img, areaList):
+        total_L ,total_U, total_V, pixel_total = 0 ,0 ,0 ,0
+        for area in areaList:
+            L ,U ,V ,P = self.countTotalPLuv(img, area)
+            total_L += L
+            total_U += U
+            total_V += V
+            pixel_total += P
+            # print "getMAverageLUV:",area,L,U,V,P
+        if (pixel_total == 0):
+            return None, None, None
+        return total_L /pixel_total ,total_U /pixel_total ,total_V /pixel_total
+
+    '''
+    计算整张图片指定区域Luv颜色值总和。
+    '''
+    def countTotalPLuv(self, img, area=None):
+        if (area is None):
+            area = (0, 0, img.shape[1], img.shape[0])  # mat shape:高、宽、颜色值个数
+        # print "getAverageLUV", img.shape, area
+        # print img.shape
+        img = np.array(img)
+        if len(area) == 4:
+            x1 = area[0]
+            y1 = area[1]
+            x2 = area[2]
+            y2 = area[3]
+            img = img[y1:y2 +1, x1:x2 +1]
+            # img = img[0:80, 0:100]
+            # print img.shape
+            total_L = 0
+            total_U = 0
+            total_V = 0
+            pixel_total = img.shape[0] * img.shape[1]
+            # print "pixel_total:",pixel_total
+
+            for x in range(0, img.shape[0]):
+                for y in range(0, img.shape[1]):
+                    total_L = total_L + img[x][y][0]
+                    total_U = total_U + img[x][y][1]
+                    total_V = total_V + img[x][y][2]
+            return total_L ,total_U , total_V , pixel_total
+
+        else:
+            return None, None, None, None
+
+    def getDiffLevel(self, stdLuv, testLuv):
+        return self.getDiffUV(stdLuv, testLuv)
+
+    def getDiffuvLevel(self, stdLuv, testLuv):
+        diff = self.getDiffuv(stdLuv, testLuv)
+        return diff[2]
+
+    def getDiffluvLevel(self ,stdLuv, testLuv):
+        return self.getDiffLUV(stdLuv, testLuv)
+
+    def getDiffUV(self, stdLuv, testLuv):
+        tmp = np.int32(testLuv[0]) - np.int32(stdLuv[0])
+        lDiff = abs(tmp)
+        tmp = np.int32(testLuv[1]) - np.int32(stdLuv[1])
+        uDiff = abs(tmp)
+        tmp = np.int32(testLuv[2]) - np.int32(stdLuv[2])
+        vDiff = abs(tmp)
+        lineDiff = np.sqrt(np.square(uDiff )+ np.square(vDiff))
+        return uDiff ,vDiff ,lineDiff
+
+    def getDiffuv(self, stdLuv, testLuv):
+        return self.getDiffUV(stdLuv, testLuv)
+
+    def getDiffLUV(self, stdLuv, testLuv):
+        tmp = np.int32(testLuv[0]) - np.int32(stdLuv[0])
+        lDiff = abs(tmp)
+        tmp = np.int32(testLuv[1]) - np.int32(stdLuv[1])
+        uDiff = abs(tmp)
+        tmp = np.int32(testLuv[2]) - np.int32(stdLuv[2])
+        vDiff = abs(tmp)
+        lineDiff = np.sqrt(np.square(lDiff) + np.square(uDiff )+ np.square(vDiff))
+        return lDiff, uDiff ,vDiff ,lineDiff
+
+    '''
+    # 描述:获取HSV平均值
+    # 参数:
+    # img_path:原图路径
+    # area:要截图的区域
+    # '''
+    def getAverageHSV(self, img_path, area = None):
+        img = cv.imread(img_path)
+        if img is None:
+            print u'Img is None, please check the img path!'
+            return None, None, None
+
+        # 求RGB;
+        if (area is None):
+            area = (0, 0, img.shape[1], img.shape[0])  # mat shape:高、宽、颜色值个数
+
+        if len(area) == 4:
+            x1 = area[0]
+            y1 = area[1]
+            x2 = area[2]
+            y2 = area[3]
+            img = img[y1:y2 +1, x1:x2 +1]
+            # 宽,高;
+            width, height = abs(x2 - x1), abs(y2 - y1)
+            # print 'area=',area
+
+            total_B = 0
+            total_G = 0
+            total_R = 0
+            # pixel_total = hsv.shape[0] * hsv.shape[1]
+            pixel_total = width * height
+            if width == 0 or height == 0:
+                print u'宽或高为0'
+                return None, None, None
+            # print 'width,hieght=',width,height,pixel_total
+
+            for y in range(0, height):
+                for x in range(0, width):
+                    total_B += img[y][x][0]
+                    total_G += img[y][x][1]
+                    total_R += img[y][x][2]
+            # print u't->b,g,r=',total_B,total_G,total_R
+            b ,g ,r = total_B / pixel_total, total_G / pixel_total, total_R / pixel_total
+            # print 'bgr=',b,g,r
+            # 转成hsv;
+            hsv = self.rgb2hsv(r ,g ,b)
+            # print 'hsv-1',hsv
+            # hsv = colorsys.rgb_to_hsv(r,g,b)
+            # print 'hsv-2',hsv
+            return [round(hsv[0 ] /2), round(hsv[1]), round(hsv[2])]
+        else:
+            return [None, None, None]
+
+    # def rgb2hsv(self, r, g, b):
+    #     r, g, b = r/255.0, g/255.0, b/255.0
+    #     mx = max(r, g, b)
+    #     mn = min(r, g, b)
+    #     df = mx-mn
+    #     if mx == mn:
+    #         h = 0
+    #     elif mx == r:
+    #         h = (60 * ((g-b)/df) + 360) % 360
+    #     elif mx == g:
+    #         h = (60 * ((b-r)/df) + 120) % 360
+    #     elif mx == b:
+    #         h = (60 * ((r-g)/df) + 240) % 360
+    #     if mx == 0:
+    #         s = 0
+    #     else:
+    #         s = df/mx
+    #     v = mx
+    #     return h, s, v
+
+    def rgb2hsv(self, r, g, b):
+        r, g, b = r/ 255.0, g / 255.0, b / 255.0
+        mx = max(r, g, b)
+        mn = min(r, g, b)
+        df = mx - mn
+        # h值
+        if mx == mn:
+            h = 0
+        elif mx == r and g >= b:
+            h = (60 * (g - b) / df) % 360
+        elif mx == r and g < b:
+            h = (60 * ((g - b) / df) + 360) % 360
+        elif mx == g:
+            h = (60 * ((b - r) / df) + 120) % 360
+        elif mx == b:
+            h = (60 * ((r - g) / df) + 240) % 360
+            # s值
+        if mx == 0:
+            s = 0
+        else:
+            s = df / mx * 255
+        # v值
+        v = mx * 255
+        # 返回;
+        return h, s, v
+
+
+    def isUVSimilar(self, stdLuv, testLuv, diffLevel=20):
+        udiff, vdiff, lineDiff = self.getDiffUV(stdLuv, testLuv)
+        if lineDiff <= diffLevel:
+            return True
+        else:
+            return False

+ 329 - 0
ssat_sdk/picture/color_space.py

@@ -0,0 +1,329 @@
+# -*- coding:utf-8 -*-
+'''
+选定一种颜色空间,进行颜色空间的颜色颜色运算,例如:偏色
+'''
+import os, sys, time
+import cv2 as cv
+import numpy as np
+import colorsys
+import math
+CIEluv_STD_PIC = u"../resource/CIE_1976_UCS.png"
+
+class CIEluvCaculator():
+    def __init__(self, stdimg = None, testimg=None):
+        self.stdimg = stdimg
+        self.testimg = testimg
+
+    def setSTDImage(self, img):
+        self.stdimg = img
+
+    def setTestImage(self,img):
+        self.testimg = img
+
+    '''
+    计算单个指定区域的LUV平均值
+    '''
+    def getAverageLUV(self, img, area=None):
+        if (area is None):
+            area = (0, 0, img.shape[1], img.shape[0])  # mat shape:高、宽、颜色值个数
+        # print "getAverageLUV", img.shape, area
+        # print img.shape
+        img = np.array(img)
+        if len(area) == 4:
+            x1 = area[0]
+            y1 = area[1]
+            x2 = area[2]
+            y2 = area[3]
+            img = img[y1:y2+1, x1:x2+1]
+            # img = img[0:80, 0:100]
+            # print img.shape
+            total_L = 0
+            total_U = 0
+            total_V = 0
+            pixel_total = img.shape[0] * img.shape[1]
+            # print "pixel_total:",pixel_total
+
+            for x in range(0, img.shape[0]):
+                for y in range(0, img.shape[1]):
+                    total_L = total_L + img[x][y][0]
+                    total_U = total_U + img[x][y][1]
+                    total_V = total_V + img[x][y][2]
+            return total_L / pixel_total, total_U / pixel_total, total_V / pixel_total
+
+        else:
+            return None, None, None
+
+    '''
+    计算所有区域的LUV平均值
+    '''
+    def getMAverageLUV(self, img, areaList):
+        total_L,total_U, total_V, pixel_total = 0,0,0,0
+        for area in areaList:
+            L,U,V,P = self.countTotalPLuv(img, area)
+            total_L += L
+            total_U += U
+            total_V += V
+            pixel_total += P
+            # print "getMAverageLUV:",area,L,U,V,P
+        if (pixel_total == 0):
+            return None, None, None
+        return total_L/pixel_total,total_U/pixel_total,total_V/pixel_total
+
+    '''
+    计算整张图片指定区域Luv颜色值总和。
+    '''
+    def countTotalPLuv(self, img, area=None):
+        if (area is None):
+            area = (0, 0, img.shape[1], img.shape[0])  # mat shape:高、宽、颜色值个数
+        # print "getAverageLUV", img.shape, area
+        # print img.shape
+        img = np.array(img)
+        if len(area) == 4:
+            x1 = area[0]
+            y1 = area[1]
+            x2 = area[2]
+            y2 = area[3]
+            img = img[y1:y2+1, x1:x2+1]
+            # img = img[0:80, 0:100]
+            # print img.shape
+            total_L = 0
+            total_U = 0
+            total_V = 0
+            pixel_total = img.shape[0] * img.shape[1]
+            # print "pixel_total:",pixel_total
+
+            for x in range(0, img.shape[0]):
+                for y in range(0, img.shape[1]):
+                    total_L = total_L + img[x][y][0]
+                    total_U = total_U + img[x][y][1]
+                    total_V = total_V + img[x][y][2]
+            return total_L,total_U , total_V , pixel_total
+
+        else:
+            return None, None, None, None
+
+    def getDiffLevel(self, stdLuv, testLuv):
+        return self.getDiffUV(stdLuv, testLuv)
+
+    def getDiffuvLevel(self, stdLuv, testLuv):
+        diff = self.getDiffuv(stdLuv, testLuv)
+        return diff[2]
+
+    def getDiffluvLevel(self,stdLuv, testLuv):
+        return self.getDiffLUV(stdLuv, testLuv)
+
+    def getDiffUV(self, stdLuv, testLuv):
+        tmp = np.int32(testLuv[0]) - np.int32(stdLuv[0])
+        lDiff = abs(tmp)
+        tmp = np.int32(testLuv[1]) - np.int32(stdLuv[1])
+        uDiff = abs(tmp)
+        tmp = np.int32(testLuv[2]) - np.int32(stdLuv[2])
+        vDiff = abs(tmp)
+        lineDiff = np.sqrt(np.square(uDiff)+ np.square(vDiff))
+        return uDiff,vDiff,lineDiff
+
+    def getDiffuv(self, stdLuv, testLuv):
+        tmp = np.int32(testLuv[0]) - np.int32(stdLuv[0])
+        lDiff = abs(tmp)
+        tmp = np.int32(testLuv[1]) - np.int32(stdLuv[1])
+        uDiff = abs(tmp)
+        tmp = np.int32(testLuv[2]) - np.int32(stdLuv[2])
+        vDiff = abs(tmp)
+        lineDiff = np.sqrt(np.square(uDiff)+ np.square(vDiff))
+        return uDiff,vDiff,lineDiff
+
+    def getDiffLUV(self, stdLuv, testLuv):
+        tmp = np.int32(testLuv[0]) - np.int32(stdLuv[0])
+        lDiff = abs(tmp)
+        tmp = np.int32(testLuv[1]) - np.int32(stdLuv[1])
+        uDiff = abs(tmp)
+        tmp = np.int32(testLuv[2]) - np.int32(stdLuv[2])
+        vDiff = abs(tmp)
+        lineDiff = np.sqrt(np.square(lDiff) + np.square(uDiff)+ np.square(vDiff))
+        return lDiff, uDiff,vDiff,lineDiff
+
+    '''
+    # 描述:获取HSV平均值
+    # 参数:
+    # img_path:原图路径
+    # area:要截图的区域
+    # '''
+    def getAverageHSV(self, img_path, area = None):
+        img = cv.imread(img_path)
+        if img is None:
+            print u'Img is None, please check the img path!'
+            return None, None, None
+
+        # 求RGB;
+        if (area is None):
+            area = (0, 0, img.shape[1], img.shape[0])  # mat shape:高、宽、颜色值个数
+
+        if len(area) == 4:
+            x1 = area[0]
+            y1 = area[1]
+            x2 = area[2]
+            y2 = area[3]
+            img = img[y1:y2+1, x1:x2+1]
+            # 宽,高;
+            width, height = abs(x2 - x1), abs(y2 - y1)
+            # print 'area=',area
+
+            total_B = 0
+            total_G = 0
+            total_R = 0
+            # pixel_total = hsv.shape[0] * hsv.shape[1]
+            pixel_total = width * height
+            if width == 0 or height == 0:
+                print u'宽或高为0'
+                return None, None, None
+            # print 'width,hieght=',width,height,pixel_total
+
+            for y in range(0, height):
+                for x in range(0, width):                
+                    total_B += img[y][x][0]
+                    total_G += img[y][x][1]
+                    total_R += img[y][x][2]
+            # print u't->b,g,r=',total_B,total_G,total_R
+            b,g,r = total_B / pixel_total, total_G / pixel_total, total_R / pixel_total
+            # print 'bgr=',b,g,r
+            #转成hsv;
+            hsv = self.rgb2hsv(r,g,b)
+            # print 'hsv-1',hsv
+            # hsv = colorsys.rgb_to_hsv(r,g,b)
+            # print 'hsv-2',hsv
+            return [round(hsv[0]/2), round(hsv[1]), round(hsv[2])]
+        else:
+            return [None, None, None]
+
+    '''
+    img_path:图像路径
+    area:图片区域坐标x,y
+    '''
+    def getAvgGray(self, img_path, area = None):
+        img = cv.imread(img_path)
+        img = cv.imread(img_path)
+        if img is None:
+            print u'Img is None, please check the img path!'
+            return None
+
+        # 求RGB;
+        if (area is None):
+            area = (0, 0, img.shape[1], img.shape[0])  # mat shape:高、宽、颜色值个数
+
+        if len(area) == 4:
+            x1 = area[0]
+            y1 = area[1]
+            x2 = area[2]
+            y2 = area[3]
+            img = img[y1:y2+1, x1:x2+1]
+            imgGray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
+            return np.average(imgGray)
+        else:
+            return None
+
+    # def rgb2hsv(self, r, g, b):
+    #     r, g, b = r/255.0, g/255.0, b/255.0
+    #     mx = max(r, g, b)
+    #     mn = min(r, g, b)
+    #     df = mx-mn
+    #     if mx == mn:
+    #         h = 0
+    #     elif mx == r:
+    #         h = (60 * ((g-b)/df) + 360) % 360
+    #     elif mx == g:
+    #         h = (60 * ((b-r)/df) + 120) % 360
+    #     elif mx == b:
+    #         h = (60 * ((r-g)/df) + 240) % 360
+    #     if mx == 0:
+    #         s = 0
+    #     else:
+    #         s = df/mx
+    #     v = mx
+    #     return h, s, v
+
+    def rgb2hsv(self, r, g, b):
+        r, g, b = r/255.0, g/255.0, b/255.0
+        mx = max(r, g, b)
+        mn = min(r, g, b)
+        df = mx-mn
+        # h值
+        if mx == mn:
+            h = 0
+        elif mx == r and g >= b:
+            h = (60 * (g-b)/df) % 360
+        elif mx == r and g < b:
+            h = (60 * ((g-b)/df) + 360) % 360
+        elif mx == g:
+            h = (60 * ((b-r)/df) + 120) % 360
+        elif mx == b:
+            h = (60 * ((r-g)/df) + 240) % 360 
+        # s值
+        if mx == 0:
+            s = 0
+        else:
+            s = df/mx*255
+        # v值
+        v = mx*255
+        #返回;
+        return h, s, v
+    
+        
+    '''
+        # 描述:bgr色差计算。ΔE=( ΔL^2 + ΔA^2 + ΔB^2 ) ^ (1/2) ΔE
+        # 参数:color1、color2: bgr格式
+        # 返回值:
+        #   相差值,越小越相近。
+        #   0~50(微小色差),感觉极微; 
+        #   50~150(小色差),感觉轻微; 
+        #   150~300(较小色差),感觉明显; 
+        #   300~600(较大色差),感觉很明显; 
+        #   600以上(大色差),感觉强烈。
+        # '''
+    def colorDistance(self, color1, color2):
+        b1,g1,r1 = color1
+        b2,g2,r2 = color2
+        rmean = (r1 + r2 ) / 2
+        R = int(r1) - int(r2)
+        G = int(g1) - int(g2)
+        B = int(b1) - int(b2)
+        return math.sqrt((2+rmean/256)*(R**2)+4*(G**2)+(2+(255-rmean)/256)*(B**2))
+    
+    '''
+        # 描述:颜色是否相近。
+        # 参数:
+        #   color1、color2: bgr格式
+        #   faultTolerance: 容错值大小,表示两颜色的色差值
+        # 返回值:<= faultTolerance返回True,否则返回False.
+        # 
+        # '''
+    def isColorSimilar(self, color1, color2, faultTolerance = 100):
+        if 0:
+            b1 = color1[0] in range(color2[0]-faultTolerance,color2[0]+faultTolerance)
+            b2 = color1[1] in range(color2[1]-faultTolerance,color2[1]+faultTolerance)
+            b3 = color1[2] in range(color2[2]-faultTolerance,color2[2]+faultTolerance)
+
+            if b1 == True and b2  == True and b3  == True:
+                return True
+            else:
+                return False
+        if 1:
+            val = self.colorDistance(color1, color2)
+            return True if val <= faultTolerance else False
+
+    def isUVSimilar(self, stdLuv, testLuv, diffLevel=20):
+        udiff,vdiff,lineDiff = self.getDiffUV(stdLuv,testLuv)
+        if lineDiff <= diffLevel:
+            return True
+        else:
+            return False
+
+if __name__ == "__main__":
+    # img = cv.imread(CIEluv_STD_PIC, 1)
+    # imgLuv = cv.cvtColor(img,cv.COLOR_BGR2Luv)
+    # imgXYZ = cv.cvtColor(img,cv.COLOR_BGR2XYZ)
+
+    cieCal = CIEluvCaculator()
+    print cieCal.colorDistance([0,255,0],[0,220,0])
+    print cieCal.isUVSimilar([0,255,0],[0,250,3])
+    # print  cieCal.getDiffUV(imgLuv[1200,1200], imgLuv[1024,1024])
+

+ 3320 - 0
ssat_sdk/picture/feature_detect.py

@@ -0,0 +1,3320 @@
+# -*- coding:utf-8 -*-
+import inspect
+import os, sys, time
+
+'''
+用于图片特征提取和定位
+'''
+
+import image_util
+import PreImageJudge
+import cv2 as cv
+import numpy as np
+import json
+
+from ssat_sdk.sat_environment import *
+from TST.NetOCR import *
+from ssat_sdk.ocr_convert import *
+from color_space import *
+from line_util import LineUtil
+from ssat_sdk.device_manage import *
+
+# 导入枚举;
+from enum import Enum
+from itertools import chain
+# from ssat_sdk.device_manage.RedRatHub3 import RedRat3
+from ssat_sdk.tv_operator import *
+from ssat_sdk.video_capture import VideoCapture
+# import ssat_sdk.utils.LoggingUtil
+from ssat_sdk.utils import LoggingUtil
+from ssat_sdk.utils import string_util
+from ssat_sdk.picture.RGB import RGBColor
+from ssat_sdk.picture.pq_detect import PQDetect
+
+pyFileName = os.path.split(__file__)[-1]
+
+def get_current_function_name():
+    return inspect.stack()[1][3]
+
+# 效果图形状
+class IconShape(Enum):
+    Rectangle = 0   # 矩形;
+    Circle = 1      # 圆形(包括椭圆)
+    Polygon = 2     # 多边形
+
+class FocusType(Enum):
+    Border = 0      # 加框;
+    Coloring = 1    # 着色;
+    Deform = 2      # 变形;
+    ColorLine = 3   # 纯色线查找矩形;
+    ThresholdBinary = 4 # 二值化阀值查找焦点框;
+    matchTempl = 5  # 模板匹配法查找焦点框;
+
+class Direction(Enum):
+    UnChange = 0    # 区域不变;
+    TopChange = 1   # 上;
+    BottomChange = 2  # 下;
+    LeftChange = 3  # 左;
+    RightChange = 4 # 右;
+    ZoomIn = 5      # 放大;
+    ZoomOut = 6     # 缩小;
+
+methods = [cv.TM_CCOEFF, cv.TM_CCOEFF_NORMED, cv.TM_CCORR, cv.TM_CCORR_NORMED, cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED]
+
+class FeatureDetect():
+    def __init__(self):
+        self.className = self.__class__.__name__
+        self.CIELuv = CIEluvCaculator()
+        self.OCR = OCRConvert()
+        self.lineUtil = LineUtil()
+        # self.redRat3 = RedRat3()
+        self.redRat3 = TvOperator()
+        self.vp = VideoCapture()
+        self.PQ = PQDetect()
+
+    '''
+    检测传入图片的指定区域的线段
+    :param file:图片文件路径
+    :param lineMineLen:线段最短长度。 默认 20
+    :param lineMaxLen:线段最长长度。默认 1000
+    :param ableArea:指定区域。 默认 整张图片
+    :return 返回线段数组
+    '''
+    def getLinesByFile(self, file, lineMinLen = 20, lineMaxLen = 1000, ableArea=None, threshold1=50, threshold2=100, apertureSize=5):
+        img = cv.imread(file)
+        return self.getLines(img, lineMinLen, lineMaxLen, ableArea, threshold1, threshold2, apertureSize)
+
+    def getLines(self, img, lineMinLen = 20, lineMaxLen = 1000, ableArea=None, threshold1=50, threshold2=100, apertureSize=5
+                 , lineThreshold = 200):
+        targetMat = np.array(img)
+        if (ableArea <> None):
+            targetMat = image_util.cutMat(img, ableArea)
+
+        grayMat = cv.cvtColor(targetMat, cv.COLOR_BGR2GRAY)
+        # cv.imshow("gray", grayMat)
+
+        edges = cv.Canny(grayMat, threshold1, threshold2, apertureSize=apertureSize)
+        # cv.imshow("edges",edges)
+        lines = cv.HoughLinesP(edges, 2, np.pi / 180, lineThreshold)
+        retLines = []
+        if lines is None:
+            return None, None
+        for line in lines:
+            x1, y1, x2, y2 = line[0]
+            # print "line:", x1, y1, x2, y2
+            # "line rgb:",PreImageJudge.getImgAverageRGB(targetMat, (x1, y1, x2+1, y2+1)), \
+            # "icon rgb:",PreImageJudge.getAverageRGB(icon)
+            lineLen = self.lineUtil.caculateLinLen(line[0])
+            if lineLen > lineMinLen and lineLen < lineMaxLen:
+                # print "retLine:", line[0]
+                cv.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
+                retLines.append(line[0])
+        # cv.imshow("getLines", img)
+        # cv.waitKey(0)
+        # cv.destroyAllWindows()
+        return retLines, img
+
+    def findRectByFile(self, picPath, type):
+        img = cv.imread(picPath)
+        return self.findRectByImg(img, type)
+
+    def findRectByImg(self, img, type):
+        retLines = None
+        lineImg = None
+        if (type == "parentLock"):
+            retLines, lineImg = self.getLines(img, lineThreshold=200)
+            comLines = self.lineUtil.combineLineList(retLines)
+            # for line in comLines:
+            #     print "comLines",line
+                # cv.line(img, (line[0],line[1]),(line[2],line[3]), (0, 0, 255), 2)
+            minPoint,maxPoint = self.lineUtil.getLineMMPoint(comLines)
+            rect = [minPoint[0],minPoint[1], maxPoint[0],maxPoint[1]]
+            # print rect
+            # cv.rectangle(img, (minPoint[0], minPoint[1]), (maxPoint[0],maxPoint[1]), (0, 0, 255), 2)
+            # cv.imshow("comLine", img)
+            # cv.waitKey(0)
+            # cv.destroyAllWindows()
+            return rect
+        # print retLines
+        # cv.imwrite(os.path.join(LoggingUtil.getCaseRunLogDirPath(), "lineImg.png"), lineImg)
+
+    '''
+    根据icon,寻找在图片中icon的位置,返回选中区域的特征:文本、文本区域、聚焦区域
+    :param icon:图标文件路径
+    :param screen:整张图片文件路径
+    :param uiDict: 可能出现icon的区域。
+    例如:
+    #name的字符串,需为text_area区域显示的文字
+    {
+    focus_tv : '{"name":"tv","text_area":[179,210,242,248,"english", 253],"focus_area":[43,128,380,265]}',
+    focus_av : '{"name":"av","text_area":[180,339,242,381,"english", 253],"focus_area":[43,257,380,395]}',
+    focus_hdmi1 : '{"name":"hdmi1","text_area":[156,466,269,510,"english", 2],"focus_area":[43,386,380,525]}',
+    focus_hdmi2 : '{"name":"hdmi2","text_area":[159,599,265,641,"english", 2],"focus_area":[43,517,380,655]}',
+    focus_usb : '{"name":"media","text_area":[160,730,260,771,"english", 2],"focus_area":[43,648,380,785]}'
+    }
+    :param border_wide:四周选择框的厚度
+    :return 返回文本、文本区域、聚焦区域
+    '''
+    def getFocusArea(self, icon, screen, uiDict, border_wide):
+        # 1 获取焦点框特征
+        icon_luv= self.calBorderAVGLuvPic(icon, border_wide)
+        print "icon:", icon_luv
+
+        # 2 与可能的聚焦位置进行比较
+        # print self.focusDict
+        # 计算直线数最接近区域
+        target_text_area = None
+        target_focus_area = None
+        dLine = 10000
+        focus_end = "focus_end"
+        for focus in uiDict:
+            one_dict = json.loads(uiDict[focus])
+            area = one_dict["focus_area"]
+            focus_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus + "_focus.png")
+            image_util.saveCropPic(screen, focus_img, area)
+            img_luv = self.calBorderAVGLuvPic(focus_img, border_wide)
+            # print focus, " ,img_luv:", img_luv
+            if img_luv is None:
+                continue
+            diff = self.CIELuv.getDiffUV(icon_luv, img_luv)
+            if diff[2] < dLine:
+                dLine = diff[2]
+                # print "current dLine:", dLine, area, one_dict["text_area"]
+                target_focus_area = area
+                target_text_area = one_dict["text_area"]
+                focus_end = focus
+        text_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus_end + ".png")
+        image_util.saveCropPic(screen, text_img, target_text_area[0:4])
+        language = target_text_area[4]
+        ocrType = target_text_area[5]
+
+        if len(target_text_area) != 6 or target_text_area[6] == {}:
+            target_text = self.OCR.getStr(text_img, language, ocrType)
+        else:
+            target_text = self.OCR.getStrWithImgProcess(text_img, target_text_area[6], language, ocrType)
+        # target_text = self.OCR.getStr(text_img, language, ocrType)
+        print u"getFocusArea,选中区域文字:",target_text
+        return target_text, target_text_area, target_focus_area
+
+    '''
+    根据icon,寻找在图片中icon的位置
+    :param icon:选择框图标文件路径
+    :param screen:整张图片文件路径
+    :param focusDict: 可能出现icon的区域。
+    例如:
+    #name的字符串,需为text_area区域显示的文字
+    {
+    'back': [152, 780, 432, 860],
+    'forgot': [120, 946, 486, 1010],
+    'gmain': [152, 618, 386, 672]
+    }
+    :param border_wide:四周选择框的厚度
+    :return 聚焦区域
+    '''
+    def locateFocusArea(self, icon, screen, focusDict, border_wide):
+        icon_img = cv.imread(icon)
+        return self.locateImgFocusArea(icon_img, screen, focusDict, border_wide)
+
+    '''
+        根据icon,寻找在图片中icon的位置
+        :param icon:选择框图标的cv的mat对象
+        :param screen:整张图片文件路径
+        :param focusDict: 可能出现icon的区域。
+        例如:
+        #name的字符串,需为text_area区域显示的文字
+        {
+        'back': [152, 780, 432, 860],
+        'forgot': [120, 946, 486, 1010],
+        'gmain': [152, 618, 386, 672]
+        }
+        :param border_wide:四周选择框的厚度
+        :return 聚焦区域
+        '''
+    def locateImgFocusArea(self, icon, screen, focusDict, border_wide):
+        # 1 获取焦点框特征
+        icon_luv = self.calBorderAVGLuvImg(icon, border_wide)
+        # print "icon luv:", icon_luv
+
+        # 2 与可能的聚焦位置进行比较,按LUV平均值接近度衡量
+        focus_area = None
+        focus_name = None
+        dLine = 10000
+        for focus in focusDict:
+            area = focusDict[focus]
+            # print "area name:", focus
+            focus_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), "locateImgFocusArea_focus.png")
+            image_util.saveCropPic(screen, focus_img, area)
+            img_luv = self.calBorderAVGLuvPic(focus_img, border_wide)
+            # print "img_luv:", img_luv,one_dict
+            if img_luv is None:
+                continue
+            diff = self.CIELuv.getDiffUV(icon_luv, img_luv)
+            # print focus, img_luv,diff
+            if diff[2] < dLine:
+                dLine = diff[2]
+                # print "current dLine:", dLine, area, one_dict["text_area"]
+                focus_area = area
+                focus_name = focus
+        return focus_name, focus_area
+    '''
+    计算图片四周边框的颜色平均值,颜色空间Luv
+    :param pic:图片文件路径
+    :param wide:四周边框的宽度
+    :return LUV:四周边框Luv平均值
+    '''
+    def calBorderAVGLuvPic(self,pic, wide):
+        img = cv.imread(pic)
+        return self.calBorderAVGLuvImg(img, wide)
+
+    def calBorderAVGLuvImg(self, img, wide):
+        luv_img = cv.cvtColor(img, cv.COLOR_BGR2Luv)
+        height, width, colorDim = luv_img.shape
+        left_area = [0,0,wide,height]
+        right_area = [width-wide,0,width,height]
+        top_area = [wide,0,width-wide,wide]
+        bottom_area = [wide,height-wide,width-wide,height]
+        return self.CIELuv.getMAverageLUV(luv_img,[left_area,right_area,top_area,bottom_area])
+
+    # Ex interface
+    '''
+    描述:寻找图片中符合icon效果图的位置,返回焦点区域的特征:文本、文本区域、聚集区域.
+    参数:
+    icon_path: 效果图路径;
+    screen_path: 要检测焦点框的全屏图路径;
+    uiDict: ui字典;
+    示例字典,需为text_area区域显示的文字
+    {
+        focus_tv : '{"name":"tv","text_area":[179,210,242,248,"english", 253],"focus_area":[43,128,380,265]}',
+        focus_av : '{"name":"av","text_area":[180,339,242,381,"english", 253],"focus_area":[43,257,380,395]}',
+        focus_hdmi1 : '{"name":"hdmi1","text_area":[156,466,269,510,"english", 2],"focus_area":[43,386,380,525]}',
+        focus_hdmi2 : '{"name":"hdmi2","text_area":[159,599,265,641,"english", 2],"focus_area":[43,517,380,655]}',
+        focus_usb : '{"name":"media","text_area":[160,730,260,771,"english", 2],"focus_area":[43,648,380,785]}'
+    }
+    icon_shape: 效果图形状;
+    focus_type: 焦点类型;
+    focus_direction: 焦点变化方向;
+    border_wide: 焦点框边框大小;
+    返回值:
+        None
+    示例:
+    注意:
+    '''
+    def getFocusAreaEx(self, icon_path, screen_path, uiDict, icon_shape, focus_type, focus_direction, border_wide):
+        # 整型转枚举;
+        if type(icon_shape) != type(IconShape.Rectangle):
+            icon_shape = IconShape(icon_shape)
+
+        if type(focus_type) != type(FocusType.Border):
+            focus_type = FocusType(focus_type)
+
+        if type(focus_direction) != type(Direction.ZoomIn):
+            focus_direction = Direction(focus_direction)
+
+        # 矩形效果图;
+        if icon_shape == IconShape.Rectangle:
+            return self.getRectangleFocusArea(icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide)
+        # 圆形效果图;
+        elif icon_shape == IconShape.Circle:
+            return self.getCircleFocusArea(icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide)
+        # 非矩形平行四边形效果图;
+        elif icon_shape == IconShape.Polygon: 
+            return self.getPolygonFocusArea(icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide)
+        else:
+            LoggingUtil.getDebugLogger().info(
+                pyFileName, 
+                self.className, 
+                get_current_function_name(), 
+                'SDK:icon_shape不存范围内'+icon_shape)
+            return None, None, None
+       
+    # 获取矩形的焦点区域;
+    def getRectangleFocusArea(self, icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide):
+        # 焦点框(外镶边框)
+        if focus_type == FocusType.Border:  
+            return self.getBorderRectangleFocusArea(icon_path, screen_path, uiDict, focus_direction, border_wide)
+        # 区域变色;
+        elif focus_type == FocusType.Coloring:  
+            return self.getColoringleFocusArea(icon_path, screen_path, uiDict, focus_direction)
+        # 图形变化;
+        elif focus_type == FocusType.Deform:  
+            return self.getZoomFocusArea(icon_path, screen_path, uiDict, focus_direction)
+
+    # 获取圆形(包含椭圆)的焦点区域;
+    def getCircleFocusArea(self, icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide):
+        # 焦点框(外镶边框)
+        if focus_type == FocusType.Border:  
+            return self.getBorderCircleFocusArea(icon_path, screen_path, uiDict, focus_direction, border_wide)
+        # 区域变色;
+        elif focus_type == FocusType.Coloring:  
+            return self.getColoringleFocusArea(icon_path, screen_path, uiDict, focus_direction)
+        # 图形变化;
+        elif focus_type == FocusType.Deform:  
+            return self.getZoomFocusArea(icon_path, screen_path, uiDict, focus_direction)
+
+    # 多边形的焦点区域;
+    def getPolygonFocusArea(self, icon_path, screen_path, uiDict, focus_type, focus_direction, border_wide):
+        # 焦点框(外镶边框)
+        if focus_type == FocusType.Border:  
+            return self.getBorderRectangleFocusArea(icon_path, screen_path, uiDict, focus_direction, border_wide)
+        # 区域变色;
+        elif focus_type == FocusType.Coloring:  
+            return self.getColoringleFocusArea(icon_path, screen_path, uiDict, focus_direction)
+        # 图形变化;
+        elif focus_type == FocusType.Deform:  
+            return self.getZoomFocusArea(icon_path, screen_path, uiDict, focus_direction)
+
+    '''
+        # 描述:查找矩形焦点框,选中焦点框的效果为矩形边框外镶一层有色边框。focus_type=0,简述:矩形边框。
+        # 参数:
+        # icon_path:焦点框被选中时,外镶一层边框的效果图路径。
+        # screen_path:需要判断焦点框的电视画面截图。
+        # uiDict:
+        # focus_direction:
+        # border_wide:焦点框被选中时,外镶边框的厚度(像素宽)
+        # 返回值:
+        # 
+        # '''
+    def getBorderRectangleFocusArea(self, icon_path, screen_path, uiDict, focus_direction, border_wide):
+        # 获取焦点框特征
+        icon_luv = self.calBorderAVGLuvPic(icon_path, border_wide)
+       
+        # 计算直线数最接近区域
+        target_text_area = None
+        target_focus_area = None
+        dLine = 10000
+        focus_end = "focus_end"
+        for focus in uiDict:
+            # one_dict = json.loads(uiDict[focus])
+            one_dict = uiDict[focus]
+            # print one_dict["focus_area"]
+            # diff = 11
+            area = one_dict["focus_area"]
+            # area = [one_dict["focus_area"][0] - diff,one_dict["focus_area"][1] - diff,one_dict["focus_area"][2] + diff,one_dict["focus_area"][3] + diff]
+            # zoomin;
+            # print area
+            focus_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus + "_focus.png")
+            image_util.saveCropPic(screen_path, focus_img, area)
+            img_luv = self.calBorderAVGLuvPic(focus_img, border_wide)
+            # print focus, " ,img_luv:", img_luv
+            if img_luv is None:
+                continue
+            diff = self.CIELuv.getDiffUV(icon_luv, img_luv)
+            if diff[2] < dLine:
+                dLine = diff[2]
+                # print "current dLine:", dLine, area, one_dict["text_area"]
+                target_focus_area = area
+                target_text_area = one_dict["text_area"]
+                focus_end = focus
+        #end for
+        
+        try:
+            # 识别焦点区域文字;
+            target_text = None
+            if list(target_text_area[0:4]) != list([-1,-1,-1,-1]):
+                text_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus_end + ".png")
+                image_util.saveCropPic(screen_path, text_img, target_text_area[0:4])
+                language = target_text_area[4]
+                ocrType = target_text_area[5]
+
+                if len(target_text_area) != 6 or target_text_area[6] == {}:
+                    target_text = self.OCR.getStr(text_img, language, ocrType)
+                else:
+                    target_text = self.OCR.getStrWithImgProcess(text_img, target_text_area[6], language, ocrType)
+                # target_text = self.OCR.getStr(text_img, language, ocrType)
+                print u"getFocusArea,选中区域文字:",target_text
+            # 返回;
+            return target_text, target_text_area, target_focus_area
+        except Exception, e:
+            LoggingUtil.getDebugLogger().info(
+                pyFileName, 
+                self.className, 
+                get_current_function_name(), 
+                'SDK:OCR识别异常'+e)
+            return None,None,None
+
+    # 圆形边框;
+    def getBorderCircleFocusArea(self, icon_path, screen_path, uiDict, focus_direction, border_wide):
+        return None,None,None
+
+    # 多边形边框;
+    def getBorderPolygonFocusArea(self, icon_path, screen_path, uiDict, focus_direction, border_wide):
+        return None,None,None
+
+    '''
+        # 描述:查找焦点框(不区分形状),选中效果为焦点框背景色变化成其他颜色。focus_type=1,简述:区域着色
+        # 参数:
+        # icon_path:焦点框被选中时,背景变色的效果图路径。
+        # screen_path:需要判断焦点框的电视画面截图。
+        # uiDict:
+        # focus_direction:
+        # 返回值:
+        # 
+        # '''
+    def getColoringleFocusArea(self, icon_path, screen_path, uiDict, focus_direction):
+        # 获取效果图的luv平均值;
+        imgColoring = cv.imread(icon_path)
+        luvColoring = self.CIELuv.getAverageLUV(imgColoring)
+
+        # 遍历字典中所有区域截图,计算luv值;
+        luvNoraml = None
+        # 目标文本区域;
+        target_text_area = None
+        # 目标焦点区域;
+        target_focus_area = None
+        # 默认最大值;
+        dLine = 10000
+        focus_end = "focus_end"
+        for focus in uiDict:
+            #正常图片;
+            # print u'name=',focus
+            one_dict = uiDict[focus]
+            normalArea = one_dict["focus_area"]
+            focus_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus + "_focus.png")
+            image_util.saveCropPic(screen_path, focus_img, normalArea)
+            luvNoraml = self.CIELuv.getAverageLUV(cv.imread(focus_img))
+            if luvNoraml is None:
+                continue
+            # print u'normalArea=',normalArea
+            
+            # 计算相差;
+            diff = self.CIELuv.getDiffUV(luvColoring, luvNoraml)
+            if diff[2] < dLine:
+                dLine = diff[2]
+                target_focus_area = normalArea
+                target_text_area = one_dict["text_area"]
+                focus_end = focus
+
+        try:
+            # 识别焦点区域文字;
+            target_text = None
+            text_img_path = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus_end + ".png")
+            if list(target_text_area[0:4]) != list([-1,-1,-1,-1]):
+                image_util.saveCropPic(screen_path, text_img_path, target_text_area[0:4])
+                language = target_text_area[4]
+                ocrType = target_text_area[5]
+
+                if len(target_text_area) != 6 or target_text_area[6] == {}:
+                    target_text = self.OCR.getStr(text_img_path, language, ocrType)
+                else:
+                    target_text = self.OCR.getStrWithImgProcess(text_img_path, target_text_area[6], language, ocrType)
+                
+                # target_text = self.OCR.getStr(text_img_path, language, ocrType)
+                # print u"getFocusArea,选中区域文字:",target_text
+
+            # 返回文本内容、文本区域、焦点区域;
+            return target_text, target_text_area, target_focus_area 
+        except Exception, e:
+            LoggingUtil.getDebugLogger().info(
+                pyFileName, 
+                self.className, 
+                get_current_function_name(), 
+                'SDK:OCR识别异常'+e)
+            return None,None,None
+
+    '''
+        # 描述:获取两区域的缩放率 
+        # 参数:
+        # normalArea:区域坐标值,如[0,0,100,100]
+        # imgZoomin:图像对象,使用cv.imread('path')获取的数组;
+        # isZoomin:缩放率是放大率,还是缩小率, True放大率,False缩小率.
+        # 
+        # 返回值:
+        #   返回浮点值,缩放率scale
+        # 示例:
+        # 
+        # 注意:
+        # 
+        # '''
+    def getZoomScale(self, normalArea, imgZoomin, isZoomin = False):
+        scale = 0.000
+        if imgZoomin is None:
+            return scale
+        if isZoomin == True:
+            scale = float(imgZoomin.shape[1]) / (normalArea[2] - normalArea[0])
+        else:
+            scale = float(normalArea[2] - normalArea[0]) / imgZoomin.shape[1]
+        # 返回缩放率;
+        return scale
+
+    
+    '''
+        # 描述:查找焦点框,选中焦点框的效果为放大后的焦点框,focus_type=2
+        # 参数:
+        # icon_path:
+        # screen_path:
+        # uiDict:
+        # focus_direction:
+        # 
+        # 返回值:
+        # 
+        # 
+        # 注意:
+        # 
+        # 
+        # '''
+    def getZoomFocusArea(self, icon_path, screen_path, uiDict, focus_direction):
+        # 获取放大的图像;
+        imgZoomin = cv.imread(icon_path)
+        # 获取第一个正常图标区域;
+        normalArea = uiDict.values()[1]["focus_area"]
+        # 计算放大率;
+        zoominScale = self.getZoomScale(normalArea, imgZoomin, True)
+               
+        # 遍历所有的区域,计算接近值;
+        # 放大的矩形区域;
+        zoominArea = None
+        # 目标文本区域;
+        target_text_area = None
+        # 目标焦点区域;
+        target_focus_area = None
+        # 默认最大值;
+        dLine = 10000
+        focus_end = "focus_end"
+        # 矩形中心坐标点;
+        cx,cy = None, None
+        # 矩形宽高;
+        width, height = None, None
+        for focus in uiDict:
+            #正常图片;
+            # print u'name=',focus
+            one_dict = uiDict[focus]
+            normalArea = one_dict["focus_area"]
+            focus_img = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus + "_focus.png")
+            image_util.saveCropPic(screen_path, focus_img, normalArea)
+            img_luv = self.CIELuv.getAverageLUV(cv.imread(focus_img))
+            if img_luv is None:
+                continue
+            # print u'normalArea=',normalArea
+
+            # 中心点位置;
+            cx,cy = normalArea[0]+(normalArea[2] - normalArea[0])/2,  normalArea[1]+(normalArea[3] - normalArea[1])/2
+            # print u'中心点位置',cx,cy
+
+            # 宽高;
+            width, height = (normalArea[2] - normalArea[0])/2,  (normalArea[3] - normalArea[1])/2
+            # print u'宽,高',width, height
+            
+            # 计算放大的区域值;
+            zoominArea = [int(cx-width*zoominScale), int(cy-height*zoominScale), int(cx+width*zoominScale), int(cy+height*zoominScale)]
+            # print u'zoomin=',zoominArea
+            #保存放大区域截图;
+            focus_img_path = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus + "_zoomin.png")
+            image_util.saveCropPic(screen_path, focus_img_path, zoominArea)
+            focus_img = cv.imread(focus_img_path)
+            if focus_img is None:
+                # print u'focus_img is None,路径:',focus_img_path
+                LoggingUtil.getDebugLogger().info(
+                pyFileName, 
+                self.className, 
+                get_current_function_name(), 
+                'SDK:focus_img is None,路径:'+focus_img_path)
+                continue
+            imgZoomin_luv = self.CIELuv.getAverageLUV(focus_img)
+            
+            # 计算相差;
+            diff = self.CIELuv.getDiffUV(imgZoomin_luv, img_luv)
+            if diff[2] < dLine:
+                dLine = diff[2]
+                target_focus_area = normalArea
+                target_text_area = one_dict["text_area"]
+                focus_end = focus
+
+        try:
+            # 识别焦点区域文字;
+            target_text = None
+            text_img_path = os.path.join(LoggingUtil.getCaseRunLogDirPath(), focus_end + ".png")
+            if list(target_text_area[0:4]) != list([-1,-1,-1,-1]):
+                image_util.saveCropPic(screen_path, text_img_path, target_text_area[0:4])
+                language = target_text_area[4]
+                ocrType = target_text_area[5]
+                if len(target_text_area) != 6 or target_text_area[6] == {}:
+                    target_text = self.OCR.getStr(text_img_path, language, ocrType)
+                else:
+                    target_text = self.OCR.getStrWithImgProcess(text_img_path, target_text_area[6], language, ocrType)
+                
+                # target_text = self.OCR.getStr(text_img_path, language, ocrType)
+            # print u"getFocusArea,选中区域文字:",target_text
+
+            # 返回文本内容、文本区域、焦点区域;
+            return target_text, target_text_area, target_focus_area 
+        except Exception, e:
+            # print u'except',e
+            LoggingUtil.getDebugLogger().info(
+                pyFileName, 
+                self.className, 
+                get_current_function_name(), 
+                'SDK:OCR识别异常'+e)
+            return None,None,None
+
+    '''
+        # 描述:查找矩形焦点框,选中焦点框的效果为焦点背景色变成纯色。focus_type=3。
+        # 参数:
+        # dict:
+        #
+        # 返回值:
+        # '''
+    def autoRecognitionFocusArea(self, dict, percent = 0.9):
+        if dict is None:
+            print u'autoRecognitionFocusArea:dict is None'
+            return None, None, None
+        # 路径;
+        imgPath = dict["shootPicPath"]
+        zoneArea = dict["zone_area"]
+        refFocusArea = dict["focus_area"]
+        refTextArea = list(dict["text_area"])
+        colorArea = dict["color_area"]
+
+        imgHome = cv.imread(imgPath)
+        if imgHome is None:
+            print u'autoRecognitionFocusArea:imgHome is None'
+            return None, None, None 
+        # colorArea中心点,用于取色;
+        cx,cy = colorArea[0] + int(colorArea[2] - colorArea[0])/2, colorArea[1] + int(colorArea[3] - colorArea[1])/2
+        # 背景色;
+        # bgColor = imgHome[cy][cx]
+        # call
+        return self.__autoRecognitionFocusArea(
+            dict["currentPicPath"], 
+            imgHome[cy][cx], 
+            zoneArea, 
+            refFocusArea,
+            refTextArea,
+            percent
+            )
+
+    '''
+        # 描述:
+        # 参数:
+        # imgPath:要检测焦点框的图片路径。如:"D:\\Home.png"
+        # focusBGColor:选中的焦点框的着色值,bgr格式。如:[122,122,122]
+        # zoneArea:焦点框所在区域范围坐标,坐标点相对于imgPath图片。如(x1,y1,x2,y2):[10,10,50,50]
+        # focusWidth:选中的焦点框宽度。
+        # focusHeight:选中的焦点框高度。
+        # textArea:文本区域在焦点框中的坐标。
+        # language:语言
+        # orcType:
+        #
+        # 返回值:
+        # 
+        # '''
+    def __autoRecognitionFocusArea(self, imgPath, focusBGColor, zoneArea, refFocusArea, refTextArea, percent = 0.9):
+        imgHome = cv.imread(imgPath)
+        # 水平、垂直
+        hLine1, hLine2 = [],[]
+        # 宽、高;
+        focusWidth, focusHeight = refFocusArea[2]-refFocusArea[0],refFocusArea[3]-refFocusArea[1]
+        # 是否找到
+        bhLine1, bhLine2 = False,False
+        if imgHome is None:
+            # print u'__autoRecognitionFocusArea:imgHome is None'
+            LoggingUtil.getDebugLogger().info(
+                pyFileName, 
+                self.className, 
+                get_current_function_name(), 
+                'SDK:图片对象空,可能路径不正确')
+            return None, None, None
+        else:
+            # 第一行;
+            for y in range(zoneArea[1], zoneArea[3]):
+                for x in range(zoneArea[0], zoneArea[2]):
+                    if bhLine1 == False:
+                        # if list(imgHome[y][x]) == list(focusBGColor):
+                        if self.CIELuv.isColorSimilar(imgHome[y][x],focusBGColor) == True:
+                            hLine1.append([x,y])
+                    #end if
+                #end for
+
+                # 判断本线是否符合要求;
+                if bhLine1 == False:
+                    count = len(hLine1)
+                    if float(count)/focusWidth > percent:
+                        bhLine1 = True
+                    else:
+                        hLine1 = []
+            #end for
+            if len(hLine1) == 0:
+                # print u'__autoRecognitionFocusArea: 未找到第一行,长度为0'
+                LoggingUtil.getDebugLogger().info(
+                    pyFileName, 
+                    self.className, 
+                    get_current_function_name(), 
+                    'SDK:未找到第一行,长度为0')
+                return None, None, None
+
+            #最后一行,倒查;
+            zoneArea = [hLine1[0][0]-10, hLine1[0][1], hLine1[0][0] + focusWidth, hLine1[0][1] + focusHeight]
+            print u'zoneArea=',zoneArea
+            for y in range(zoneArea[3], zoneArea[1],-1):
+                for x in range(zoneArea[0], zoneArea[2]):
+                    if bhLine2 == False:
+                        # if list(imgHome[y][x]) == list(focusBGColor):
+                        if self.CIELuv.isColorSimilar(imgHome[y][x],focusBGColor) == True:
+                            hLine2.append([x,y])
+                    #end if
+                #end for 
+
+                # 判断本线是否符合要求;
+                if bhLine2 == False:
+                    count = len(hLine2)
+                    if float(count)/focusWidth > percent:
+                        bhLine2 = True
+                    else:
+                        hLine2 = []
+            #end for
+            if len(hLine2) == 0:
+                # print u'__autoRecognitionFocusArea: 未找到最后一行,长度为0'
+                LoggingUtil.getDebugLogger().info(
+                    pyFileName, 
+                    self.className, 
+                    get_current_function_name(), 
+                    'SDK:未找到最后一行,长度为0')
+                return None, None, None
+
+            # 焦点区域;
+            focusArea = [hLine1[0][0], hLine1[0][1], hLine2[-1][0], hLine2[-1][1]]
+            # 查找文本区域;
+            realTextArea = self.findCurrentFocusTextBox(focusArea, refFocusArea, refTextArea[0:4])
+            if realTextArea is None:
+                return None,None,focusArea
+            # ocr识别文本;
+            target_text = self.getCurrentFocusText(imgPath, realTextArea, refTextArea[4:])
+            # 返回结果;
+            return target_text, realTextArea, focusArea
+
+    '''
+    Tenengrad梯度方法利用Sobel算子分别计算水平和垂直方向的梯度,同一场景下梯度值越高,图像越清晰。
+    以下是具体实现,这里衡量的指标是经过Sobel算子处理后的图像的平均灰度值,值越大,代表图像越清晰。
+    :param 图片路径
+    :return float, 值越大,代表清晰度越高
+    '''
+    def detSharpTenengrad(self, pic_path):
+        return self.PQ.detSharpTenengrad(pic_path)
+
+    '''
+    采用Laplacian梯度方法检测清晰度
+    :param 图片路径
+    :return float, 值越大,代表清晰度越高
+    '''
+    def detSharpLaplacian(self, pic_path):
+        return self.PQ.detSharpLaplacian(pic_path)
+
+    '''
+    img:opencv图像对象,BGR颜色空间
+    '''
+    def detImgSharpLaplacian(self, img):
+        return self.PQ.detImgSharpLaplacian(img)
+
+    '''
+    采用方差(Variance)方法检测清晰度。
+    方差是概率论中用来考察一组离散数据和其期望(即数据的均值)之间的离散(偏离)成都的度量方法。
+    方差较大,表示这一组数据之间的偏差就较大,组内的数据有的较大,有的较小,分布不均衡;
+    方差较小,表示这一组数据之间的偏差较小,组内的数据之间分布平均,大小相近。
+    :param 图片路径
+    :return float, 值越大,代表清晰度越高
+    '''
+    def detSharpVariance(self, pic_path):
+        return self.PQ.detSharpVariance(pic_path)
+
+    '''
+        # 描述:获取满足指定周长、面积范围的灰度二值化轮廓;
+        # 参数:
+        # bInside:使用内轮廓
+        # tvShotPath:图片路径
+        # cropArea:裁剪区域, 如:[left,top,right,bottom]
+        # grayVal:灰度值;
+        # maxGrayVal:最大灰度值
+        # minPeri: 最小周长;
+        # maxPeri: 最大周长;
+        # minArea: 最小面积;
+        # maxArea: 最大面积;
+        # find_1st: 是否只返回满足条件的第一个值
+        # 
+        # 返回:返回满足指定周长、面积范围的一个或多个轮廓坐标,坐标值如:[left,top,right,bottom];
+        # 
+        # '''
+    def getGrayBinaryContour(self, tvShotPath, cropArea, grayVal, maxGrayVal, bInside, minPeri, maxPeri, minArea, maxArea, find_1st = True):
+        contourRect = [0,0,0,0]#结果:x1,y1,x2,y2;
+        img_binary = self.getBinaryImage(tvShotPath, cropArea, grayVal, maxGrayVal)
+       # 获取二值化图像的轮廓;
+        area, perimeter = 0.0, 0.0
+        if bInside == False:#外轮廓
+            contours = cv.findContours(img_binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[1]
+        else:#内轮廓
+            contours = cv.findContours(img_binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)[1]
+        
+        # 是否查找最大轮廓;
+        bFindMaxContour = False
+        if minPeri == maxPeri and minArea == maxArea:
+            bFindMaxContour = True
+
+        listBox = []
+        maxContour = None #最大轮廓;
+        tempArea = 0
+        # 过滤掉不符合要求的轮廓;
+        x, y, w, h,result = 0,0,0,0,False
+        for cnt in contours:
+            # 面积;
+            area = cv.contourArea(cnt)
+            # 周长;
+            perimeter = cv.arcLength(cnt, True)
+
+            if bFindMaxContour == True:
+                # 获取最大轮廓;
+                if tempArea < area:
+                    tempArea = area
+                    maxContour = cnt
+            else:
+                # print area,perimeter
+                # 获取满足条件的值;
+                if area >= minArea and area <= maxArea and perimeter >= minPeri and perimeter <= maxPeri:
+                    # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高;
+                    x, y, w, h = cv.boundingRect(cnt)
+                    # print u'boundingRect=',x,y,w,h
+                    LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前找到的轮廓='+str((x,y,w,h)))
+                    # 在区域内截图判断;
+                    if cropArea is not None:
+                        x = x + cropArea[0]
+                        y = y + cropArea[1]
+                    contourRect = [x,y,x+w,y+h]
+                    # 如果只找第一个;
+                    if find_1st == True:
+                        result = True
+                        break
+                    # 多个;
+                    listBox.append(contourRect)
+                #endif
+        #endfor
+
+        if bFindMaxContour == True:
+            # 最大轮廓的矩形坐标;
+            x, y, w, h = cv.boundingRect(maxContour)
+            # 在区域内截图判断;
+            if cropArea is not None:
+                x = x + cropArea[0]
+                y = y + cropArea[1]
+            contourRect = [x,y,x+w,y+h]
+
+        if bFindMaxContour == True:
+            return contourRect
+        # 只返回满足条件的第一个值;
+        if find_1st == True:
+            return contourRect if result == True else None
+        else:
+            return listBox
+    #end
+
+    # 区域=焦点=图片;
+    def isNoneFocustArea(self,dict):
+         # 解析参数;
+        Coordinate = dict["ZoneCoordinate"]# 区域坐标;
+        if len(dict["FocusList"]) == 0:
+            return False
+        FocustArea = dict["FocusList"][0]["focus_area"]
+        if list(Coordinate) != list(FocustArea):
+            return False
+        return True
+
+    '''
+        # 描述:获取满足指定周长、面积范围的灰度二值化轮廓;
+        # 参数:
+        # bInside:使用内轮廓
+        # tvShotPath:图片路径
+        # cropArea:裁剪区域, 如:[left,top,right,bottom]
+        # maxGrayVal:最大灰度值
+        # minPeri: 最小周长;
+        # maxPeri: 最大周长;
+        # minArea: 最小面积;
+        # maxArea: 最大面积;
+        # find_1st: 是否只返回满足条件的第一个值
+        #
+        # 注意:当minPeri == maxPeri and minArea == maxArea时,只求最大的轮廓坐标;
+        #
+        # 返回:返回满足指定周长、面积范围的一个或多个轮廓坐标,坐标值如:[left,top,right,bottom];
+        # 
+        # '''
+    def getOTSUBinaryContour(self, tvShotPath, cropArea, maxGrayVal, bInside, minPeri, maxPeri, minArea, maxArea, find_1st = True):
+        contourRect = [0,0,0,0]#结果:x1,y1,x2,y2;
+        img_binary = self.getBinaryImage(tvShotPath, cropArea, 0, maxGrayVal, thresholdType=1)
+       # 获取二值化图像的轮廓;
+        area, perimeter = 0.0, 0.0
+        if bInside == False:#外轮廓
+            contours = cv.findContours(img_binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[1]
+        else:#内轮廓
+            contours = cv.findContours(img_binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)[1]
+        
+        # 是否查找最大轮廓;
+        bFindMaxContour = False
+        if minPeri == maxPeri and minArea == maxArea:
+            bFindMaxContour = True
+
+        listBox = []
+        maxContour = None #最大轮廓;
+        tempArea = 0
+        # 过滤掉不符合要求的轮廓;
+        x, y, w, h,result = 0,0,0,0,False
+        for cnt in contours:
+            # 面积;
+            area = cv.contourArea(cnt)
+            # 周长;
+            perimeter = cv.arcLength(cnt, True)
+
+            if bFindMaxContour == True:
+                # 获取最大轮廓;
+                if tempArea < area:
+                    tempArea = area
+                    maxContour = cnt
+            else:
+                # print area,perimeter
+                # 获取满足条件的值;
+                if area >= minArea and area <= maxArea and perimeter >= minPeri and perimeter <= maxPeri:
+                    # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高;
+                    x, y, w, h = cv.boundingRect(cnt)
+                    # print u'boundingRect=',x,y,w,h
+                    LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前找到的轮廓='+str((x,y,w,h)))
+                    # 在区域内截图判断;
+                    if cropArea is not None:
+                        x = x + cropArea[0]
+                        y = y + cropArea[1]
+                    contourRect = [x,y,x+w,y+h]
+                    # 如果只找第一个;
+                    if find_1st == True:
+                        result = True
+                        break
+                    # 多个;
+                    listBox.append(contourRect)
+                #endif
+        #endfor
+
+        if bFindMaxContour == True:
+            # 最大轮廓的矩形坐标;
+            x, y, w, h = cv.boundingRect(maxContour)
+            # 在区域内截图判断;
+            if cropArea is not None:
+                x = x + cropArea[0]
+                y = y + cropArea[1]
+            contourRect = [x,y,x+w,y+h]
+
+        if bFindMaxContour == True:
+            return contourRect
+        # 只返回满足条件的第一个值;
+        if find_1st == True:
+            return contourRect if result == True else None
+        else:
+            return listBox
+    #end
+
+    '''
+    # focus_type = 4;
+    # dict:区域信息;
+    # bInside:是否使用内轮廓;
+    '''
+    def getSelectFocusArea(self, dict):
+        if self.isNoneFocustArea(dict) == True:
+            return None,None,dict["ZoneCoordinate"]
+        
+        # 获取轮廓;
+        try:
+            bInside = dict["InsideContours"]# 是否使用内轮廓;
+        except:
+            bInside = False
+        contourRect = self.getGrayBinaryContour(
+            dict["TVShotPath"],
+            dict["ZoneCoordinate"],
+            dict["ZoneThreshold"],
+            dict["ZoneMaxThreshold"],
+            bInside,
+            dict["RenderingsMinGirth"],
+            dict["RenderingsMaxGirth"],
+            dict["RenderingsMinArea"],
+            dict["RenderingsMaxArea"])
+        if contourRect == None:
+            # print u'没有找到轮廓区域'
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK-ERROR:没有找到轮廓区域')
+            return None,None,None
+        #endif
+
+        # 遍历区域,找到内包含的区域;
+        x1,y1,x2,y2,result = 0,0,0,0,False
+        allFocus = dict["FocusList"]
+        for focus in allFocus:
+            x1,y1,x2,y2 = focus["focus_area"][0],focus["focus_area"][1],focus["focus_area"][2],focus["focus_area"][3]
+            #是否在效果图的轮廓内
+            if x1 >= contourRect[0] and y1 >= contourRect[1] and x2 <= contourRect[2] and y2 <= contourRect[3]:
+                result = True
+                break
+        #endfor
+
+        if result == False:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:已找到焦点轮廓,但该轮廓未包含任何focus_area在内')
+            return None,None,None
+
+        # 获取当前焦点文本;
+        target_text = self.getCurrentFocusText(dict["TVShotPath"], focus["text_area"][0:4], focus["text_area"][4:])
+        return target_text, focus["text_area"][0:4], focus["focus_area"]
+    #end
+
+    # 是否进入到指定UI界面;
+    def isEntryUI(self, dict):
+        try:
+            bInside = dict["InsideContours"]# 是否使用内轮廓;
+        except:
+            bInside = False
+        contourRect = self.getGrayBinaryContour(
+            dict["TVShotPath"],
+            dict["ZoneCoordinate"],
+            dict["ZoneThreshold"],
+            dict["ZoneMaxThreshold"],
+            bInside,
+            dict["RenderingsMinGirth"],
+            dict["RenderingsMaxGirth"],
+            dict["RenderingsMinArea"],
+            dict["RenderingsMaxArea"])
+        # contourRect = self.getGrayBinaryContour(dict)
+        if contourRect == None:
+            # print u'isEntryUI:没有找到轮廓区域'
+            LoggingUtil.getDebugLogger().info(
+                    pyFileName, 
+                    self.className, 
+                    get_current_function_name(), 
+                    'SDK:没有找到轮廓区域')
+            return False
+        #endif
+        
+        return True
+    #end
+
+    '''
+    # 描述:获取模板匹配度最高的目标。
+    # 参数:
+    # imgObj:图像对象,由cv2.imread()读取
+    # templObj:图库对象,由cv2.imread()读取
+    # 图像对象可以是RGB格式,也可以是GRAY格式,也可以是二值化后的图像对象;
+    '''
+    def templMatch(self, imgObj, templObj, setting = {'method':5, 'colorType':0, 'thresholdVal':0, 'thresholdMaxVal':255, 'matchVal':0.90 }):
+        # 参数判断;
+        if imgObj is None or templObj is None:
+            # print u'图像对象空'
+            LoggingUtil.getDebugLogger().info(
+                    pyFileName, 
+                    self.className, 
+                    get_current_function_name(), 
+                    'SDK:图像对象空')
+            return None,None
+
+        if setting["colorType"] == 1: # gray模式;
+            imgObj = cv.cvtColor(imgObj, cv.COLOR_BGR2GRAY)
+            templObj = cv.cvtColor(templObj, cv.COLOR_BGR2GRAY)
+        elif setting["colorType"] == 2: # threshold模式;
+            # 转成灰阶图;
+            imgObj = cv.cvtColor(imgObj, cv.COLOR_BGR2GRAY)
+            templObj = cv.cvtColor(templObj, cv.COLOR_BGR2GRAY)
+            # 将灰阶转成二值化图像;
+            imgObj = cv.threshold(imgObj, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1]
+            templObj = cv.threshold(templObj, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1]
+
+        # 模板匹配;
+        retVal = cv.matchTemplate(imgObj, templObj, setting["method"])
+        # 最小最大值;
+        min_val, max_val, min_loc, max_loc = cv.minMaxLoc(retVal)
+        if setting["method"] == cv.TM_SQDIFF_NORMED or setting["method"] == cv.TM_SQDIFF:
+            return min_val, min_loc
+        else:
+            return max_val, max_loc
+    
+    '''
+    # 描述:模板有多个匹配目标
+    # imgObj:图像对象,由cv2.imread()读取
+    # templObj:图库对象,由cv2.imread()读取
+    # 图像对象可以是RGB格式,也可以是GRAY格式,也可以是二值化后的图像对象;
+    # '''
+    def templMultiMatch(self, imgObj, templObj, setting = {'method':5, 'colorType':0, 'thresholdVal':0, 'thresholdMaxVal':255, 'matchVal':0.90 }):
+        # 参数判断;
+        if imgObj is None or templObj is None:
+            # print u'图像对象空'
+            LoggingUtil.getDebugLogger().info(
+                    pyFileName, 
+                    self.className, 
+                    get_current_function_name(), 
+                    'SDK:图像对象空')
+            return None
+
+        w,h = templObj.shape[::-1]
+        if setting["colorType"] == 1: # gray模式;
+            imgObj = cv.cvtColor(imgObj, cv.COLOR_BGR2GRAY)
+            templObj = cv.cvtColor(templObj, cv.COLOR_BGR2GRAY)
+        elif setting["colorType"] == 2: # threshold模式;
+            # 转成灰阶图;
+            imgObj = cv.cvtColor(imgObj, cv.COLOR_BGR2GRAY)
+            templObj = cv.cvtColor(templObj, cv.COLOR_BGR2GRAY)
+            # 将灰阶转成二值化图像;
+            imgObj = cv.threshold(imgObj, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1]
+            templObj = cv.threshold(templObj, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1]
+
+        listVal = []
+        # 模板匹配;
+        retVal = cv.matchTemplate(imgObj, templObj, setting["method"])
+        # 匹配的阀值
+        if setting["method"] == cv.TM_SQDIFF_NORMED or setting["method"] == cv.TM_SQDIFF:
+            matchVal = 1 - setting["matchVal"]
+        else:
+            matchVal = setting["matchVal"]
+
+        loc = np.where(retVal > matchVal)
+        # 如果匹配的多个目标是横排序的,过滤掉重影框;
+        listTup = sorted(zip(*loc[::-1]))
+        # 第一个矩形;
+        x1,y1 = listTup[-1][0]-10, listTup[-1][1]-10
+        x2,y2 = x1+w+20, y1+h+20
+        # 中心点是否在矩形内;
+        bInRect = False
+        for i in range(len(listTup)-1, -1, -1): 
+            pt = (listTup[i][0],listTup[i][1])
+            x,y = pt[0]+w/2,pt[1]+h/2
+            if x > x1 and x < x2 and y > y1 and y < y2:
+                if bInRect == False:
+                    bInRect = True
+                else:
+                    listTup.pop(i)
+            else:
+                # print u'外坐标:',pt
+                x1,y1 = pt[0]-10,pt[1]-10
+                x2,y2 = x1+w+10, y1+h+10
+
+        # 如果匹配的多个目标是坚排序的,过滤掉重影框;
+        bInRect = False
+        listTup = sorted(listTup, key=lambda x:x[1])
+        # 第一个矩形;
+        x1,y1 = listTup[0][0]-10, listTup[0][1]-10
+        x2,y2 = x1+w+20, y1+h+20
+        for pt in listTup: 
+            x,y = pt[0]+w/2,pt[1]+h/2
+            if x > x1 and x < x2 and y > y1 and y < y2:
+                if bInRect == False:
+                    bInRect = True
+                    listVal.append([pt[0],pt[1],pt[0]+w,pt[1]+h])
+            else:
+                # 中心点不在矩形内,切换矩形;
+                x1,y1 = pt[0]-10,pt[1]-10
+                x2,y2 = x1+w+10, y1+h+10 
+                listVal.append([pt[0],pt[1],pt[0]+w,pt[1]+h])
+        
+        # 返回多个目标的坐标值;
+        return listVal
+
+    '''
+    # 获取图库目录图像;
+    # dir:图库路径
+    # extlist:保留的后缀文件,都是图像格式。
+    '''
+    def getGallery(self, dir, extlist = ['.png','.bmp','.jpg','jpeg','.tiff']):
+        tmplist = []
+        dir = unicode(dir)
+        if os.path.exists(unicode(dir)) == True:
+            # 读取图库路径里的所有图片;
+            list = os.listdir(dir)
+            # 后缀、路径;
+            path, ext = "", ""
+            # 遍历,过滤掉文件夹和非图像文件;
+            for file in list:
+                path = os.path.join(dir, file)
+                if os.path.isfile(path):
+                    ext = os.path.splitext(path)[1]  # 获取文件后缀 [0]获取的是除了文件名以外的内容
+                    if ext in extlist:
+                        tmplist.append(path)
+            #end for
+        else:
+            # print u'路径异常',dir
+            LoggingUtil.getDebugLogger().info(
+                    pyFileName, 
+                    self.className, 
+                    get_current_function_name(), 
+                    'SDK:路径异常')
+        return tmplist
+
+    '''
+    # 描述:模板匹配,只返回图库中匹配值符合的一个或多个模板的相关数据。
+    # srceenImgPath:原图路径,即TV截图;
+    # ZoneCoordinate:要匹配的区域
+    # galleryDir: 图库路径;
+    # setting: 设定参数
+    #   method -> [cv.TM_CCOEFF, cv.TM_CCOEFF_NORMED, cv.TM_CCORR, cv.TM_CCORR_NORMED, cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED]
+    #   colorType:默认为0,表示使用RGB色彩类型, 1=使用灰度图, 2=表示二值化图像。
+    #   thresholdVal、thresholdMaxVal:当colorType=2时,才使用。
+    #   matchVal:tmplVal比较值。
+    #
+    # 返回值:
+    '''
+    def matchImage(self, srceenImgPath, ZoneCoordinate, galleryDir, setting = {'method':5, 'colorType':0, 'thresholdVal':0, 'thresholdMaxVal':255, 'matchVal':0.90 }):
+        # 判断文件是否存在;
+        tmplist = self.getGallery(galleryDir)
+        if len(tmplist) == 0:
+            # print u'图库目录空'
+            LoggingUtil.getDebugLogger().info(
+                    pyFileName, 
+                    self.className, 
+                    get_current_function_name(), 
+                    'SDK:图库目录空')
+            return None
+
+        srceenImgPath = unicode(srceenImgPath)
+        if os.path.exists(srceenImgPath) == True:
+            # 加载原图;
+            img_src = cv.imread(srceenImgPath.encode("gb18030"))
+            if img_src is None:
+                # print u'读取原图失败'
+                LoggingUtil.getDebugLogger().info(
+                    pyFileName, 
+                    self.className, 
+                    get_current_function_name(), 
+                    'SDK:读取原图失败')
+                return None
+            
+            # 判断匹配区域是否整图,不是截图进行匹配;
+            bUseZoneImg = False
+            # sw,sh = img_src.shape[1],img_src.shape[0]#原图长宽;
+            if ZoneCoordinate is not None and (ZoneCoordinate[2] - ZoneCoordinate[0] != img_src.shape[1] or ZoneCoordinate[3] - ZoneCoordinate[1] != img_src.shape[0]):
+                bUseZoneImg = True
+                zoneImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"zoneImg_"+time.strftime("%Y%m%d%H%M%S", time.localtime())+".png")
+                image_util.saveCropPic(srceenImgPath,zoneImgPath,ZoneCoordinate)
+                img_src = cv.imread(zoneImgPath.encode("gb18030"))
+                if img_src is None:
+                    # print u'截图失败'
+                    LoggingUtil.getDebugLogger().info(
+                        pyFileName, 
+                        self.className, 
+                        get_current_function_name(), 
+                        'SDK:截图失败')
+                    return None
+
+            if setting["colorType"] == 1: # gray模式;
+                img_src = cv.cvtColor(img_src, cv.COLOR_BGR2GRAY)
+            elif setting["colorType"] == 2: # threshold模式;
+                img_src = cv.cvtColor(img_src, cv.COLOR_BGR2GRAY)
+                # 将灰阶转成二值化图像;
+                img_src = cv.threshold(img_src, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1]
+
+            #加载要搜索的图像模板;
+            w,h = 0, 0
+            img_tmp = None
+            tmpVal, tmpLoc = 0,[0,0]
+            # listResult = []
+            result = {"tmpVal":0, "galleryFile":"", "coordinate":[0,0,0,0]}
+            # 遍历图库,找到最合适的
+            for file in tmplist:
+                # result = {"result": False, "tmpVal":0, "galleryFile":"", "coordinate":[0,0,0,0]}
+                # result = {"tmpVal":0, "galleryFile":"", "coordinate":[0,0,0,0]}
+                img_tmp = cv.imread(file.encode('gb18030'))
+                if img_tmp is None:
+                    # print u'模板图库相片加载失败'
+                    LoggingUtil.getDebugLogger().info(
+                        pyFileName, 
+                        self.className, 
+                        get_current_function_name(), 
+                        'SDK:模板图库相片加载失败')
+                    continue
+
+                if setting["colorType"] == 1: # gray模式;
+                    img_tmp = cv.cvtColor(img_tmp, cv.COLOR_BGR2GRAY)
+                elif setting["colorType"] == 2: # threshold模式;
+                    img_tmp = cv.cvtColor(img_tmp, cv.COLOR_BGR2GRAY)
+                    # 将灰阶转成二值化图像;
+                    img_tmp = cv.threshold(img_tmp, setting["thresholdVal"], setting["thresholdMaxVal"], cv.THRESH_BINARY)[1]
+
+                # 模板匹配;
+                w,h = img_tmp.shape[1], img_tmp.shape[0]
+                tmpVal, tmpLoc = self.templMatch(img_src, img_tmp, {'method':setting["method"], 'colorType':0 })
+                if bUseZoneImg == True:
+                    x,y = tmpLoc[0] + ZoneCoordinate[0],tmpLoc[1] + ZoneCoordinate[1]
+                else:
+                    x,y = tmpLoc[0], tmpLoc[1]
+                if setting["method"] == cv.TM_SQDIFF_NORMED or setting["method"] == cv.TM_SQDIFF:
+                    # if result["tmpVal"] > tmpVal:
+                    if tmpVal < (1-setting["matchVal"]):
+                        if result["tmpVal"] < 1-tmpVal:
+                            result["tmpVal"] = 1-tmpVal
+                            result["coordinate"] = [x,y,x+w,y+h]
+                            result["galleryFile"] = file
+                        # break
+                else:
+                    # if result["tmpVal"] < tmpVal:
+                    if tmpVal > setting["matchVal"]:
+                        if result["tmpVal"] < tmpVal:
+                            result["tmpVal"] = tmpVal
+                            result["coordinate"] = [x,y,x+w,y+h]
+                            result["galleryFile"] = file
+                        # break
+                
+                # if setting["method"] == cv.TM_SQDIFF_NORMED or setting["method"] == cv.TM_SQDIFF:
+                #     if result["tmpVal"] < (1 - setting["matchVal"]):
+                #         listResult.append(result)
+                # else:
+                #     if result["tmpVal"] > setting["matchVal"]:
+                #         listResult.append(result)
+                # print result["result"], result["tmpVal"],result["coordinate"],result["galleryFile"].decode('gb18030'),
+            #end for
+            if result["tmpVal"] == 0:
+                LoggingUtil.getDebugLogger().info(
+                        pyFileName, 
+                        self.className, 
+                        get_current_function_name(), 
+                        'SDK:模板匹配度为0')
+                return None
+            return result
+            # return listResult
+        else:
+            # print u'文件不存在'
+            LoggingUtil.getDebugLogger().info(
+                        pyFileName, 
+                        self.className, 
+                        get_current_function_name(), 
+                        'SDK:文件不存在')
+            return None
+    
+    '''
+    # 描述:单一模板匹配;
+    # 参数:
+    # srceenImgPath:原图路径,即TV截图;
+    # ZoneCoordinate:要匹配的区域
+    # templImgPath:单模板文件路径
+    # setting:设定值
+    # 注意:templ对象,在screenImg中可能存在多个匹配度符合要求的区域。
+    '''
+    def matchSingleImage(self, srceenImgPath, ZoneCoordinate, templImgPath, setting = {'method':5, 'colorType':0, 'thresholdVal':0, 'thresholdMaxVal':255, 'matchVal':0.90 }):
+        srceenImgPath = unicode(srceenImgPath)
+        templImgPath = unicode(templImgPath)
+        if os.path.exists(srceenImgPath) == True and os.path.exists(templImgPath) == True:
+            # 加载原图;
+            srceenImg = cv.imread(srceenImgPath.encode("gb18030"))
+            if srceenImg is None:
+                # print u'读取原图失败'
+                LoggingUtil.getDebugLogger().info(
+                        pyFileName, 
+                        self.className, 
+                        get_current_function_name(), 
+                        'SDK:读取原图失败')
+                return None
+
+            # 判断匹配区域是否整图,不是截图进行匹配;
+            bUseZoneImg = False
+            # sw,sh = srceenImg.shape[1],srceenImg.shape[0]#原图长宽;
+            if ZoneCoordinate is not None and (ZoneCoordinate[2] - ZoneCoordinate[0] != srceenImg.shape[1] or ZoneCoordinate[3] - ZoneCoordinate[1] != srceenImg.shape[0]):
+                bUseZoneImg = True
+                zoneImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"zoneImg_"+time.strftime("%Y%m%d%H%M%S", time.localtime())+".png")
+                image_util.saveCropPic(srceenImgPath,zoneImgPath,ZoneCoordinate)
+                srceenImg = cv.imread(zoneImgPath.encode("gb18030"))
+                if srceenImg is None:
+                    # print u'截图失败'
+                    LoggingUtil.getDebugLogger().info(
+                        pyFileName, 
+                        self.className, 
+                        get_current_function_name(), 
+                        'SDK:截图失败')
+                    return None
+            
+            if ZoneCoordinate is None or ZoneCoordinate is []:
+                ZoneCoordinate = [0, 0, srceenImg.shape[1], srceenImg.shape[0]]
+
+            #加载图像模板;
+            w,h = 0, 0
+            img_tmp = None
+            tmpVal, tmpLoc = 0,[0,0]
+            result = {"tmpVal":0, "coordinate":[0,0,0,0]}
+            # 对比图库
+            img_tmp = cv.imread(templImgPath.encode('gb18030'))
+            if img_tmp is None:
+                # print u'模板图库相片加载失败'
+                LoggingUtil.getDebugLogger().info(
+                        pyFileName, 
+                        self.className, 
+                        get_current_function_name(), 
+                        'SDK:模板图库相片加载失败')
+                return None
+
+            # 模板匹配;
+            w,h = img_tmp.shape[1], img_tmp.shape[0]
+            tmpVal, tmpLoc = self.templMatch(srceenImg, img_tmp, setting)
+            result["tmpVal"] = tmpVal
+            if bUseZoneImg == True:
+                x,y = tmpLoc[0] + ZoneCoordinate[0],tmpLoc[1] + ZoneCoordinate[1]
+                result["coordinate"] = [x,y,x+w,y+h]
+            else:
+                result["coordinate"] = [tmpLoc[0],tmpLoc[1],tmpLoc[0]+w,tmpLoc[1]+h]
+
+            if setting["method"] == cv.TM_SQDIFF_NORMED or setting["method"] == cv.TM_SQDIFF:
+                if result["tmpVal"] < (1-setting["matchVal"]):
+                    return result
+            else:
+                if result["tmpVal"] > setting["matchVal"]:
+                    return result
+            # print result["result"], result["tmpVal"]
+            return None
+        else:
+            # print u'文件不存在'
+            LoggingUtil.getDebugLogger().info(
+                        pyFileName, 
+                        self.className, 
+                        get_current_function_name(), 
+                        'SDK:文件不存在')
+            return None
+
+    '''
+        # 描述:获取矩形1在矩形2的方位;
+        # 参数:box1,box2矩形。
+        # 返回值:
+        # 左上:0 正上:1 右上:2
+        # 正左:3 正右:4
+        # 左下:5 正下:6 右下:7
+        # '''
+    def getOrientation(self, box1, box2):
+        # 矩形长宽;
+        w1,h1 = box1[2]-box1[0],box1[3]-box1[1]
+        # w2,h2 = box2[2]-box2[0],box2[3]-box2[1]
+
+        # 矩形中心点;
+        cx1,cy1 = box1[0]+w1/2,box1[1]+h1/2
+        # cx2,cy2 = box2[0]+w2/2,box2[1]+h2/2
+
+        # box1在box2右方;
+        if box1[0] >= box2[2]:
+            # 正右方;
+            if cy1 > box2[1] and cy1 < box2[3]:
+                return 4
+            # 右上方;
+            if cy1 < box2[1]:
+                return 2
+            # 右下方;
+            if cy1 > box2[3]:
+                return 7
+
+        # box1在box2下方;
+        if box1[1] >= box2[3]:
+            # 正下方;
+            if cx1 > box2[0] and cx1 < box2[2]:
+                return 6
+            # 左下方;
+            if cx1 < box2[0]:
+                return 5 
+            # 右下方;
+            if cx1 > box2[2]:
+                return 7
+
+        # box1在box2左方;
+        if box1[2] <= box2[0]:
+            # 正左方;
+            if cy1 > box2[1] and cy1 < box2[3]:
+                return 3
+            # 左上方;
+            if cy1 < box2[1]:
+                return 0
+            # 左下方;
+            if cy1 > box2[3]:
+                return 5
+
+        # box1在box上方;
+        if box1[3] <= box2[1]:
+            # 正上方;
+            if cx1 > box2[0] and cx1 < box2[2]:
+                return 1
+            # 左上方;
+            if cx1 < box2[0]:
+                return 0
+            # 右上方;
+            if cx1 > box2[2]:
+                return 2
+        
+        # box1在box2里面;
+        return -1
+    
+    '''
+        # 描述:由两矩形的中心点来判别方位;
+        # 
+        # 
+        # '''
+    def getOrientationEx(self, box1, box2):
+        # 矩形长宽;
+        w1,h1 = box1[2]-box1[0],box1[3]-box1[1]
+        # w2,h2 = box2[2]-box2[0],box2[3]-box2[1]
+
+        # 矩形中心点;
+        cx1,cy1 = box1[0]+w1/2,box1[1]+h1/2
+        # cx2,cy2 = box2[0]+w2/2,box2[1]+h2/2
+
+        # box1在box2右方;
+        if cx1 > box2[2]:
+            # 正右方;
+            if cy1 > box2[1] and cy1 < box2[3]:
+                return 4
+            # 右上方;
+            if cy1 < box2[1]:
+                return 2
+            # 右下方;
+            if cy1 > box2[3]:
+                return 7
+
+        # box1在box2下方;
+        if cy1 > box2[3]:
+            # 正下方;
+            if cx1 > box2[0] and cx1 < box2[2]:
+                return 6
+            # 左下方;
+            if cx1 < box2[0]:
+                return 5 
+            # 右下方;
+            if cx1 > box2[2]:
+                return 7
+
+        # box1在box2左方;
+        if cx1 < box2[0]:
+            # 正左方;
+            if cy1 > box2[1] and cy1 < box2[3]:
+                return 3
+            # 左上方;
+            if cy1 < box2[1]:
+                return 0
+            # 左下方;
+            if cy1 > box2[3]:
+                return 5
+
+        # box1在box上方;
+        if cy1 < box2[1]:
+            # 正上方;
+            if cx1 > box2[0] and cx1 < box2[2]:
+                return 1
+            # 左上方;
+            if cx1 < box2[0]:
+                return 0
+            # 右上方;
+            if cx1 > box2[2]:
+                return 2
+        
+        # box1在box2里面;
+        return -1
+
+    '''
+        # 描述:生成网络数据。
+        # 参数:一组矩形坐标。
+        # 返回值:分组的矩形坐标;
+        # 注意:
+        #   给定的一组矩形,如果不是继续的一组网格矩形,无法判断缺少的矩形位置.
+        # 
+        # '''
+    def generateGrid(self,listRects):
+        ret = 0
+        # 1、先上下排序,分层级;        
+        for i in range(len(listRects)-1):
+            for j in range(0, len(listRects)-i-1):
+                ret = self.getOrientation(listRects[j], listRects[j+1])
+                if ret in [5,6,7]:
+                    listRects[j], listRects[j+1] = listRects[j+1], listRects[j]
+        
+        # 2、层级分组;
+        listLyaer = []
+        lastIndex = 0
+        for i in range(0,len(listRects)-1):
+            ret = self.getOrientation(listRects[i],listRects[i+1])
+            if ret in (0,1,2):
+                # 将同一行的所有网格添加为一个数组元素;
+                listLyaer.append(listRects[lastIndex:i+1])
+                lastIndex = i+1
+
+        # 如果有最后一个,自成一组。
+        # if i < len(listRects)-1:
+        if lastIndex < len(listRects):
+            listLyaer.append(listRects[lastIndex::])
+
+        # 3、分组后,再每一组排序;
+        for row in listLyaer:
+            for i in range(len(row)-1):
+                for j in range(0, len(row)-i-1):
+                    ret = self.getOrientation(row[j], row[j+1])
+                    if ret == 4:
+                        row[j], row[j+1] = row[j+1], row[j]
+        
+        # 4、返回结果。
+        return listLyaer
+
+    '''
+        # 描述:查找指定矩形在网格中的坐标;
+        # 参数:
+        # rect:要查找的盒子坐标;
+        # listGrid:网格
+        # 
+        # 返回:行、列、是否边界(0:未到边界,1:右边界,-1:左边界;2:最后一格; -2:最前一格; 3:最后一格且该行只有一格)
+        #
+        # '''
+    def getGridCoordinate(self, rect, listGrid):
+        result,boundary = False,0
+        try:
+            grid,cell = None,None
+            for row in range(0,len(listGrid)):
+                grid = listGrid[row]
+                for col in range(0,len(grid)):
+                    cell = grid[col]
+                    # 包含在内;
+                    if rect[0] <= cell[0] and rect[1] <= cell[1] and rect[2] >= cell[2] and rect[3] >= cell[3]:
+                        result = True
+                        if col == len(grid)-1:
+                            boundary = 1 #已到右边界;
+                        elif col == 0:
+                            boundary = -1 #左边界;
+                        break
+                if result == True:
+                    break
+        except Exception, e:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:循环异常'+str(e))
+        
+        if result == True:
+            # 再判断,是否是最后一格;
+            if row == len(listGrid) - 1 and col == len(listGrid[len(listGrid) - 1]) - 1:
+                boundary = 2 # 最后一格;
+                if col == 0:#单格一行;
+                    boundary = 3
+                print u'最后一格:'.encode('gbk'),row,col,boundary
+            # 判断是不是最前一格;
+            if row == 0 and col == 0:
+                boundary = -2
+                print u'最前一格:'.encode('gbk'),row,col,boundary
+            return row,col,boundary
+        else:
+            return -1,-1,boundary
+
+    '''
+        # 描述:顺(正)序查找目标焦点框
+        # 参数:
+        # 
+        # 
+        # '''
+    def sequentialSearch(self, curRow, curBoundary, remoteCtrlTimes, listRect, listGrid, dict, findFunc):
+        row,boundary,keyDirection = 0,0,True
+        curText,curTextArea,curFocusArea = None,None,None
+        ctrl_times = int((1 + 0.5)*remoteCtrlTimes + 2) 
+        if curBoundary == 1:#右边界。
+            # 向下遥控;
+            self.redRat3.sendKey('down')
+            keyDirection = False # 向左遥控;
+            # 截图;
+            dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_"+time.strftime("%Y%m%d%H%M%S", time.localtime())+".png")
+            self.vp.takePicture(dict["TVShotPath"])
+            ocr_result, curFocusArea = findFunc(dict)
+            if ocr_result == True:
+                return True, curFocusArea
+        
+        result = False
+        for i in range(ctrl_times):
+            if keyDirection == True:
+                #向右遥控;
+                self.redRat3.sendKey('right')
+            else:
+                #向左遥控;
+                self.redRat3.sendKey('left')
+
+            # 按键后,获取当前焦点;
+            dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+            self.vp.takePicture(dict["TVShotPath"])
+            ocr_result, curFocusArea = findFunc(dict)
+            if ocr_result == True:
+                result = True
+                break
+            # 不是目标焦点,继续遥控;
+            row,col,boundary = self.getGridCoordinate(curFocusArea,listGrid)
+            print u'curRow,curCol,curBoundary',row,col,boundary
+            if boundary == 1 or boundary == -1:
+                # 向下遥控;
+                self.redRat3.sendKey('down')
+                keyDirection = True if boundary == -1 else False # 向左或向右遥控;
+                dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+                self.vp.takePicture(dict["TVShotPath"])
+                ocr_result, curFocusArea = findFunc(dict)
+                if ocr_result == True:
+                    result = True
+                    break
+        
+        return result, curFocusArea
+
+    '''
+        # 描述:反序查找目标焦点框;
+        # 参数:
+        # 
+        # 
+        # '''
+    def reverseSearch(self, curRow, curBoundary, remoteCtrlTimes, listRect, listGrid, dict, findFunc):
+        row,boundary,keyDirection = 0,0,False
+        curText,curTextArea,curFocusArea = None,None,None
+        ctrl_times = int((1 + 0.5)*remoteCtrlTimes + 2) 
+        if curBoundary == -1:#左边界;
+            # 向下遥控;
+            self.redRat3.sendKey('up')
+            keyDirection = True # 向右遥控;
+            # 截图;
+            dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_"+time.strftime("%Y%m%d%H%M%S", time.localtime())+".png")
+            self.vp.takePicture(dict["TVShotPath"])
+            ocr_result, curFocusArea = findFunc(dict)
+            if ocr_result == True:
+                return True, curFocusArea
+        
+        result = False
+        for i in range(ctrl_times):
+            if keyDirection == True:
+                #向右遥控;
+                self.redRat3.sendKey('right')
+            else:
+                #向左遥控;
+                self.redRat3.sendKey('left')
+
+            # 按键后,获取当前焦点;
+            dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+            self.vp.takePicture(dict["TVShotPath"])
+            ocr_result, curFocusArea = findFunc(dict)
+            if ocr_result == True:
+                result = True
+                break
+            # 不是目标焦点,继续遥控;
+            row,col,boundary = self.getGridCoordinate(curFocusArea,listGrid)
+            print u'curRow,curCol,curBoundary',row,col,boundary
+            if boundary == 1 or boundary == -1:
+                # 向上遥控;
+                self.redRat3.sendKey('up')
+                keyDirection = False if boundary == 1 else True # 向左或向右遥控;
+                dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+                self.vp.takePicture(dict["TVShotPath"])
+                ocr_result, curFocusArea = findFunc(dict)
+                if ocr_result == True:
+                    result = True
+                    break
+        
+        return result, curFocusArea
+
+    def SearchEx(self, listGrid, dict, findFunc, searchType = True):
+        result = False
+        # 按键方向:True->右;False->左;
+        keyDirection = False
+        # 上一行行号;
+        lastRow = -1 
+        # 遍历尾行的行号;
+        endRow = 0 
+        #下一行按键;
+        nextRow = 'up'
+        # 以上是逆序时的预设值;
+        if searchType == True: # 正序查找;
+            endRow = len(listGrid)-1
+            keyDirection = True
+            nextRow = 'down'
+
+        while True:
+            # 获取当前坐标(x,y)及边界性;
+            dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+            self.vp.takePicture(dict["TVShotPath"])
+            ocr_result, curFocusArea = findFunc(dict)
+            if curFocusArea is None:
+                break
+            if ocr_result == True:
+                result = True
+                break
+            row, col, boundary = self.getGridCoordinate(curFocusArea, listGrid)
+            # 判断是否遍历完最后一行;
+            if lastRow == endRow:
+                # 根据按键方向判断是否到达尾行尾格;
+                if keyDirection and col == len(listGrid[row])-1:
+                    break
+                if not keyDirection and col == 0:
+                    break
+            # 记录当前行;
+            lastRow = row
+
+            if boundary == 0:
+                # 没找到,遥控下一格;
+                if keyDirection == True:
+                    self.redRat3.sendKey('right')
+                else:
+                    self.redRat3.sendKey('left')
+            elif boundary == 1:#最右
+                if keyDirection == True:
+                    keyDirection = not keyDirection
+                    self.redRat3.sendKey(nextRow)
+                else:
+                    self.redRat3.sendKey('left')
+            elif boundary == 2:#最后一行最后一格;(该行有多格)
+                if searchType == True:
+                    break#结束遍历;
+                else:
+                    if keyDirection == False:
+                        self.redRat3.sendKey('left')
+            elif boundary == 3:#最后一行最后一格;(该行只有一格)
+                if searchType == True:
+                    break#结束遍历;
+                else:
+                    keyDirection = not keyDirection
+                    self.redRat3.sendKey(nextRow)
+            elif boundary == -1:#最左;
+                if keyDirection == False:
+                    keyDirection = not keyDirection
+                    self.redRat3.sendKey(nextRow)
+                else:
+                    self.redRat3.sendKey('right')
+            elif boundary == -2:#最前一格;
+                if searchType == False:
+                    break#结束遍历;
+                else:
+                    if keyDirection == True:
+                        self.redRat3.sendKey('right')
+
+        return result
+
+    '''
+        # 描述:到达网格的第一个盒子焦点;
+        # 
+        # 
+        # 
+        # '''
+    def reach1stBox(self, listGrid, cropArea, grayVal, maxGrayVal, bInside, minPeri, maxPeri, minArea, maxArea, find_1st = True): 
+        result = True              
+        while True:
+            # 当前焦点位置;
+            curFocusBox = self.findCurrentFocusBoxInType04(cropArea,grayVal,maxGrayVal,bInside,minPeri,maxPeri,minArea,maxArea)[0]
+            if curFocusBox is None:
+                result = False
+                break
+
+            # 获取行列坐标标;
+            row, col, boundary = self.getGridCoordinate(curFocusBox, listGrid)
+            if row == 0 and col == 0:# and boundary == -1:
+                result = True
+                break
+
+            if row > 0:
+                self.redRat3.sendKey('up')
+            elif row == 0 and boundary != -2:
+                self.redRat3.sendKey('left', col, 0.5)
+
+        return result
+
+    '''
+        # 描述:查找当前焦点框信息;
+        # 
+        # 
+        # 
+        # '''
+    def findCurrentFocusFrameInType6(self, dict):
+        # 1、获取当前轮廓;
+        try:
+            bInside = dict["InsideContours"]# 是否使用内轮廓;
+        except:
+            bInside = False
+        contourRect = self.getGrayBinaryContour(
+            dict["TVShotPath"],
+            dict["ZoneCoordinate"],
+            dict["ZoneThreshold"],
+            dict["ZoneMaxThreshold"],
+            bInside,
+            dict["RenderingsMinGirth"],
+            dict["RenderingsMaxGirth"],
+            dict["RenderingsMinArea"],
+            dict["RenderingsMaxArea"])
+        if contourRect == None:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:没有找到轮廓区域')
+            return False,None
+        #endif
+
+        # 计算文本位置;
+        realTextBox = self.findCurrentFocusTextBox(contourRect, dict["RenderingsArea"], dict["RenderingsTextArea"][0:4])
+        if realTextBox is None:
+            return False,contourRect
+
+        ocrImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"OCR_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        image_util.saveCropPic(dict['TVShotPath'], ocrImgPath, realTextBox)
+        result, realTextBox = self.OCR.findPicStr(dict['TargetText'], ocrImgPath, dict['RenderingsTextArea'][4],dict['RenderingsTextArea'][5],dict['RenderingsTextArea'][6],dict['RenderingsTextArea'][7])
+        return result, contourRect
+
+    '''
+        # 描述:查找目标焦点框之type6.
+        # 参数:
+        # 
+        # 返回值:成功找到焦点返回True,否则返回False;
+        # 
+        # '''
+    def findTargetFocusFrameInType6(self,dict):    
+        # 1、计算出几行几列;
+        listRect = []
+        for focus in dict["FocusList"]:
+            listRect.append(focus["focus_area"])
+        # 生成网格;
+        listGrid = self.generateGrid(listRect)
+        # 正反序查找;
+        result = self.SearchEx(listGrid, dict, self.findCurrentFocusFrameInType6, True)
+        if result == False:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:逆序查找')
+            result = self.SearchEx(listGrid, dict, self.findCurrentFocusFrameInType6, False)
+
+        return result
+    #end
+
+    '''
+        # 描述:图像形态学处理。
+        # 
+        # 
+        # '''
+    def preProcess(self, grayImage,setting):
+        # 1、sobel算子,x方向求梯度;
+        sobel = cv.Sobel(grayImage, cv.CV_8U, 1, 0, ksize = 3)
+        # 2、二值化;
+        if 'gray' in setting:
+            binary = cv.threshold(sobel, setting['gray'], 255, cv.THRESH_BINARY+cv.THRESH_OTSU)[1]
+        else:
+            binary = cv.threshold(sobel, 0, 255, cv.THRESH_BINARY+cv.THRESH_OTSU)[1]
+
+        binary_path = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"binary_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        cv.imwrite(binary_path, binary)  
+
+        # 第一次;
+        first,second,third = None,None,None
+        ksize = (1,1)
+        ksize = tuple(setting["first"][1:3])
+        kernel = cv.getStructuringElement(setting["first"][3], ksize)
+        if setting["first"][0] == "dilate":
+            first = cv.dilate(binary, kernel, setting["first"][4])
+        else:
+            first = cv.erode(binary, kernel, setting["first"][4])
+
+        # 第二次;
+        second = None
+        if 'second' in setting and len(setting["second"]) != 0:
+            ksize = tuple(setting["second"][1:3])
+            kernel = cv.getStructuringElement(setting["second"][3], ksize)
+            if setting["second"][0] == "dilate":
+                second = cv.dilate(first, kernel, setting["second"][4])
+            else:
+                second = cv.erode(first, kernel, setting["second"][4])
+        else:
+            second = first
+
+        # 第三次;
+        third = None
+        if "third" in setting and len(setting["third"]) != 0:
+            ksize = tuple(setting["third"][1:3])
+            kernel = cv.getStructuringElement(setting["third"][3], ksize)
+            if setting["third"][0] == "dilate":
+                third = cv.dilate(second, kernel, setting["third"][4])
+            else:
+                third = cv.erode(second, kernel, setting["third"][4])
+        else:
+            third = second
+
+        # 7. 存储中间图片
+        tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"binary_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        cv.imwrite(tmpdir, binary)
+        tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"first_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        cv.imwrite(tmpdir, first)
+        if second is not None:
+            tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"second_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+            cv.imwrite(tmpdir, second)
+        if third is not None:
+            tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"third_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+            cv.imwrite(tmpdir, third)
+
+        return third if third is not None else second
+    
+    '''
+        # 描述:查找文字区域
+        # 
+        # 
+        # 
+        # '''
+    def findTextRegion(self, img, minGirth, maxGirth, minArea, maxArea):
+        # 1. 查找轮廓
+        contours = cv.findContours(img, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)[1]
+        if len(contours) == 0:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:没有找文本到轮廓')
+            return None
+    
+        # 2. 过滤
+        listCnt = []
+        maxBox, maxCnt = 0, None
+        for i in range(len(contours)):
+            cnt = contours[i]
+            # 计算该轮廓的面积
+            area = cv.contourArea(cnt) 
+            perimeter = cv.arcLength(cnt, True)
+
+            # 符合结果的,保存在list中;
+            if area > minArea and area < maxArea and perimeter > minGirth and perimeter < maxGirth:
+                listCnt.append(cnt)
+        
+        if len(listCnt) == 0:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:没有找到符合目标的轮廓')
+            return None
+    
+        # 只保留面积最大的;
+        for i in range(len(listCnt)):
+            cnt = listCnt[i]
+            # 计算该轮廓的面积
+            area = cv.contourArea(cnt) 
+            if(maxBox < area):
+                maxBox = area
+                maxCnt = cnt
+
+        # 找到最小的矩形,该矩形可能有方向
+        rect = cv.minAreaRect(maxCnt)
+        print ("rect is: ",rect)
+
+        # box是四个点的坐标
+        box = cv.boxPoints(rect)
+        box = np.int0(box)
+
+        # print 'box',box
+        if rect[2] > -45.0:
+            rect = [box[1][0],box[1][1],box[3][0],box[3][1],]
+        else:#-90
+            rect = [box[2][0],box[2][1],box[0][0],box[0][1],]
+
+        return rect
+
+    '''
+        # 描述:查找当前焦点的文本轮廓。
+        # 参数:
+        # 
+        # 
+        # 返回值:返回文本内容。
+        # 
+        # '''
+    def findTextcontourRect(self, tvShotPath, dict):
+        coordinate = dict["ZoneCoordinate"]
+        thresholdVal = dict["ZoneThreshold"]
+        maxThresholdVal = dict["ZoneMaxThreshold"]
+        try:
+            maxGirth = dict["RenderingsMaxGirth"]# 效果图周长上限;(最大值)
+            minGirth = dict["RenderingsMinGirth"]# 效果图周长下限;(最小值)
+            maxArea = dict["RenderingsMaxArea"]# 效果图面积上限;(最大值)
+            minArea = dict["RenderingsMinArea"]# 效果图面积下限;(最小值)
+        except:
+            maxGirth = 1000000# 效果图周长上限;(最大值)
+            minGirth = 10# 效果图周长下限;(最小值)
+            maxArea = 1000000# 效果图面积上限;(最大值)
+            minArea = 100# 效果图面积下限;(最小值)
+        ocrtype = dict["ocrtype"]
+        language = dict["language"]
+        setting = dict["setting"]
+        # 按区域截图,在区域中查找轮廓。
+        zoneImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"zoneImg_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        image_util.saveCropPic(tvShotPath, zoneImgPath, coordinate)
+        imgTV = cv.imread(zoneImgPath.encode("gb18030"))
+
+        # 判断对象有效性;
+        if imgTV is None:
+            # print u'效果图对象空,可能文件路径无效'
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:效果图对象空,可能文件路径无效')
+            return None,None
+        
+        # 将截图转成灰阶;
+        imgGray = cv.cvtColor(imgTV, cv.COLOR_BGR2GRAY)
+        # 高斯模糊;
+        imgGray = cv.GaussianBlur(imgGray, (3, 3), 0)
+        # 将灰阶转成二值化图像;
+        thresh = cv.threshold(imgGray, thresholdVal, maxThresholdVal, cv.THRESH_BINARY)[1]
+        tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"thresh_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        cv.imwrite(tmpdir, thresh)
+
+        # 图像形态学处理;
+        dilation = self.preProcess(thresh,setting)
+        # 查找和筛选文字区域
+        text_area = self.findTextRegion(dilation,minGirth,maxGirth,minArea,maxArea)
+
+        if text_area is None:
+            return None,None
+
+        ocrImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"OCR_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        image_util.saveCropPic(dict['TVShotPath'], ocrImgPath, text_area)
+        ocr_result, ocr_text = self.OCR.findPicStr(dict['TargetText'], ocrImgPath, language, ocrtype, dict['ocrImgInfo'],dict['ignoreChar'])
+        
+        return ocr_result, ocr_text
+
+
+    '''
+        # 描述:查找当前焦点框之type7.
+        # 参数:
+        # 
+        # 返回:
+        #
+        # 注意:只适用于左右遥控按键.
+        #  
+        # '''
+    def findCurrentFocusFrameInType7(self,dict):
+        if 'setting' not in dict:
+            dict["setting"] = {
+                "first":["erode",2,2,0,1],      # 第一次操作是腐蚀还是膨胀,内核大小,内核形状,操作次数
+                "second":["dilate",30,9,0,1],   # 内核大小,内核形状,操作次数
+                "third":["dilate",24,10,0,2]    # 内核大小,内核形状,操作次数
+            }
+        
+        # 先正序查找;
+        result = False
+        target_text,last_text = '',''
+        sendTimes = dict["SendTimes"]
+        changeTimes = -1
+        while sendTimes > 0:
+            # 电视截图;
+            TVShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+            self.vp.takePicture(TVShotPath)
+            # 查找文本轮廓
+            ocr_result, target_text = self.findTextcontourRect(TVShotPath, dict)
+            if ocr_result == True:
+                result = True
+                break
+            # 发送右按钮;
+            self.redRat3.sendKey('right')
+            sendTimes -= 1
+            if last_text != target_text:
+                last_text = target_text
+                changeTimes += 1
+
+        # 正序未找到,逆序查找;
+        if result == False:
+            #向左发送changeTimes+1次,回到原点;
+            keys = ['left']*(changeTimes+1)
+            self.redRat3.sendKeys(keys)
+            sendTimes = dict["SendTimes"]
+            while sendTimes > 0:
+                # 电视截图;
+                TVShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+                self.vp.takePicture(TVShotPath)
+                ocr_result, target_text = self.findTextcontourRect(TVShotPath, dict)
+                if ocr_result == True:
+                    result = True
+                    break
+                # 发送左按钮;
+                self.redRat3.sendKey('left')
+                sendTimes -= 1
+        
+        return result
+
+    def getHSVBinaryContour(self, tvShotPath, cropArea, hsvVal, maxHsvVal, bInside, minPeri, maxPeri, minArea, maxArea, find_1st = True):
+        contourRect = [0,0,0,0]#结果:x1,y1,x2,y2;
+        # 判断文件是否存在;
+        if os.path.exists(tvShotPath) == False:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片路径不存在'+tvShotPath)
+            return None
+        
+        # 打开图片;
+        img_tvShot = cv.imread(tvShotPath)
+        if img_tvShot is None:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片对象空')
+            return None
+        
+        # 只保留区域部分;
+        try:
+            img_crop = np.array(img_tvShot[cropArea[1]:cropArea[3], cropArea[0]:cropArea[2]])
+        except:
+            img_crop = img_tvShot
+        cv.imwrite(LoggingUtil.getCaseRunLogDirPath() + "crop_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png", img_crop)
+
+        # 判断对象有效性;
+        if img_crop is None:
+            # print u'效果图对象空,可能文件路径无效'
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:区域截图对象空,可能文件路径无效')
+            return None
+                
+        # 将截图转成灰阶;
+        img_hsv = cv.cvtColor(img_crop, cv.COLOR_BGR2HSV)
+        # 根据参数的阀值得到特定颜色区域;
+        img_binary = cv.inRange(img_hsv, tuple(hsvVal),tuple(maxHsvVal))
+         # 图像形态学内核对象;
+        kernerl = cv.getStructuringElement(cv.MORPH_RECT, ksize=(3,3))
+        # 开操作,去噪点;
+        img_binary = cv.morphologyEx(img_binary,cv.MORPH_OPEN,kernerl)
+        # 闭操作,连通域;
+        img_binary = cv.morphologyEx(img_binary,cv.MORPH_CLOSE,kernerl)
+
+        # 获取二值化图像的轮廓;
+        area, perimeter = 0.0, 0.0
+        if bInside == False:#外轮廓
+            contours, hierarchy = cv.findContours(img_binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[1:3]
+        else:#内轮廓
+            contours, hierarchy = cv.findContours(img_binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)[1:3]
+        
+        listBox = []
+        # 过滤掉不符合要求的轮廓;
+        x, y, w, h,result = 0,0,0,0,False
+        for cnt in contours:
+            # 面积;
+            area = cv.contourArea(cnt)
+            # 周长;
+            perimeter = cv.arcLength(cnt, True)
+            # 获取满足条件的值;
+            if area >= minArea and area <= maxArea and perimeter >= minPeri and perimeter <= maxPeri:
+                # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高;
+                x, y, w, h = cv.boundingRect(cnt)
+                # print u'boundingRect=',x,y,w,h
+                LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前找到的轮廓='+str((x,y,w,h)))
+                # 判断矩形是否包含在区域内;
+                # 在区域内截图判断;
+                x = x + cropArea[0]
+                y = y + cropArea[1]
+                contourRect = [x,y,x+w,y+h]
+                # 如果只找第一个;
+                if find_1st == True:
+                    result = True
+                    break
+                # 多个;
+                listBox.append(contourRect)
+            #endif
+        #endfor
+        # 只返回满足条件的第一个值;
+        if find_1st == True:
+            return contourRect if result == True else None
+        else:
+            return listBox
+    #end
+
+    def findCurrentFocusFrameInType8(self,dict):
+        # 1、获取当前轮廓;
+        try:
+            bInside = dict["InsideContours"]# 是否使用内轮廓;
+        except:
+            bInside = False
+        contourRect = self.getHSVBinaryContour(
+            dict["TVShotPath"],
+            dict["ZoneCoordinate"],
+            dict["ZoneThreshold"],
+            dict["ZoneMaxThreshold"],
+            bInside,
+            dict["RenderingsMinGirth"],
+            dict["RenderingsMaxGirth"],
+            dict["RenderingsMinArea"],
+            dict["RenderingsMaxArea"])
+        if contourRect == None:
+            # print u'没有找到轮廓区域'
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:没有找到轮廓区域')
+            return False,None
+        #endif
+
+        # 计算文本位置;
+        realTextBox = self.findCurrentFocusTextBox(contourRect, dict["RenderingsArea"], dict["RenderingsTextArea"][0:4])
+        if realTextBox is None:
+            return False,contourRect
+        # # 获取文本内容;
+        ocrImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"OCR_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        image_util.saveCropPic(dict['TVShotPath'], ocrImgPath, realTextBox)
+        result, realTextBox = self.OCR.findPicStr(dict['TargetText'], ocrImgPath, dict['RenderingsTextArea'][4],dict['RenderingsTextArea'][5],dict['RenderingsTextArea'][6],dict['RenderingsTextArea'][7])
+        return result, contourRect
+
+
+    def findTargetFocusFrameInType8(self,dict):
+        # 1、查找当前焦点框;
+        dict["TVShotPath"] = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        self.vp.takePicture(dict["TVShotPath"])
+        ocr_result, curFocusArea = self.findCurrentFocusFrameInType8(dict)
+        if curFocusArea is None:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), u'SDK-Type8:当前焦点框空')
+            return False
+
+        result = False
+        # 2、当前焦点是否目标焦点;
+        if ocr_result == True:
+            # curText, curTextArea, curFocusArea
+            return True
+        else:
+            # 3、计算出几行几列;
+            listRect = []
+            for focus in dict["FocusList"]:
+                listRect.append(focus["focus_area"])
+            
+            listGrid = self.generateGrid(listRect)
+
+            # 4、当前焦点在哪个位置;
+            curRow,curCol,curBoundary = self.getGridCoordinate(curFocusArea,listGrid)
+
+            # 5、分正序查找和逆序查找两次查找。
+            keyDirection = True # True表示向右发送遥控,False表示向左发送遥控;
+            # 将多维数组转一维;
+            listRect = list(chain(*listGrid))
+            # 行数,列数;
+            rows,cols = len(listGrid),len(listGrid[0])
+            # 剩余遥控次数;
+            ctrl_times = len(listRect) - (curRow+1)*cols + curCol
+            # 5.1、顺序查找;
+            result, curFocusArea = self.sequentialSearch(curRow, curBoundary, ctrl_times, listRect, listGrid, dict, self.findCurrentFocusFrameInType8)
+            # 5.2、逆序查找;
+            if result == False:
+                curRow,curCol,curBoundary = self.getGridCoordinate(curFocusArea,listGrid)
+                ctrl_times = curRow*cols + curCol
+                result, curFocusArea = self.reverseSearch(curRow, curBoundary, ctrl_times, listRect, listGrid, dict, self.findCurrentFocusFrameInType8)
+
+            return result
+    #end
+
+    '''
+    # 描述:计算出两矩形的正包含的相交百分比。(以相交面积/最小矩形面积)
+    # 
+    # 返回值:正包含的相交百分比
+    # '''
+    # 两矩形是否相交,返回相交百分比;
+    def bbOverlap(self, box1, box2):
+        # 外正四方;
+        if box1[0] > box2[2]:
+            return 0.0
+        if box1[1] > box2[3]:
+            return 0.0
+        if box1[2] < box2[0]:
+            return 0.0
+        if box1[3] < box2[1]:
+            return 0.0
+       
+        # 计算相交长宽;
+        colInt = abs(min(box1[2], box2[2]) - max(box1[0], box2[0]))
+        rowInt = abs(min(box1[3], box2[3]) - max(box1[1], box2[1]))
+        # 计算相交面积
+        overlapArea = colInt * rowInt
+        # 各自面积
+        area1 = (box1[2]-box1[0])*(box1[3]-box1[1])
+        area2 = (box2[2]-box2[0])*(box2[3]-box2[1])
+        # 是否全包含;
+        # if overlapArea == area1 or overlapArea == area2:
+            # return 1.0
+        # 返回相交比;
+        # return float(overlapArea) / (area1 + area2 - overlapArea)
+        return float(overlapArea) / min(area1, area2)#以最小面积的占用比,作为相交比,可计算出全包含。
+
+    '''
+        # 描述:返回目标焦点在当前选中焦点的哪个方向。
+        # 参数:
+        # focusType:查找当前焦点的方式。
+        # focusDict:查找当前焦点的参数。
+        # matchDicts:查找目标焦点的模板匹配参数。
+        # 返回值:
+        # 
+        # '''
+    def findTargetFocusDirection(self, focusType, focusDict, matchDict):
+        # 查找当前焦点;
+        curFocusText, curFocusTextArea, curFocusArea = None,None,None
+        if focusType == 0:
+            curFocusText, curFocusTextArea, curFocusArea = self.getBorderRectangleFocusArea(focusDict["RenderingsPath"],focusDict["TVShotPath"],focusDict["FocusList"],focusDict["FocusDirection"],focusDict["RenderingsBorderWide"],)
+        elif focusType == 1:
+            curFocusText, curFocusTextArea, curFocusArea = self.getColoringleFocusArea(focusDict["RenderingsPath"],focusDict["TVShotPath"],focusDict["FocusList"],focusDict["FocusDirection"],)
+        elif focusType == 2:
+            curFocusText, curFocusTextArea, curFocusArea = self.getZoomFocusArea(focusDict["RenderingsPath"],focusDict["TVShotPath"],focusDict["FocusList"],focusDict["FocusDirection"],)
+        elif focusType == 3:
+            curFocusText, curFocusTextArea, curFocusArea = self.autoRecognitionFocusArea(focusDict,)
+        elif focusType == 4:
+            curFocusText, curFocusTextArea, curFocusArea = self.getSelectFocusArea(focusDict,)
+        elif focusType == 5:
+            if focusDict["setting"] == None:
+                curFocusArea = self.matchSingleImage(focusDict["TVShotPath"],focusDict["ZoneCoordinate"],focusDict["TemplatePath"])
+            else:
+                curFocusArea = self.matchSingleImage(focusDict["TVShotPath"],focusDict["ZoneCoordinate"],focusDict["TemplatePath"],focusDict["setting"])
+        
+        # 获取模板匹配结果,找到目标焦点区域坐标;
+        if matchDict["setting"] == None:
+            tagFocusArea = self.matchSingleImage(matchDict["TVShotPath"],matchDict["ZoneCoordinate"],matchDict["TemplatePath"])
+        else:
+            tagFocusArea = self.matchSingleImage(matchDict["TVShotPath"],matchDict["ZoneCoordinate"],matchDict["TemplatePath"],matchDict["setting"])
+        
+        # 计算两矩形是否相交,返回正包含相交比.
+        overlap = self.bbOverlap(curFocusArea, tagFocusArea["coordinate"])
+        # 返回结果;
+        return curFocusText, curFocusTextArea, curFocusArea, tagFocusArea["coordinate"], overlap
+
+    '''
+        # 描述:焦点识别类型00:选中的焦点框边框外镶了一层有色边框,通过抓取这层边框颜色来识别焦点框。
+        # 
+        # 
+        # '''
+    def findCurrentFocusBoxInType00(self, dict):
+        # 当前电视机截图;
+        tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        self.vp.takePicture(tvShotPath)
+        # 选中焦点框的效果图;
+        effectPath = dict["EffectPath"]
+        # 焦点所在区域;
+        zoneBox = dict["ZoneCoordinate"]
+        # 效果图外边框厚度;
+        borderWidth = dict["BorderWidth"]
+
+        # 获取选中焦点框效果图的外边框的luv平均值.
+        effectImg = cv.imread(effectPath)
+        effectLuv = cv.cvtColor(effectImg, cv.COLOR_BGR2Luv)
+        
+        # 将外边框拆分成4个矩形。
+        height,width = effectLuv.shape[0:2]
+        leftBox = [0, 0, borderWidth, height]
+        rightBox = [width - borderWidth, 0, width, height]
+        topBox = [borderWidth, 0, width - borderWidth, borderWidth]
+        bottomBox = [borderWidth, height - borderWidth, width - borderWidth, height]
+        # luv平均值;
+        avgluv = self.CIELuv.getMAverageLUV(effectLuv, [leftBox, rightBox, topBox, bottomBox])
+
+    '''
+        # 描述:焦点识别类型03:选中的焦点框背景色变成其他颜色,通过计算背景色获取上下两边直线来确定识别焦点框。
+        # 
+        # 
+        # '''
+    def findCurrentFocusBoxInType03(self, zoneBox, effectBox, effectColorBox, effectPath, matchVal):
+        # 当前电视机截图;
+        tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        self.vp.takePicture(tvShotPath)
+        # 判断文件是否存在;
+        if os.path.exists(tvShotPath) == False:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片路径不存在'+tvShotPath)
+            return None
+        
+        if os.path.exists(effectPath) == False:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片路径不存在'+effectPath)
+            return None
+        
+        # 判断区域是否在有效范围内;
+        # 略过……
+
+        # 打开图片;
+        img_tvShot = cv.imread(tvShotPath)
+        img_effect = cv.imread(effectPath)
+        if img_tvShot is None or img_effect is None:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片对象空')
+            return None
+
+        # effectColorBox中心点,用于取色;
+        cx,cy = effectColorBox[0] + int(effectColorBox[2] - effectColorBox[0])/2, effectColorBox[1] + int(effectColorBox[3] - effectColorBox[1])/2
+        focusBGColor = img_effect[cx,cy]
+        # 效果图宽、高
+        effectWidth, effectHeight = effectBox[2]-effectBox[0],effectBox[3]-effectBox[1]
+
+        # 水平、垂直
+        hLine1, hLine2 = [],[]
+        # 是否找到
+        bhLine1, bhLine2 = False,False
+        # 查找第一行与effectColor相近的颜色线条;
+        for y in range(zoneBox[1], zoneBox[3]):
+            for x in range(zoneBox[0], zoneBox[2]):
+                if bhLine1 == False:
+                    if self.CIELuv.isColorSimilar(img_tvShot[y][x],focusBGColor) == True:
+                        hLine1.append([x,y])
+                #end if
+            #end for
+        
+            # 判断本线是否符合要求;
+            if bhLine1 == False:
+                count = len(hLine1)
+                if float(count)/effectWidth > matchVal:
+                    bhLine1 = True
+                else:
+                    hLine1 = []
+        #end for
+        if len(hLine1) == 0:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:未找到第一行,长度为0')
+            return None
+        
+        # 查找最后一行与effectColor相近的颜色线条;
+        zoneBox = [hLine1[0][0]-10, hLine1[0][1], hLine1[0][0] + effectWidth, hLine1[0][1] + effectHeight]
+        for y in range(zoneBox[3], zoneBox[1],-1):
+            for x in range(zoneBox[0], zoneBox[2]):
+                if bhLine2 == False:
+                    if self.CIELuv.isColorSimilar(img_tvShot[y][x],focusBGColor) == True:
+                        hLine2.append([x,y])
+                #end if
+            #end for 
+
+            # 判断本线是否符合要求;
+            if bhLine2 == False:
+                count = len(hLine2)
+                if float(count)/effectWidth > matchVal:
+                    bhLine2 = True
+                else:
+                    hLine2 = []
+        #end for
+        if len(hLine2) == 0:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:未找到最后一行,长度为0')
+            return None
+        
+        # 焦点区域;
+        focusBox = [hLine1[0][0], hLine1[0][1], hLine2[-1][0], hLine2[-1][1]]
+        # 返回结果;
+        # return True,focusBox
+        return focusBox
+
+    '''
+        # 描述:焦点识别类型04:选中的焦点框背景色变成其他颜色,通过二值化获取轮廓面积、周长来识别焦点框。
+        # 
+        # 
+        # '''
+    def findCurrentFocusBoxInType04(self, zoneBox, threshold, maxThreshold, bInside, minPeri, maxPeri, minArea, maxArea, find_1st = True):
+        contourRect = [0,0,0,0]#结果:x1,y1,x2,y2;
+        tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        self.vp.takePicture(tvShotPath)
+        # 获取轮廓;
+        contourRect = self.getGrayBinaryContour(tvShotPath, zoneBox, threshold, maxThreshold, bInside, minPeri, maxPeri, minArea, maxArea, find_1st)
+        # 返回结果;
+        if find_1st == True:
+            return contourRect,tvShotPath
+        else:
+            return contourRect,tvShotPath
+    
+    '''
+        # 描述:焦点识别类型05:选中的焦点框明显与未选中时有差别,通过【多组图像的模板匹配】方式来识别焦点框。
+        # 
+        # 
+        # '''
+    def findCurrentFocusBoxInType05(self, zoneBox, focusDir):
+        tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        self.vp.takePicture(tvShotPath)
+        val = self.matchImage(tvShotPath, zoneBox, focusDir)
+        if val is None:
+            return None
+        
+        return val["coordinate"]#,val["galleryFile"]
+
+
+    '''
+        # 描述:焦点识别类型06:选中的焦点框前景高亮着色,但背景是可变的,通过二值化获取大概轮廓,再膨胀腐蚀操作后得到最大轮廓来识别焦点框。
+        # 
+        # 
+        # '''
+    def findCurrentFocusBoxInType06(self, zoneBox, threshold, maxThreshold, minPeri = 10, maxPeri = 1000000, minArea = 100, maxArea = 1000000, setting = {"first":["erode",2,2,0,1], "second":["dilate",30,9,0,1],"third":["dilate",24,10,0,2]}):
+        tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        self.vp.takePicture(tvShotPath)
+        
+        # 按区域截图,在区域中查找轮廓。
+        img_tvShot = cv.imread(tvShotPath)
+        if img_tvShot is None:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片路径可能无效'+tvShotPath)
+            return None
+
+        img_zone = image_util.cutImage(img_tvShot, zoneBox)
+        if img_zone is None:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:裁剪图片失败')
+            return None
+        
+        # 将截图转成灰阶;
+        imgGray = cv.cvtColor(img_zone, cv.COLOR_BGR2GRAY)
+        # 高斯模糊;
+        imgGray = cv.GaussianBlur(imgGray, (3, 3), 0)
+        # 将灰阶转成二值化图像;
+        thresh = cv.threshold(imgGray, threshold, maxThreshold, cv.THRESH_BINARY)[1]
+        tmpdir = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"thresh_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        cv.imwrite(tmpdir, thresh)
+
+        # 图像形态学处理;
+        dilation = self.preProcess(thresh,setting)
+        # 查找和筛选文字区域
+        text_area = self.findTextRegion(dilation,minPeri,maxPeri,minArea,maxArea)
+        if text_area is None:
+            return None
+        
+        return text_area
+
+    '''
+        # 描述:焦点识别类型07:选中的焦点框前景色变成其他颜色,但是背景可变,通过计颜色阀值来计算出轮廓,再通过周长面积过滤后来调识别焦点框。
+        # 
+        # 
+        # '''
+    def findCurrentFocusBoxInType07(self, zoneBox, threshold, maxThreshold, bInside, minPeri, maxPeri, minArea, maxArea):
+        tvShotPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"TVShot_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        self.vp.takePicture(tvShotPath)
+        return self.getHSVBinaryContour(tvShotPath,zoneBox,threshold,maxThreshold,bInside,minPeri,maxPeri,minArea,maxArea)
+
+    # 在多区域中找当前焦点;
+    def findCurrentFocusBox(self, listdict):
+        if len(listdict) == 0:
+            return None,None
+        
+        # 遍历区域,查找当前焦点所在区域;
+        zType = -1
+        zData = []
+        zName = ''
+        bInside = False
+        # 当前焦点框坐标;
+        focus_box = None
+        for zone in listdict:
+            zName = zone["zoneName"]
+            zType = zone["zoneType"]
+            zData = zone["zoneData"]
+
+            # print '打印->'.encode('gbk'),zName.encode('gbk'),zType
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前遍历Zone:'+zName+',类型:'+str(zType))
+            
+            try:
+                bInside = zData["InsideContours"]# 是否使用内轮廓;
+            except:
+                bInside = False
+            
+            if zType == 3:
+                focus_box = self.findCurrentFocusBoxInType03(zData["ZoneCoordinate"],zData["EffectArea"],zData["EffectColorBox"],zData["EffectPath"],zData["MatchVal"])
+            elif zType == 4:
+                focus_box = self.findCurrentFocusBoxInType04(
+                    zData["ZoneCoordinate"],
+                    zData["ZoneThreshold"],
+                    zData["ZoneMaxThreshold"],
+                    bInside,
+                    zData["RenderingsMinGirth"],
+                    zData["RenderingsMaxGirth"],
+                    zData["RenderingsMinArea"],
+                    zData["RenderingsMaxArea"])[0]
+            elif zType == 5:
+                focus_box = self.findCurrentFocusBoxInType05(
+                    zData["ZoneCoordinate"],
+                    zData["focusPicDir"])
+            elif zType == 6:
+                focus_box = self.findCurrentFocusBoxInType04(
+                    zData["ZoneCoordinate"],
+                    zData["ZoneThreshold"],
+                    zData["ZoneMaxThreshold"],
+                    bInside,
+                    zData["RenderingsMinGirth"],
+                    zData["RenderingsMaxGirth"],
+                    zData["RenderingsMinArea"],
+                    zData["RenderingsMaxArea"])[0]
+            elif zType == 7:
+                setting = None
+                if 'setting' not in zone:
+                    setting = {"first":["erode",2,2,0,1], "second":["dilate",30,9,0,1],"third":["dilate",24,10,0,2]}
+                else:
+                    setting = zone["setting"]
+                focus_box = self.findCurrentFocusBoxInType06( 
+                    zData["ZoneCoordinate"],
+                    zData["ZoneThreshold"],
+                    zData["ZoneMaxThreshold"],
+                    zData["RenderingsMinGirth"],
+                    zData["RenderingsMaxGirth"],
+                    zData["RenderingsMinArea"],
+                    zData["RenderingsMaxArea"],
+                    setting)
+            elif zType == 8:
+                focus_box = self.findCurrentFocusBoxInType07(
+                    zData["ZoneCoordinate"],
+                    zData["ZoneThreshold"],
+                    zData["ZoneMaxThreshold"],
+                    bInside,
+                    zData["RenderingsMinGirth"],
+                    zData["RenderingsMaxGirth"],
+                    zData["RenderingsMinArea"],
+                    zData["RenderingsMaxArea"])
+            else:
+                # print u'未使用的焦点类型'
+                LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:未使用的焦点类型:'+str(zType))
+
+            # 找到当前焦点框;
+            if focus_box is not None:
+                break
+        # end-for
+
+        if focus_box is None:
+            return None,None
+
+        return focus_box,zName
+
+    '''
+        # 描述:查找当前焦点文本框;
+        # 参数:
+        #     'realFocusBox':[x1,y1,x2,y2],   #实际焦点框,坐标系必须是原图左上角;
+        #     'refTextBox':[x1,y1,x2,y2],     #参考文本框,坐标系必须是原图左上角;
+        #     'refFocusBox':[x1,y1,x2,y2]     #参考焦点框,坐标系必须是原图左上角;
+        #     注意:x1,y1,x2,y2分别为矩形左上角(x1,y1)和右下角(x2,y2)
+        #
+        # 返回:当前焦点文本框坐标
+        # 
+        # '''
+    def findCurrentFocusTextBox(self, realFocusBox, refFocusBox, refTextBox):
+        return self.getObjAbsoluteBox(realFocusBox, refFocusBox, refTextBox)
+
+    '''
+        # 描述:根据参照物的相对坐标,来获取对象的绝对坐标值;
+        # 参数:
+        #   参照对象实际坐标
+        #   参照对象参照坐标
+        #   目标对象参照坐标
+        # 
+        #  '''
+    def getObjAbsoluteBox(self, refObjRealBox, refObjRefBox, tagObjRefBox):
+        if refObjRealBox is None:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:焦点框None,无法计算文本框坐标')
+            return None
+        # 目标区域的宽、高;
+        txWidth, txHeight = tagObjRefBox[2]-tagObjRefBox[0], tagObjRefBox[3]-tagObjRefBox[1]
+        # 以参照物作为新的坐标系,计算目标区域的相对原点(left,top)
+        left,top = tagObjRefBox[0]-refObjRefBox[0], tagObjRefBox[1]-refObjRefBox[1]
+
+        # 目标对象的绝对原点坐标;
+        left += refObjRealBox[0]
+        top += refObjRealBox[1]
+        
+        # 返回目标对象绝对坐标;
+        return [left, top, left+txWidth, top+txHeight]
+
+    '''
+        # 描述:获取焦点文本内容;
+        # 参数:
+        #   img_or_path:可以是cv2.imread()对象,也可以是图片路径;
+        #   textBox:[x1,y1,x2,y2]
+        #   orcInfo: ['language',10001,{}]    # language, ocrType, imgProcParams
+        # '''
+    def getCurrentFocusText(self, img_or_path, textBox, orcInfo):
+        if textBox is None or list(textBox) == list([-1,-1,-1,-1]):
+            return None
+        tx_img = None
+        # 保存截图;
+        if type(img_or_path) == type(list()):#如果传递的对象;
+            tx_img = image_util.cutImage(img_or_path, textBox)
+        elif type(img_or_path) == type(str()):#如果传递的是路径;
+            imgObj = cv.imread(img_or_path)
+            tx_img = image_util.cutMat(imgObj, textBox)
+        
+        if tx_img is None:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:裁剪图片失败')
+            return ''
+        tx_img_path = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"fotx_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+        cv.imwrite(tx_img_path, tx_img)
+        # orc识别;
+        text = ''
+        if len(orcInfo) >= 3 and orcInfo[2] <> {}:
+            text = self.OCR.getStrWithImgProcess(tx_img_path, orcInfo[2], orcInfo[0], orcInfo[1]).replace(' ','')
+        else:
+            text = self.OCR.getStr(tx_img_path, orcInfo[0], orcInfo[1]).replace(' ','')
+        
+        # 返回文本;
+        LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:文字识别结果:'+str(text))
+        return text
+
+
+    '''
+        # 描述:到达目标区域,成功到达返回True,否则False。
+        # 
+        # 
+        # '''
+    def reachTargetZone(self, dictZoneInfo):
+        if len(dictZoneInfo) == 0:
+            return None
+        
+        # 目标区域名称;
+        destZoneName = dictZoneInfo["destZoneName"]
+        # 排序dict;
+        listdict = dictZoneInfo["zoneList"]
+        for i in range(len(listdict)-1):
+            for j in range(0, len(listdict)-i-1):
+                if listdict[j]["zone_priority"] > listdict[j+1]["zone_priority"]:
+                    listdict[j], listdict[j+1] = listdict[j+1], listdict[j]
+    
+        # 遍历区域,查找当前焦点所在区域;
+        focus_box,zName = self.findCurrentFocusBox(listdict)
+
+        # 没有找到当前焦点框;
+        if focus_box is None:
+            return False
+
+        # 当前焦点在目标区域;
+        if str(zName) == destZoneName:
+            return True
+        
+        # 找到目标区域坐标;
+        zoneBox = []
+        for zone in dictZoneInfo["zoneList"]:
+            if destZoneName == str(zone["zoneName"]):
+                if zone["zoneType"] == 3:
+                    zoneBox = zone["zoneData"]["zone_area"]
+                else:
+                    zoneBox = zone["zoneData"]["ZoneCoordinate"]
+                break
+
+        times = 100
+        while str(zName) != str(destZoneName):
+            # 查找目标区域;
+            retVal = self.getOrientationEx(focus_box, zoneBox)
+            if retVal == 0: # 左上方;
+                self.redRat3.sendKey('down')
+                self.redRat3.sendKey('right')
+            elif retVal == 1: #正上方;
+                self.redRat3.sendKey('down')
+            elif retVal == 2: #右上方;
+                self.redRat3.sendKey('down')
+                self.redRat3.sendKey('left')
+            elif retVal == 3: #正左方;
+                self.redRat3.sendKey('right')
+            elif retVal == 4: #正右方;
+                self.redRat3.sendKey('left')
+            elif retVal == 5: #左下方;
+                self.redRat3.sendKey('up')
+                self.redRat3.sendKey('right')
+            elif retVal == 6: #正下方;
+                self.redRat3.sendKey('up')
+            elif retVal == 7: #右下方
+                self.redRat3.sendKey('up')
+                self.redRat3.sendKey('left')
+
+            times -= 1
+            if times <= 0:
+                break
+            
+            focus_box, zName = self.findCurrentFocusBox(listdict)
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前焦点区域:'+str(zName)+',目标Zone:'+str(destZoneName))
+            if focus_box is None:
+                LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:failed=>当前焦点区域空')
+                break
+            # print '结果:'.encode('gbk'),zName.encode('gbk'),destZoneName.encode('gbk')
+        
+        return True if zName == destZoneName else False
+        
+    '''
+        # 描述:获取灰阶二值化图像对象;
+        # 参数:
+        #   tvShotPath 图片路径;
+        #   cropArea 裁剪区域
+        #   grayVal、maxGrayVal 灰度值和最大灰度值
+        #   thresholdType 阀值类型,0=全局阀值,1=otsu阀值,2=自适应阀值;
+        # 
+        # '''
+    def getBinaryImage(self, tvShotPath, cropArea, grayVal, maxGrayVal, thresholdType = 0):
+        # 判断文件是否存在;
+        if os.path.exists(tvShotPath) == False:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片路径不存在'+tvShotPath)
+            return None
+        
+        # 打开图片;
+        img_tvShot = cv.imread(tvShotPath)
+        if img_tvShot is None:
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:图片对象空')
+            return None
+        
+        # 只保留区域部分;
+        try:
+            img_crop = np.array(img_tvShot[cropArea[1]:cropArea[3], cropArea[0]:cropArea[2]])
+        except:
+            img_crop = img_tvShot
+        cv.imwrite(LoggingUtil.getCaseRunLogDirPath() + "crop_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png", img_crop)
+
+        # 判断对象有效性;
+        if img_crop is None:
+            # print u'效果图对象空,可能文件路径无效'
+            LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:区域截图对象空,可能文件路径无效')
+            return None
+                
+        # 将截图转成灰阶;
+        img_gray = cv.cvtColor(img_crop, cv.COLOR_BGR2GRAY)
+        # 高斯模糊;
+        img_gray = cv.GaussianBlur(img_gray, (3, 3), 0)
+        # 将灰阶转成二值化图像;
+        img_binary = None
+        if thresholdType == 0:
+            img_binary = cv.threshold(img_gray, grayVal, maxGrayVal, cv.THRESH_BINARY)[1]
+        elif thresholdType == 1:
+            img_binary = cv.threshold(img_gray, 0, maxGrayVal, cv.THRESH_BINARY+cv.THRESH_OTSU)[1]
+        elif thresholdType == 2:
+            img_binary = cv.medianBlur(img_gray, (3,3))
+            # bloksize、C两参数,暂时固定为3、4
+            img_binary = cv.adaptiveThreshold(img_binary, maxGrayVal, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 3, 4.5)
+        cv.imwrite(LoggingUtil.getCaseRunLogDirPath() + "binary_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png", img_binary)
+
+        return img_binary
+
+    ''' 
+        # 描述:获取滑块轮廓;
+        # 
+        # 
+        # 
+        # 
+        # '''
+    def getSilderCountor(self, tvShotPath, cropArea, grayVal, maxGrayVal, bInside, minPeri, maxPeri, minArea, maxArea, morphology, find_1st = True):
+        # 获取灰阶二值化图片对象;
+        img_binary = self.getBinaryImage(tvShotPath, cropArea, grayVal, maxGrayVal)
+        if img_binary is None:
+            print u'binary is none'
+            return None
+        
+        # 对二值化图像进行腐蚀或膨胀操作--形态学;
+        for val in morphology:
+            ksize = tuple(val[1:3])
+            kernel = cv.getStructuringElement(val[3], ksize)
+            # if val[0] == 0: # 腐蚀;
+            if val[0] == "erode":
+                img_binary = cv.erode(img_binary, kernel, val[4])
+            else:   # 膨胀;
+                img_binary = cv.dilate(img_binary, kernel, val[4])
+        
+        morphology_path = LoggingUtil.getCaseRunLogDirPath() + "\\morphology_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png"
+        cv.imwrite(morphology_path, img_binary)
+
+        # 对形态学结果进行轮廓获取;
+        # 获取二值化图像的轮廓;
+        area, perimeter = 0.0, 0.0
+        if bInside == False:#外轮廓
+            contours = cv.findContours(img_binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[1]
+        else:#内轮廓
+            contours = cv.findContours(img_binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)[1]
+        
+        listBox = []
+        # 过滤掉不符合要求的轮廓;
+        x, y, w, h,result = 0,0,0,0,False
+        for cnt in contours:
+            # 面积;
+            area = cv.contourArea(cnt)
+            # 周长;
+            perimeter = cv.arcLength(cnt, True)
+            # 获取满足条件的值;
+            if area >= minArea and area <= maxArea and perimeter >= minPeri and perimeter <= maxPeri:
+                # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高;
+                x, y, w, h = cv.boundingRect(cnt)
+                # print u'boundingRect=',x,y,w,h
+                LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:当前找到的轮廓='+str((x,y,w,h)))
+                # 在区域内截图判断;
+                x = x + cropArea[0]
+                y = y + cropArea[1]
+                contourRect = [x,y,x+w,y+h]
+                # 如果只找第一个;
+                if find_1st == True:
+                    result = True
+                    break
+                # 多个;
+                listBox.append(contourRect)
+            #endif
+        #endfor
+        
+        # 只返回满足条件的第一个值;
+        if find_1st == True:
+            return contourRect if result == True else None
+        else:
+            return listBox
+    #end
+
+    '''
+        # 描述:用Type9到达指定的焦点框
+        # 参数:
+        # targetText: 目标焦点框文本
+        # focusArg: Type9焦点识别参数
+        # 返回:成功到达焦点框返回True,失败返回False
+        # 
+        # '''
+    def reachTargetFocusBoxInType9(self, targetText, focusArg):
+        # 参数;
+        loop = focusArg['loop']       
+        cropArea = focusArg['ZoneCoordinate']
+        grayVal = focusArg['ZoneThreshold']
+        maxGrayVal = focusArg['ZoneMaxThreshold']
+        bInside = focusArg['InsideContours']
+        minPeri = focusArg['focusBoxMinPeri']
+        maxPeri = focusArg['focusBoxMaxPeri']
+        minArea = focusArg['focusBoxMinArea']
+        maxArea = focusArg['focusBoxMaxArea']
+        refFocusBox = focusArg['RenderingsArea']
+        refTextBox = focusArg['RenderingsTextArea']
+        ctrldir = focusArg['FocusDirection']#只有左右=0, 上下=1,混合=2。
+        # 兼容focus_direction;
+        if ctrldir == 1 or ctrldir == 2:
+            ctrldir = 1
+        elif ctrldir == 3 or ctrldir == 4:
+            ctrldir = 0
+        keys = [['left','right'],['up','down']]
+
+        def reachType9TargetFocusBox(ctrlkey):
+            keyCount = 0
+            ret = False
+            lastText = ''
+            firstText = None
+            sametimes = 0
+            while True:
+                # 获取焦点框;
+                curFocusBox,tvShotPath = self.findCurrentFocusBoxInType04(cropArea,grayVal,maxGrayVal,bInside,minPeri,maxPeri,minArea,maxArea)
+                if curFocusBox is None:
+                    LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), 'SDK:找不到当前焦点')
+                    break
+                # 获取文本框;
+                textBox = self.getObjAbsoluteBox(curFocusBox, refFocusBox, refTextBox)
+                ocrImgPath = os.path.join(LoggingUtil.getCaseRunLogDirPath(),"OCR_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png")
+                image_util.saveCropPic(tvShotPath, ocrImgPath, textBox)
+                ocr_result, text = self.OCR.findPicStr(targetText, ocrImgPath, refTextBox[4], refTextBox[5], refTextBox[6],refTextBox[7])
+        
+                if ocr_result == True:
+                    ret = True
+                    break
+
+                # 记录第一次文本;
+                if firstText is None:
+                    firstText = text
+                else:
+                    # 循环列表,同时回到起始位置;
+                    if loop == True and firstText == text:
+                        break
+
+                # 记录最后次文本;
+                if lastText != text:
+                    lastText = text
+                    if loop == False:
+                        keyCount += 1
+                else:
+                    if loop == False:
+                        sametimes += 1
+            
+                # 如果相同次数超过n次,认为到达底部;
+                if loop == False and sametimes >= 2:
+                    break
+
+                # 遥控;
+                self.redRat3.sendKey(ctrlkey)
+            
+            # 返回结果;
+            return ret,keyCount
+
+        # 正序查找;
+        result,keyCount = reachType9TargetFocusBox(keys[ctrldir][1])
+        if result == False:
+            if loop == True:
+                return False
+            
+            # 到达起点,再反向开始查找;
+            self.redRat3.sendKey(keys[ctrldir][0], keyCount)
+            result = reachType9TargetFocusBox(keys[ctrldir][0])[0]
+
+        # 返回文本数组;
+        return result
+
+    def getRGBBinaryImage(self, src_pic, cropArea, bgr, offset=20):
+        # 读取图片;
+        img = cv.imread(src_pic)
+        if img is None:
+            return None,None
+        # 如果截图区域空,使用原图大小;
+        if cropArea is not None or cropArea.__len__() == 0:
+            cropArea = [0, 0, img.shape[1], img.shape[0]]
+
+        img_crop = None
+        # 只保留区域部分;
+        try:
+            img_crop = np.array(img[cropArea[1]:cropArea[3], cropArea[0]:cropArea[2]])
+        except:
+            img_crop = img
+        imgArr = np.asarray(img_crop).astype(np.int16)
+        bgrArr = np.asarray(bgr).astype(np.int16)
+        # 矩阵相减;
+        subImg = imgArr - bgrArr
+        cv.imwrite(r'E:\\subImg.png', subImg)
+
+        # img_RGB = cv.cvtColor(img_crop,cv.COLOR_BGR2RGB)
+        # imgArr_RGB = np.asarray(img_RGB).astype(np.int16)
+        # rgb = (bgr[2],bgr[1], bgr[0])
+        # rgbArr = np.asarray(rgb).astype(np.int16)
+        # subImg_RGB = imgArr_RGB - rgb
+        # subImgsum = (subImg + subImg_RGB)
+        # cv.imwrite(r'E:\\subImgsum.png', subImgsum)
+        # np.where(condition,x,y)满足条件值改为x,否则为y;
+        # subImg_wh1 = np.where((subImg<-offset)|(subImg>offset), np.uint8(255), np.uint8(0))
+        subImg_wh1 = np.where((subImg > -offset) & (subImg < offset), np.uint8(0), np.uint8(255))
+        cv.imwrite(r'E:\\subImg_wh1.png', subImg_wh1)
+        # 将图处转成灰阶
+        img_gray = cv.cvtColor(subImg_wh1, cv.COLOR_BGR2GRAY)
+        # 反转灰度值,以便适合查找轮廓;
+        img_gray = np.where((img_gray < -offset) | (img_gray > offset), np.uint8(0), np.uint8(255))
+        # 高斯模糊;
+        img_gray = cv.GaussianBlur(img_gray, (3, 5), 0)
+        # 输出结果图片;
+        cv.imwrite(r'E:\\img_gray.png', img_gray)
+        # 使用灰度图求得二值化图;
+        # thresh = cv.threshold(img_gray, 1, 255, cv.THRESH_OTSU | cv.THRESH_BINARY)[1]
+        thresh = cv.adaptiveThreshold(img_gray,255,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY_INV,15,10)
+        cv.imwrite(r'E:\\img_thresh.png', thresh)
+        # 返回二值化图;
+        return thresh, cropArea
+
+    '''
+        在传入的图片上,查找指定bgr颜色框矩形区域,返回矩形区域的坐标
+        :param picPath:图片路径
+        :param bgr:颜色值
+        :return rectArea:矩形区域 和区域图片
+        '''
+    def findCurrentFoucsByColor(self, picPath, cropArea, bgr, offset=20, minPeri=0, maxPeri=0, minArea=0, maxArea=0, morphology = []):
+        # 获取rgb二值图;
+        img_binary, cropArea = self.getRGBBinaryImage(picPath, cropArea, bgr, offset)
+        if img_binary is None:
+            return False,None
+
+        # 对二值化图像进行腐蚀或膨胀操作--形态学;
+        for val in morphology:
+            ksize = tuple(val[1:3])
+            kernel = cv.getStructuringElement(val[3], ksize)
+            # 腐蚀;
+            if val[0] == "erode":
+                img_binary = cv.erode(img_binary, kernel, val[4])
+            else:   # 膨胀;
+                img_binary = cv.dilate(img_binary, kernel, val[4])
+        
+        morphology_path = LoggingUtil.getCaseRunLogDirPath() + "\\morphology_" + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png"
+        cv.imwrite(morphology_path, img_binary)
+
+        # 查找符合要求的外轮廓;
+        # opencv3开始,有3个返回值:img, countours, hierarchy
+        contours = cv.findContours(img_binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)[1]
+        # 是否查找最大轮廓;
+        bFindMaxContour = False
+        # 如果周长、面积都一样,只返回最大轮廓;
+        if minPeri == maxPeri and minArea == maxArea:
+            bFindMaxContour = True
+
+        contourRect = []
+        maxContour = None  # 最大轮廓;
+        tempArea = 0
+        # 过滤掉不符合要求的轮廓;
+        x, y, w, h, result = 0, 0, 0, 0, False
+        for cnt in contours:
+            # 面积;
+            area = cv.contourArea(cnt)
+            # 周长;
+            perimeter = cv.arcLength(cnt, True)
+            # print "area=%ld, perimeter=%ld"%(area, perimeter)
+            if bFindMaxContour == True:
+                # 获取最大轮廓;
+                if tempArea < area:
+                    tempArea = area
+                    maxContour = cnt
+            else:
+                # print area,perimeter
+                # 获取满足条件的值;
+                if area >= minArea and area <= maxArea and perimeter >= minPeri and perimeter <= maxPeri:
+                    # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高;
+                    x, y, w, h = cv.boundingRect(cnt)
+                    # 在区域内截图判断;
+                    if cropArea is not None:
+                        x = x + cropArea[0]
+                        y = y + cropArea[1]
+                    contourRect = [x, y, x + w, y + h]
+                    result = True
+                    break
+                # endif
+        # endfor
+
+        if bFindMaxContour == True and maxContour is not None:
+            result = True
+            # 最大轮廓的矩形坐标;
+            x, y, w, h = cv.boundingRect(maxContour)
+            # 在区域内截图判断;
+            if cropArea is not None:
+                x = x + cropArea[0]
+                y = y + cropArea[1]
+            contourRect = [x, y, x + w, y + h]
+
+        # 只返回结果、轮廓坐标;
+        return result, contourRect 
+
+    # 文本颜色作为焦点色:获取文本色所有轮廓,计算最左、最右、最上、最下四坐标;
+    def findFramelessFocusByColor(self, src_pic, cropArea, bgr, offset=20):
+        # 获取rgb二值图;
+        thresh, cropArea = self.getRGBBinaryImage(src_pic, cropArea, bgr, offset)
+        if thresh is None:
+            return False,[]
+        # 查找符合要求的外轮廓;
+        # opencv3开始,有3个返回值:img, countours, hierarchy
+        contours = cv.findContours(thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[1]
+
+        # contourRect = []
+        left, right, top, bottom = 100000,0,100000,0
+        # 遍历轮廓;
+        for cnt in contours:
+            # 面积;
+            # area = cv.contourArea(cnt)
+            # 周长;
+            # perimeter = cv.arcLength(cnt, True)
+            # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高;
+            x, y, w, h = cv.boundingRect(cnt)
+            # 在区域内截图判断;
+            if cropArea is not None:
+                x = x + cropArea[0]
+                y = y + cropArea[1]
+            
+            if left > x:
+                left = x
+            if top > y:
+                top = y
+            if right < x + w:
+                right = x + w
+            if bottom < y + h:
+                bottom = y + h
+        #endfor;
+
+        # 是否有效矩形;
+        if left >= right or top >= bottom:
+            return False, []
+        # 返回结果;
+        return True, [left-3, top-3, right+3, bottom+3]
+        
+def testFocusArea():
+    focusAreaDict = {u'tv': [43, 128, 380, 265], u'media': [43, 648, 380, 785], u'hdmi1': [43, 386, 380, 525], u'hdmi2': [43, 517, 380, 655], u'av': [43, 257, 380, 395]}
+    focusDict = {
+        "focus_tv": '{"name":"tv","text_area":[179,210,242,248,"english", 253],"focus_area":[43,128,380,265]}',
+        "focus_av": '{"name":"av","text_area":[180,339,242,381,"english", 253],"focus_area":[43,257,380,395]}',
+        "focus_hdmi1": '{"name":"hdmi1","text_area":[156,466,269,510,"english", 2],"focus_area":[43,386,380,525]}',
+        "focus_hdmi2": '{"name":"hdmi2","text_area":[159,599,265,641,"english", 2],"focus_area":[43,517,380,655]}',
+        "focus_usb": '{"name":"media","text_area":[160,730,260,771,"english", 2],"focus_area":[43,648,380,785]}'
+    }
+
+    icon = r"res\icon.png"
+    focus_tv = r"res\focus_tv_focus.png"
+    focus_av = r"res\focus_av_focus.png"
+    focus_hdmi1 = r"res\focus_hdmi1_focus.png"
+    focus_hdmi2 = r"res\focus_hdmi2_focus.png"
+    focus_usb = r"res\focus_usb_focus.png"
+    picture = r"res\source.jpg"
+    fDetect = FeatureDetect()
+
+    print fDetect.getFocusArea(icon, picture, focusDict, 10)
+
+def testRect():
+    fDetect = FeatureDetect()
+    # fDetect.findRectByFile(r"D:\temp-pic\parentlock\channel_lock1.png", "parentLock")
+    fDetect.findRectByFile(r"D:\temp-pic\parentlock\channel_lock2.png", "parentLock")
+    # fDetect.findRectByFile(r"D:\temp-pic\parentlock\source_lock1.png", "parentLock")
+
+if __name__ == "__main__":
+    fdetect = FeatureDetect()
+    if 0:
+        focusArg = {
+            "TVShotPath": '',
+            "ZoneCoordinate": [1204, 0, 1920, 1080],
+            "InsideContours": False,
+            "ZoneThreshold": 150,
+            "ZoneMaxThreshold": 255,
+            # 选中焦点框的周长、面积;
+            "focusBoxMinPeri": 50,
+            "focusBoxMaxPeri": 90,
+            "focusBoxMinArea": 300,
+            "focusBoxMaxArea": 500,
+            # 文本最左边;
+            "textleft":1260,
+            # 文本最右边;
+            "textright":1860,
+            # 文本在焦点滑块上方多高;
+            "aboveHeight":80,
+            "aboveMargin":30,
+            "morphology":[
+                ["erode",6,6,0,2],
+                ["dilate",6,6,0,2]
+            ]
+        }
+
+        listText = []
+        for i in range(99):
+            path = r'Z:\Picture\V27N\CusPicMode\%d.png'%i
+            img = cv.imread(path)
+            if img is not None:
+                countor = fdetect.getSilderCountor(
+                    path, 
+                    focusArg['ZoneCoordinate'],
+                    focusArg['ZoneThreshold'],
+                    focusArg['ZoneMaxThreshold'],
+                    focusArg['InsideContours'],
+                    focusArg['focusBoxMinPeri'],
+                    focusArg['focusBoxMaxPeri'],
+                    focusArg['focusBoxMinArea'],
+                    focusArg['focusBoxMaxArea'],
+                    focusArg['morphology'],
+                    )
+
+                width,height = countor[2]-countor[0],countor[3]-countor[1]
+                # 文本坐标;
+                txbox = [focusArg['textleft'], countor[1] - focusArg['aboveHeight'], focusArg['textright'], countor[1] - focusArg['aboveMargin']]
+                text = fdetect.getCurrentFocusText(path, txbox, ["chineseprc+english",10000])
+                listText.append(text)
+        
+        print listText
+
+    # findFocusTextByColor测试;
+    if 0:
+        src_pic = r'C:\Users\jianfeng1.wang\Pictures\Saved Pictures\img.png'
+        icon_pic = r'C:\Users\jianfeng1.wang\Pictures\Saved Pictures\icon.png'
+        iconImg = cv.imread(icon_pic)
+
+        # 获取小图平均bgr值;
+        rgbColor = RGBColor()
+        bgr = rgbColor.getAvgBGR(iconImg)
+
+        print fdetect.findFocusTextByColor(src_pic, [], bgr)

+ 225 - 0
ssat_sdk/picture/image_util.py

@@ -0,0 +1,225 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+import inspect
+import cv2 as cv
+import numpy as np
+from ssat_sdk.utils import LoggingUtil
+
+pyFileName = os.path.split(__file__)[-1]
+
+def get_current_function_name():
+    return inspect.stack()[1][3]
+
+'''
+按照坐标系调整区域数值
+:param areaRXDY:二维区域。坐标系:x轴:水平向右,y轴:纵向向下
+:return areaDXRY:二维区域。坐标系:x轴:纵向向下,y轴:水平向右
+'''
+def coordinateRXDY2DXRY(areaRXDY):
+    areaDXRY = []
+    areaDXRY.append(areaRXDY[1])
+    areaDXRY.append(areaRXDY[0])
+    areaDXRY.append(areaRXDY[3])
+    areaDXRY.append(areaRXDY[2])
+    return areaDXRY
+'''
+截取图片区域。
+:param area:二维区域。area按照常规x,y坐标描述。示例:[x1,y1,x2,y2]. 点(x1,y1)为矩形左上角,点(x2,y2)为矩形右下角。
+'''
+def cutMat(srcimg, area):
+    try:
+        mat_area = coordinateRXDY2DXRY(area)
+        # print "cutMat:mat_area:", mat_area
+        imgArr = np.array(srcimg)
+        return imgArr[int(mat_area[0]):int(mat_area[2]),int(mat_area[1]):int(mat_area[3])]
+    except Exception, e:
+        LoggingUtil.getDebugLogger().info(pyFileName, "image_util", get_current_function_name(), 'ERROR:截图异常:'+str(e))
+        return None
+
+'''
+    # 描述:裁剪图片,返回指定裁剪区域的图片;
+    # 参数:
+    #   srcimg:opencv.imread打开的对象。
+    #   area: 要裁剪的区域,示例:[x1,y1,x2,y2]. 点(x1,y1)为矩形左上角,点(x2,y2)为矩形右下角。
+    #
+    # 返回:成功返回裁剪区域的图片对象,否则返回None
+    #
+    # 注意:相对cutMat来说,少一步坐标转换。
+    # '''
+def cutImage(srcimg, area):
+    try:
+        return np.array(srcimg[area[1]:area[3]+1, area[0]:area[2]+1])
+    except Exception,e:
+        LoggingUtil.getDebugLogger().info(pyFileName, "image_util", get_current_function_name(), 'ERROR:裁剪图片异常:'+str(e))
+        return None
+
+'''
+    # 描述:裁剪图片,返回指定裁剪区域的图片;
+    # 参数:
+    #   imgPath:图片路径
+    #   area: 要裁剪的区域,示例:[x1,y1,x2,y2]. 点(x1,y1)为矩形左上角,点(x2,y2)为矩形右下角。
+    #
+    # 返回:成功返回裁剪区域的图片对象,否则返回None
+    #
+    # 注意:相对cutMat来说,少一步坐标转换。
+    # '''
+def cutImageByPath(imgPath, area):
+    srcimg = cv.imread(imgPath)
+    try:
+        return np.array(srcimg[area[1]:area[3], area[0]:area[2]])
+    except Exception,e:
+        LoggingUtil.getDebugLogger().info(pyFileName, "image_util", get_current_function_name(), 'ERROR:裁剪图片异常:'+str(e))
+        return None
+
+def saveCropPic(srcPic, destPic, area):
+    srcImg = cv.imread(srcPic)
+    if srcImg is None:
+        LoggingUtil.getDebugLogger().info(pyFileName, "image_util", get_current_function_name(), 'SDK:打开图像返回空对象,文件可能不存在或无权限打开')
+        return False
+    if len(area) != 4:
+        LoggingUtil.getDebugLogger().info(pyFileName, "image_util", get_current_function_name(), 'SDK:给定的区域坐标无效:'+str(area))
+        return False
+    destImg = cutMat(srcImg, area)
+    if destImg is None:
+        LoggingUtil.getDebugLogger().info(pyFileName, "image_util", get_current_function_name(), 'SDK:截图失败:')
+        return False
+    cv.imwrite(destPic,destImg)
+    return True
+
+def saveCropImg(srcImg, destPic, area):
+    destImg = cutMat(srcImg, area)
+    cv.imwrite(destPic,destImg)
+    return True
+
+def cutImgWithRate(srcImg, destPic, rateArea):
+    height, width, dim = srcImg.shape
+    print "srcImg: height, width:", height, width, rateArea
+    dest_x1 = int(width * rateArea[0])
+    dest_x2 = int(width * rateArea[2])
+    dest_y1 = int(height * rateArea[1])
+    dest_y2 = int(height * rateArea[3])
+    print "dest: dest_x1,dest_y1, dest_x2, dest_y2:", dest_x1,dest_y1, dest_x2, dest_y2
+    destImg = cutMat(srcImg, (dest_x1,dest_y1, dest_x2, dest_y2))
+    cv.imwrite(destPic, destImg)
+
+def cutPicWithRate(srcPic, destPic, rateArea):
+    srcImg = cv.imread(srcPic)
+    cutImgWithRate(srcImg, destPic, rateArea)
+
+'''
+截取选中区域以外的图片
+'''
+def saveRetCropPic(srcPic, area):
+    srcImg = cv.imread(srcPic)
+    return saveRetCropImg(srcImg, area)
+
+def saveRetCropImg(srcImg, area):
+    # print "saveRetCropImg,area:",area
+    height,width,chNum = srcImg.shape
+    area1 = [0,0,area[0], height]
+    area2 = [area[0],0,area[2], area[1]]
+    area3 = [area[0], area[3],area[2], height]
+    area4 = [area[2],0,width, height]
+
+    rectList = []
+    addRect2List(rectList, area1)
+    addRect2List(rectList, area2)
+    addRect2List(rectList, area3)
+    addRect2List(rectList, area4)
+
+    imgList = []
+    for rect in rectList:
+        img = cutMat(srcImg, rect)
+        imgList.append(img)
+
+    return imgList
+
+'''
+根据设定的灰阶阈值,保存一张新图片,并返回img对象。
+原图像是BGR模式
+'''
+def saveThresholdPicImg(srcImg,threshLow,threshTop, thresholdType):
+    # print u"转成灰阶图;"
+    img_gray = cv.cvtColor(srcImg, cv.COLOR_BGR2GRAY)
+    # print u"转成二值化;"
+    ret, img_threshold = cv.threshold(img_gray, threshLow, threshTop, thresholdType)
+    return img_threshold
+
+'''
+检测rect是否正确,正确才添加到rectList
+'''
+def addRect2List(rectList, rect):
+    # print "addRect2List,rect:",rect
+    if (rect[0] <> rect[2]) and (rect[1] <> rect[3]):
+        rectList.append(rect)
+
+def caculateLinLen(line):
+    dx = abs(line[0] - line[2])
+    dy = abs(line[1]-line[3])
+    # print "caculateLinLen:",dx,dy
+    lineLen = np.sqrt(np.square(dx) + np.square(dy))
+    return lineLen
+
+
+class ImageUtil():
+    def __init__(self):
+        pass
+
+    '''
+    根据传入的图像对象,逆时针旋转角度
+    '''
+    def rotaImg(self, srcImg, angle):
+        height, width, color = srcImg.shape
+        M = cv.getRotationMatrix2D(((height-1)/2.0,(width-1)/2.0),angle,1)
+        print M
+        dstImg = cv.warpAffine(srcImg, M, (width,height))
+        return dstImg
+
+    def rotaImg180(self, srcImg):
+        dstImg = cv.flip(srcImg,0) #根据x轴翻转
+        dstImg = cv.flip(dstImg,1) #根据y轴翻转
+        return dstImg
+
+    '''
+    根据传入的图片和高宽差参数,移动图片位置
+    '''
+    def transferImg(self, srcImg, dHeight, dWidth):
+        height,width,color = srcImg.shape
+        M = np.float32([[1,0,dWidth],[0,1,dHeight]])
+        dstImg = cv.warpAffine(srcImg,M,(width,height))
+        return dstImg
+    '''
+    根据传入的图片mat和坐标点,计算灰阶中间值
+    '''
+    def calGrayMid(self, img, area):
+        # cv.line(img,(area[0],area[1]),(area[2],area[3]),(0,255,0))
+        # cv.imshow("img",img)
+        # cv.waitKey(0)
+        # cv.destroyAllWindows()
+        dstArea = list(area)
+        if area[0] + area[1] > area[2]+area[3]:
+            dstArea[0] = area[2]
+            dstArea[1] = area[3]
+            dstArea[2] = area[0]
+            dstArea[3] = area[1]
+        dstImg = cutImage(img, dstArea)
+        # print "ImageUtil.calGrayAvg: dstImg:",dstImg
+        return (np.max(dstImg) + np.min(dstImg))/2
+
+    '''
+    根据传入的图片mat和坐标点,计算灰阶平均值
+    '''
+    def calGrayAvg(self, img, area):
+        # cv.line(img,(area[0],area[1]),(area[2],area[3]),(0,255,0))
+        # cv.imshow("img",img)
+        # cv.waitKey(0)
+        # cv.destroyAllWindows()
+        dstArea = list(area)
+        if area[0] + area[1] > area[2]+area[3]:
+            dstArea[0] = area[2]
+            dstArea[1] = area[3]
+            dstArea[2] = area[0]
+            dstArea[3] = area[1]
+        dstImg = cutImage(img, dstArea)
+        # print "ImageUtil.calGrayAvg: dstImg:",dstImg
+        return np.average(dstImg)

+ 251 - 0
ssat_sdk/picture/line_util.py

@@ -0,0 +1,251 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+import inspect
+import cv2 as cv
+import numpy as np
+from ssat_sdk.utils import LoggingUtil
+from image_util import *
+
+class LineUtil():
+
+    '''
+    BGR图片传入,获取直线
+
+    '''
+    def getLines(self, img, lineMinLen = 20, lineMaxLen = 1000, ableArea=None, threshold1=50, threshold2=100, apertureSize=5, lineThreshold = 200):
+        targetMat = np.array(img)
+        if (ableArea <> None):
+            targetMat = cutMat(img, ableArea)
+
+        grayMat = cv.cvtColor(targetMat, cv.COLOR_BGR2GRAY)
+        # cv.imshow("gray", grayMat)
+
+        lines, imageGray = self.getGrayLines(grayMat, lineMinLen,lineMaxLen,ableArea,threshold1,threshold2,apertureSize,lineThreshold)
+        return lines, imageGray
+
+    def getGrayLines(self, imgGray, lineMinLen = 20, lineMaxLen = 1000, ableArea=None, threshold1=50, threshold2=100, apertureSize=5, lineThreshold = 200):
+        edges = cv.Canny(imgGray, threshold1, threshold2, apertureSize=apertureSize)
+        # cv.imshow("edges",edges)
+        lines = cv.HoughLinesP(edges, 2, np.pi / 180, lineThreshold)
+        retLines = []
+        if lines is None:
+            return None, None
+        for line in lines:
+            x1, y1, x2, y2 = line[0]
+            # print "line:", x1, y1, x2, y2
+            # "line rgb:",PreImageJudge.getImgAverageRGB(targetMat, (x1, y1, x2+1, y2+1)), \
+            # "icon rgb:",PreImageJudge.getAverageRGB(icon)
+            lineLen = self.caculateLinLen(line[0])
+            if lineLen > lineMinLen and lineLen < lineMaxLen:
+                # print "retLine:", line[0]
+                cv.line(imgGray, (x1, y1), (x2, y2), 255, 2)
+                retLines.append(line[0])
+        # cv.imshow("getLines", imgGray)
+        # cv.waitKey(0)
+        # cv.destroyAllWindows()
+        return retLines, imgGray
+
+    '''
+    :param lines:
+
+    '''
+    def combineLineList(self, lines, maxDistanse=5):
+        retLines = []
+        for findex in range(0, lines.__len__() - 1):
+            if (lines[findex] is None):
+                continue
+            tmpLine = lines[findex]
+            for sindex in range(1, lines.__len__()):
+                if (lines[sindex] is None):
+                    continue
+                secondLine = lines[sindex]
+                isCombined,tmpLine = self.combine2Line(tmpLine,secondLine, maxDistanse)
+                if (isCombined):
+                    lines[sindex] = None
+            retLines.append(tmpLine)
+        return retLines
+    '''
+    判断两条线段是否在同一个线段上,是否可以组合成一条线段。
+    :param line1:[x1, y1, x2, y2],第一条线段,基础线段。
+    :param line2:[x1, y1, x2, y2],第二条线段,拿来对比合并的线段。
+    :return 1: bool,是否合并了两条线段。True代表已合并,False代表未合并。
+    :return 2:[x1, y1, x2, y2],最终得到的线段,True是已合并的线段,False则返回line1
+    '''
+
+    def combine2Line(self, line1, line2, maxDistance):
+        # print "combine2Line,line1,line2:",line1, line2
+        line1 = self.toOriginLine(line1)
+        line2 = self.toOriginLine(line2)
+        # 1 判断是不是同是竖线或者横线。纵向方向不同,返回False
+        direct1 = self.lineDirect(line1)
+        direct2 = self.lineDirect(line2)
+        if (direct1 <> direct2):
+            return False, line1
+
+        # 2 判断两条线段所在直线的平行距离。平行距离太远返回False
+        maxDistance = maxDistance
+        paralDistance = self.getParalDistance(line1, line2, direct1)
+        if (paralDistance > maxDistance):
+            return False, line1
+
+        # 3 判断两条线段纵向距离。在纵向上相离,相交,包含,相离太远返回False
+        maxInterval = maxDistance
+        status, interval, lineNear, lineFar = self.getLengthwaysDistance(line1, line2, direct1)
+        if ((status == 1) and (interval > maxInterval)) or (status == -1):
+            return False, line1
+
+        # 4 合并相离不远、相交和包含的线段
+        point1 = lineNear[0],lineNear[1]
+        point2 = self.getMaxPoint([lineNear[2], lineNear[3]],[lineFar[0], lineFar[1]],[lineFar[2], lineFar[3]])
+        retLine = [-1,-1,-1,-1]
+        if direct1 == 1:
+            retLine[0] = point1[0]
+            retLine[2] = point2[0]
+            retLine[1] = (point1[1] + point2[1])/2
+            retLine[3] = (point1[1] + point2[1])/2
+        else:
+            retLine[1] = point1[1]
+            retLine[3] = point2[1]
+            retLine[0] = (point1[0] + point2[0])/2
+            retLine[2] = (point1[0] + point2[0])/2
+
+        return True, retLine
+
+    '''
+    例如:[1002 ,134,1002 , 43] 调整为[1002,43,1002,134]
+    '''
+    def toOriginLine(self, line):
+        retLine = line
+        if (line[0] + line[1]) > (line[2]+line[3]):
+            retLine = [line[2] , line[3], line[0],line[1]]
+        return retLine
+
+    def getMaxPoint(self, *pointList):
+        # print "getMaxPoint:",pointList
+        maxPoint = [-1,-1]
+        for point in pointList:
+            if (maxPoint[0] + maxPoint[1]) < (point[0] + point[1]):
+                maxPoint[0] = point[0]
+                maxPoint[1] = point[1]
+        return maxPoint
+
+    def getMinPoint(self, *pointList):
+        # print "getMinPoint:",pointList
+        minPoint = [10000,10000]
+        for point in pointList:
+            if (minPoint[0] + minPoint[1]) > (point[0] + point[1]):
+                minPoint[0] = point[0]
+                minPoint[1] = point[1]
+        return minPoint
+
+    '''
+    找到所有线段中的最小坐标点和最大坐标点
+    '''
+    def getLineMMPoint(self, lineList):
+        minPoint = [10000,10000]
+        maxPoint = [-1,-1]
+        for line in lineList:
+            minPoint = self.getMinPoint(minPoint, (line[0],line[1]), (line[2],line[3]))
+            maxPoint = self.getMaxPoint(maxPoint, (line[0],line[1]), (line[2],line[3]))
+        return minPoint,maxPoint
+
+    '''
+    查看line是水平线段,还是竖直线段,或者其他斜线
+    :param line1:[x1, y1, x2, y2],线段1
+    :param line2:[x1, y1, x2, y2],线段2
+    :param direct:1 代表水平,2代表竖直
+    :return int:1 代表相离,2 代表相交,3 代表包含, -1 代表识别异常
+    :return int:相离的距离,-1或者相离实际距离
+    :return line1: 接近原点的线段
+    :return line2: 远离原点的线段
+    '''
+    def getLengthwaysDistance(self, line1, line2, direct):
+        status = -1
+        distance = -1
+        tallLine = line1
+        shortLine = line2
+        lineLen1 = self.caculateLinLen(line1)
+        lineLen2 = self.caculateLinLen(line2)
+        if (lineLen1 > lineLen2):
+            tallLine = line1
+            shortLine = line2
+        else:
+            tallLine = line2
+            shortLine = line1
+
+        if (direct == 1):
+            if (tallLine[1] < shortLine[1]) and (tallLine[3] < shortLine[1]):
+                status = 1
+                distance = shortLine[1] - tallLine[3]
+                return status, distance, tallLine, shortLine
+            if (tallLine[1] < shortLine[1]) and (tallLine[3] >= shortLine[1]) and (tallLine[3] < shortLine[3]):
+                status = 2
+                distance = -1
+                return status, distance, tallLine, shortLine
+            if (tallLine[1] <= shortLine[1]) and (tallLine[3] >= shortLine[3]):
+                status = 3
+                distance = -1
+                return status, distance, tallLine, shortLine
+            if (tallLine[1] > shortLine[1]) and (tallLine[1] <= shortLine[3]) and (tallLine[3] > shortLine[3]):
+                status = 2
+                distance = -1
+                return status, distance, shortLine, tallLine
+            if (tallLine[1] > shortLine[3]) and (tallLine[3] > shortLine[3]):
+                status = 1
+                distance = tallLine[1] - shortLine[3]
+                return status, distance, shortLine, tallLine
+
+        if (direct == 2):
+            if (tallLine[0] < shortLine[0]) and (tallLine[2] < shortLine[0]):
+                status = 1
+                distance = shortLine[0] - tallLine[2]
+                return status, distance, tallLine, shortLine
+            if (tallLine[0] < shortLine[0]) and (tallLine[2] >= shortLine[0]) and (tallLine[2] < shortLine[2]):
+                status = 2
+                distance = -1
+                return status, distance, tallLine, shortLine
+            if (tallLine[0] <= shortLine[0]) and (tallLine[2] >= shortLine[2]):
+                status = 3
+                distance = -1
+                return status, distance, tallLine, shortLine
+            if (tallLine[0] > shortLine[0]) and (tallLine[0] <= shortLine[2]) and (tallLine[2] > shortLine[2]):
+                status = 2
+                distance = -1
+                return status, distance, shortLine, tallLine
+            if (tallLine[0] > shortLine[2]) and (tallLine[2] > shortLine[2]):
+                status = 1
+                distance = tallLine[0] - shortLine[2]
+                return status, distance, shortLine, tallLine
+        return status, distance, shortLine, tallLine
+    '''
+    查看line是水平线段,还是竖直线段,或者其他斜线
+    :param line1:[x1, y1, x2, y2],线段1
+    :param line2:[x1, y1, x2, y2],线段2
+    :param direct:1 代表水平,2代表竖直
+    :return int:1 代表水平,2代表竖直,0代表其他斜线
+    '''
+    def getParalDistance(self, line1, line2, direct):
+        if direct == 1:
+            return abs(line1[1] - line2[1])
+        if direct == 2:
+            return abs(line1[0] - line2[0])
+
+    '''
+    查看line是水平线段,还是竖直线段,或者其他斜线
+    :param line:[x1, y1, x2, y2],线段
+    :return bool:1 代表水平,2代表竖直,0代表其他斜线
+    '''
+    def lineDirect(self, line):
+        if (line[0] == line[2]):
+            return 2
+        elif (line[1] == line[3]):
+            return 1
+        else:
+            return 0
+
+    def caculateLinLen(self, line):
+        dx = abs(line[0] - line[2])
+        dy = abs(line[1] - line[3])
+        # print "caculateLinLen:",dx,dy
+        lineLen = np.sqrt(np.square(dx) + np.square(dy))
+        return lineLen

+ 29 - 0
ssat_sdk/picture/object_detect.py

@@ -0,0 +1,29 @@
+# -*- coding:utf-8 -*-
+import os
+import sys
+import time
+from ssat_sdk.picture.feature_detect import FeatureDetect
+
+'''处理OpenCV图像对象'''
+
+
+class ObjectDetect():
+    def __init__(self):
+        self.featureDetect = FeatureDetect()
+
+    '''
+    在大图bigImg中,检测iconImg是否存在,如果存在返回位置。
+    :param bigPic  被检索的全图路径
+    :param iconPic  需要检索的图标路径
+    :param targetArea  全图区域
+    :return boolean,area。boolean:True代表检测到icon,False代表没有。为True时,返回icon区域坐标,为False时,返回None。
+    '''
+
+    def detectIcon(self, bigImg, iconImg, targetArea=None):
+        isFind = False
+        iconArea = None
+        ret = self.featureDetect.matchSingleImage(bigImg, targetArea, iconImg)
+        if ret is not None and ret["tmpVal"] > 0.8:
+            isFind = True
+            iconArea = ret["coordinate"]
+        return isFind, iconArea

+ 161 - 0
ssat_sdk/picture/ocr_baidu.py

@@ -0,0 +1,161 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+from aip import AipOcr
+import cchardet as chardet
+from ssat_sdk.utils import LoggingUtil
+from ssat_sdk.config.baidu_config import BaiduConfig
+
+
+class OCRBaidu():
+    '''
+    识别语言类型,默认为CHN_ENG。可选值包括:
+    - CHN_ENG#中英文混合;
+    - ENG#英文;
+    - POR#葡萄牙语;
+    - FRE#法语;
+    - GER#德语;
+    - ITA#意大利语;
+    - SPA#西班牙语;
+    - RUS#俄语;
+    - JAP#日语;
+    - KOR#韩语
+    '''
+
+    def __init__(self):
+        baiduCFG = BaiduConfig()
+        APP_ID = baiduCFG.getAppID()
+        API_KEY = baiduCFG.getAPIKey()
+        SECRET_KEY = baiduCFG.getSecretKey()
+        print "APP_ID,API_KEY,SECRET_KEY:",APP_ID,API_KEY,SECRET_KEY
+        self.client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
+
+    """ 读取图片 """
+
+    def get_file_content(self, filePath):
+        try:
+            with open(filePath, 'rb') as fp:
+                img = fp.read()
+                fp.close()
+                return img
+        except Exception,e:
+            LoggingUtil.printLog("OCR", u"百度OCR读取图片失败,Err:" + unicode(e))
+            return None
+
+    '''
+    :param language:
+        - CHN_ENG#中英文混合;
+        - ENG#英文;
+        - POR#葡萄牙语;
+        - FRE#法语;
+        - GER#德语;
+        - ITA#意大利语;
+        - SPA#西班牙语;
+        - RUS#俄语;
+        - JAP#日语;
+        - KOR#韩语
+    '''
+    def basicGeneral(self, pic_path, language):
+        """ 读取图片 """
+        image = self.get_file_content(pic_path)
+        if image is None:
+            return None
+
+        """ 如果有可选参数 """
+        options = {}
+        options["language_type"] = language
+
+        """ 带参数调用通用文字识别, 图片参数为本地图片 """
+        """
+        {u'log_id': 3857306686703806895L,
+        u'direction': 0,
+        u'words_result_num': 1,
+        u'words_result':
+            [
+                {
+                    u'words': u'\u58f0\u97f3',
+                    u'probability': {u'variance': 0.0,
+                                    u'average': 0.99964, u'min': 0.99952}
+                }
+            ],
+        u'language': -1}
+        """
+        try:
+            print "普通精度"
+            result = self.client.basicGeneral(image, options)
+        except Exception,e:
+            LoggingUtil.printLog("OCR",u"百度普通OCR连接失败,Err:"+ unicode(e))
+            return None
+
+        try:
+            str_words = self.genOCRStrList(result["words_result"])
+        except Exception,e:
+            LoggingUtil.printLog("OCR",u"百度普通OCR识别失败,Err:"+ unicode(e))
+            return []
+
+        return str_words
+
+    def basicAccurate(self, pic_path, language="CHN_ENG"):
+        baiduCFG = BaiduConfig()
+        retCount = baiduCFG.getRetCount()
+        # if retCount <= 0:
+        #     return self.basicGeneral(pic_path, language)
+
+        """ 读取图片 """
+        image = self.get_file_content(pic_path)
+        if image is None:
+            return None
+        """ 如果有可选参数 """
+        options = {}
+        # options["detect_direction"] = "true"
+        # options["probability"] = "true"
+
+        """ 带参数调用通用文字识别, 图片参数为本地图片 """
+        """
+        {u'log_id': 3857306686703806895L,
+        u'direction': 0,
+        u'words_result_num': 1,
+        u'words_result':
+            [
+                {
+                    u'words': u'\u58f0\u97f3',
+                    u'probability': {u'variance': 0.0,
+                                    u'average': 0.99964, u'min': 0.99952}
+                }
+            ],
+        u'language': -1}
+        """
+        try:
+            print "高精度"
+            result = self.client.basicAccurate(image, options)
+        except Exception,e:
+            LoggingUtil.printLog("OCR",u"百度高精度OCR连接失败,Err:" + unicode(e))
+            return None
+
+        try:
+            str_words = self.genOCRStrList(result["words_result"])
+        except Exception,e:
+            LoggingUtil.printLog("OCR",u"百度高精度OCR识别失败,Err:" + unicode(e) + unicode(result))
+            return []
+        finally:
+            baiduCFG = BaiduConfig()
+            baiduCFG.subRetCount(1)
+
+        return str_words
+
+    def genOCRStrList(self, words_result):
+        strList = []
+        for word in words_result:
+            strList.append(word["words"].encode("utf-8"))
+
+        return strList
+
+if __name__ == "__main__":
+    pic_path = r"D:\ocr_err\mi_1.png"
+    # pic_path = r"D:\ocr_err\mi_3.png"
+    # pic_path = r"D:\ocr_err\mi_5.png"
+    ocr = OCRBaidu()
+    # for word in ocr.basicGeneral(pic_path,"CHN_ENG"):
+    #     print "word:", word, chardet.detect(word)
+
+    for word in ocr.basicAccurate(pic_path):
+        print "word:", word, chardet.detect(word)

+ 43 - 0
ssat_sdk/picture/ocr_tesseract.py

@@ -0,0 +1,43 @@
+# -*- coding:utf-8 -*-
+from ssat_sdk import sat_environment
+from ssat_sdk.utils.LoggingUtil import DebugLogger
+
+import os, sys, time
+import pytesseract as tes
+import cv2 as cv
+try:
+    from PIL import Image
+except ImportError:
+    import Image
+
+Lan_Default = ""
+Tes_LanDIC = {"chineseprc+english": "chi_eng", "chinesetaiwan+english": "",
+                "spanish": "",
+                "chineseprc": "chi_sim", "chinesetaiwan": "chi_tra", "russian": "", "french": "",
+                "english": "eng", "vietnamese": "", "hebrew": "", "thai": "",
+                "arabic":""
+                }
+
+class OCRTes():
+    def __init__(self):
+        self.logger = DebugLogger()
+        self.uiStyle = sat_environment.getMenuTreeSelectedStyle()
+        self.chip = sat_environment.getMenuTreeSelectedChip()
+        self.tesData_proj = self.chip + "." + self.uiStyle
+        self.logger.info(__file__, "OCRTes", "__init__", "tesData:" + self.tesData_proj)
+        # print "getMenuTreeRootDir:",sat_environment.getMenuTreeRootDir()
+        # print "getMenuTreeSelectedProjectCfgPath:",sat_environment.getMenuTreeSelectedProjectCfgPath()
+        # print "getMenuTreeSelectedChip:",sat_environment.getMenuTreeSelectedChip()
+        # print "getMenuTreeSelectedTVUI:",sat_environment.getMenuTreeSelectedTVUI()
+        # print "getSATMenuTreeDIR:",sat_environment.getSATMenuTreeDIR()
+
+
+    def getStr(self,picPath, lan, type ):
+        tesdata = self.tesData_proj #+ "." + Lan_Default #每个项目,暂时只放一个字库,不区分语言
+        print "OCRTes.getStr,params:",picPath, tesdata, type
+        startTime = time.time()
+        strArr = tes.image_to_string(picPath, lang=tesdata)
+        print "OCRTes,getStr cost:", time.time() - startTime
+        # strArr = tes.image_to_data(img, lang=lan)
+        # print strArr
+        return strArr

+ 120 - 0
ssat_sdk/picture/pic_segment.py

@@ -0,0 +1,120 @@
+# -*- coding:utf-8 -*-
+import os,sys,time
+import cv2 as cv
+import numpy as np
+from ssat_sdk.utils import LoggingUtil
+from ssat_sdk.picture import image_util
+
+class PicSegment():
+    def __init__(self):
+        pass
+
+
+    '''
+    在传入的图片上,查找指定bgr颜色框矩形区域,返回矩形区域的坐标
+    :param picPath:图片路径
+    :param bgr:颜色值
+    :return result, contourRect:result=True/False, contourRect:矩形区域
+    '''
+    def findAreaByColor(self, srcImg, cropArea, bgr, offset=20, minPeri=0, maxPeri=0, minArea=0, maxArea=0,
+                                morphology=[]):
+        # 获取rgb二值图;
+        img_binary, cropArea = self.getBGRBinaryImage(srcImg, cropArea, bgr, offset)
+        if img_binary is None:
+            return False, None
+
+        # 查找符合要求的外轮廓;
+        # opencv3开始,有3个返回值:img, countours, hierarchy
+        contours = cv.findContours(img_binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)[1]
+        # 是否查找最大轮廓;
+        bFindMaxContour = False
+        # 如果周长、面积都一样,只返回最大轮廓;
+        if minPeri == maxPeri and minArea == maxArea:
+            bFindMaxContour = True
+
+        contourRect = []
+        maxContour = None  # 最大轮廓;
+        tempArea = 0
+        # 过滤掉不符合要求的轮廓;
+        x, y, w, h, result = 0, 0, 0, 0, False
+        for cnt in contours:
+            # 面积;
+            area = cv.contourArea(cnt)
+            # 周长;
+            perimeter = cv.arcLength(cnt, True)
+            # print "area=%ld, perimeter=%ld"%(area, perimeter)
+            if bFindMaxContour == True:
+                # 获取最大轮廓;
+                if tempArea < area:
+                    tempArea = area
+                    maxContour = cnt
+            else:
+                # print area,perimeter
+                # 获取满足条件的值;
+                if area >= minArea and area <= maxArea and perimeter >= minPeri and perimeter <= maxPeri:
+                    # 直边矩形,(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高;
+                    x, y, w, h = cv.boundingRect(cnt)
+                    # 在区域内截图判断;
+                    if cropArea is not None:
+                        x = x + cropArea[0]
+                        y = y + cropArea[1]
+                    contourRect = [x, y, x + w, y + h]
+                    result = True
+                    break
+                # endif
+        # endfor
+
+        if bFindMaxContour == True and maxContour is not None:
+            result = True
+            # 最大轮廓的矩形坐标;
+            x, y, w, h = cv.boundingRect(maxContour)
+            # 在区域内截图判断;
+            if cropArea is not None:
+                x = x + cropArea[0]
+                y = y + cropArea[1]
+            contourRect = [x, y, x + w, y + h]
+
+        # 只返回结果、轮廓坐标;
+        return result, contourRect
+
+    '''
+    不做任何图片补足处理的颜色区域处理,和二值化图片获取
+    '''
+    def getBGRBinaryImage(self, srcImg, cropArea, bgr, offset=20):
+        # 读取图片;
+        if srcImg is None:
+            return None,None
+        # 如果截图区域空,使用原图大小;
+        if cropArea is not None or cropArea.__len__() == 0:
+            cropArea = [0, 0, srcImg.shape[1], srcImg.shape[0]]
+
+        img_crop = None
+        # 只保留区域部分;
+        try:
+            img_crop = image_util.cutImage(srcImg, cropArea)
+        except:
+            img_crop = srcImg
+        subImg = self.subImgBgr(img_crop, bgr)
+        cv.imwrite(r'd:\\subImg.png', subImg)
+        subImg_wh1 = np.where((subImg > -offset) & (subImg < offset), np.uint8(0), np.uint8(255))
+        cv.imwrite(r'd:\\subImg_wh1.png', subImg_wh1)
+        # 将图处转成灰阶
+        img_gray = cv.cvtColor(subImg_wh1, cv.COLOR_BGR2GRAY)
+        # 反转灰度值,以便适合查找轮廓;
+        img_gray = np.where((img_gray < -offset) | (img_gray > offset), np.uint8(0), np.uint8(255))
+        # 输出结果图片;
+        cv.imwrite(r'd:\\img_gray.png', img_gray)
+        # 使用灰度图求得二值化图;
+        thresh = cv.threshold(img_gray, 1, 255, cv.THRESH_BINARY)[1]
+        # 返回二值化图;
+        return thresh, cropArea
+
+    '''
+    在原有图片,减去固定的BGR颜色值,用于后续的颜色筛选.返回减去bgr的图片。
+    '''
+    def subImgBgr(self, bgrImg, bgr):
+        imgArr = np.asarray(bgrImg).astype(np.int16)
+        bgrArr = np.asarray(bgr).astype(np.int16)
+        # 矩阵相减;
+        subImg = imgArr - bgrArr
+        return subImg

+ 169 - 0
ssat_sdk/picture/pq_detect.py

@@ -0,0 +1,169 @@
+# -*- coding:utf-8 -*-
+from RGB import *
+from image_util import *
+
+import os, sys, time
+import cv2 as cv
+import numpy as np
+import random
+
+class PQDetect():
+    def __init__(self):
+        self.RGBColor = RGBColor()
+
+    def isSnowPic2(self, pic_path):
+        im1 = cv.imread(pic_path)
+        h1, w1, c1 = im1.shape
+        print h1, w1, c1
+        im1_1 = im1[0:h1 / 2, 0:w1 / 2]
+        im1_2 = im1[0:h1 / 2, w1 / 2:w1]
+        im1_3 = im1[h1 / 2:h1, 0:w1 / 2]
+        im1_4 = im1[h1 / 2:h1, w1 / 2:w1]
+        hist_list = []
+        hist_list.append(cv.calcHist([im1_1], [0], None, [256], [0, 256]))
+        hist_list.append(cv.calcHist([im1_2], [0], None, [256], [0, 256]))
+        hist_list.append(cv.calcHist([im1_3], [0], None, [256], [0, 256]))
+        hist_list.append(cv.calcHist([im1_4], [0], None, [256], [0, 256]))
+        count = 0
+        totalHist = 0
+        for hist_1 in hist_list:
+            for hist_2 in hist_list:
+                diffHist = cv.compareHist(hist_1, hist_2, cv.HISTCMP_BHATTACHARYYA)
+                if diffHist <> 0:
+                    count += 1
+                    totalHist += diffHist
+        four_zone_diff = totalHist / count
+        print four_zone_diff
+
+        rhist = cv.calcHist([im1_1], [0], None, [256], [0, 256])
+        bhist = cv.calcHist([im1_2], [2], None, [256], [0, 256])
+        bgr_diff = cv.compareHist(rhist, bhist, cv.HISTCMP_BHATTACHARYYA)
+        print bgr_diff
+
+        if four_zone_diff < 0.1 and bgr_diff < 0.2:
+            return True
+        else:
+            return False
+
+    '''
+    判断传入的图片,是否为ATV无信号雪花屏。定义雪花屏:清晰度25左右.然后随机取20个区域,查看直方图趋势相似度。
+    :param pic_path:图片路径
+    :threshold: 直方图差异临界值。 范围 0 ~ 1
+    :sharpDiff: 清晰度容错范围值。
+    :return : True代表是雪花屏,False代表不是雪花屏
+    '''
+    def isSnowPic(self, pic_path, threshold=0.1, sharpDiff=10, sharpSnow = 25):
+        im1 = cv.imread(pic_path)
+        h1, w1, c1 = im1.shape
+
+        sharpP = self.detSharpLaplacian(pic_path)
+
+        hist_list = []
+        for i in range(20):
+            x1 = random.randint(0,h1/2)
+            y1 = random.randint(0,w1/2)
+            rect_h = h1/2
+            rect_w = w1/2
+            # print [x1,y1, x1+ rect_w, y1+rect_h]
+            im = cutMat(im1, [x1,y1, x1+ rect_h, y1+rect_w])
+            hist = cv.calcHist([im], [0], None, [256], [0, 256])
+            hist_list.append(hist)
+
+        count = 0
+        totalHist = 0
+        for hist_1 in hist_list:
+            for hist_2 in hist_list:
+                diffHist = cv.compareHist(hist_1, hist_2, cv.HISTCMP_BHATTACHARYYA)
+                if diffHist <> 0:
+                    count += 1
+                    totalHist += diffHist
+        four_zone_diff = totalHist / count
+
+        print "isSnowPic:", four_zone_diff,sharpP
+
+        if four_zone_diff < threshold and abs(sharpP - sharpSnow) <=  sharpDiff:
+            return True
+        else:
+            return False
+
+    '''
+    根据传入的图片路径,计算图片色温
+    '''
+    def calculateCCT(self, pic_path):
+        img = cv.imread(pic_path)
+        xyImg = cv.cvtColor(img,cv.COLOR_BGR2XYZ)
+        #色温计算
+        X = np.average(xyImg[:,:,0])
+        Y = np.average(xyImg[:,:,1])
+        Z = np.average(xyImg[:,:,2])
+        x = X/(X+Y+Z) #色谱XYZ转换成色坐标x
+        y = Y/(X+Y+Z) #色谱XYZ转换成色坐标y
+        print "calculateCCT,x,y:",x,y
+        n = (x-0.3320)/(0.1858-y)
+        print "calculateCCT,n:",n
+        CCT = 437*n**3 + 3601*n**2 + 6831*n + 5517
+        print "calculateCCT,CCT:",CCT
+        return CCT
+
+    '''
+        Tenengrad梯度方法利用Sobel算子分别计算水平和垂直方向的梯度,同一场景下梯度值越高,图像越清晰。
+        以下是具体实现,这里衡量的指标是经过Sobel算子处理后的图像的平均灰度值,值越大,代表图像越清晰。
+        :param 图片路径
+        :return float, 值越大,代表清晰度越高
+        '''
+
+    def detSharpTenengrad(self, pic_path):
+        img = cv.imread(pic_path)
+        grayImg = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
+
+        sobelImg = cv.Sobel(grayImg, cv.CV_16U, 1, 1)
+        grayV = cv.mean(sobelImg)[0]  # 图像的平均灰度
+
+        # cv.putText(img, str(grayV), (20,50), cv.FONT_HERSHEY_COMPLEX, 0.8, (255,255,0))
+        # cv.imshow("清晰度",img)
+        # cv.waitKey(0)
+        return grayV
+
+    '''
+    采用Laplacian梯度方法检测清晰度
+    :param 图片路径
+    :return float, 值越大,代表清晰度越高
+    '''
+
+    def detSharpLaplacian(self, pic_path):
+        img = cv.imread(pic_path)
+        return self.detImgSharpLaplacian(img)
+
+    def detImgSharpLaplacian(self, img):
+        grayImg = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
+
+        sobelImg = cv.Laplacian(grayImg, cv.CV_16U)[0]
+        grayV = cv.mean(sobelImg)[0]  # 图像的平均灰度
+
+        # cv.putText(img, str(grayV), (20, 50), cv.FONT_HERSHEY_COMPLEX, 0.8, (255, 255, 0))
+        # cv.imshow("清晰度", img)
+        # cv.waitKey(0)
+        return grayV
+
+    '''
+    采用方差(Variance)方法检测清晰度。
+    方差是概率论中用来考察一组离散数据和其期望(即数据的均值)之间的离散(偏离)成都的度量方法。
+    方差较大,表示这一组数据之间的偏差就较大,组内的数据有的较大,有的较小,分布不均衡;
+    方差较小,表示这一组数据之间的偏差较小,组内的数据之间分布平均,大小相近。
+    :param 图片路径
+    :return float, 值越大,代表清晰度越高
+    '''
+
+    def detSharpVariance(self, pic_path):
+        img = cv.imread(pic_path)
+        grayImg = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
+
+        # 求灰度图像的标准差
+        stdValueImg = cv.meanStdDev(grayImg)
+        # print stdValueImg
+        meanValue = stdValueImg[1][0][0]  # 求得均方差。stdValueImg:(均差array([[ 16.58646894]]), 标准差array([[ 10.1834467]]))
+        grayV = meanValue * meanValue
+        # cv.putText(img, str(grayV), (20, 50), cv.FONT_HERSHEY_COMPLEX, 0.8, (255, 255, 0))
+        # cv.imshow("清晰度", img)
+        # cv.waitKey(0)
+        return grayV

+ 18 - 0
ssat_sdk/picture/rect_util.py

@@ -0,0 +1,18 @@
+# -*- coding:utf-8 -*-
+
+class RectUtil():
+    def __init__(self):
+        pass
+    def calArea(self,area):
+        if area.__len__() < 4:
+            return -1
+        height = area[2] - area[1]
+        width = area[3] - area[0]
+        return height * width
+
+    def calArcLength(self, area):
+        if area.__len__() < 4:
+            return -1
+        height = area[2] - area[1]
+        width = area[3] - area[0]
+        return height * 2 + width*2

BIN
ssat_sdk/picture/std.jpg


BIN
ssat_sdk/picture/test.jpg


+ 1938 - 0
ssat_sdk/python_uiautomator.py

@@ -0,0 +1,1938 @@
+# -*- 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)
+            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()
+
+    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 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)
+
+    '''
+    作用:获取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='', 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 instance == -1:
+                print "没有参数带入,找不到对象"
+                return uiObject
+
+            if className != "" and resourceId != "" and text != "" and description != "" and instance != -1:
+                uiObject = self.u(className=className, resourceId=resourceId, text=text, description=description,
+                                  instance=instance)
+            # 缺少一个元素的
+            if className != "" and resourceId != "" and text != "" and description != "" and instance == -1:
+                uiObject = self.u(className=className, resourceId=resourceId, text=text, description=description)
+
+            if className != "" and resourceId != "" and text != "" and description == "" and instance != -1:
+                uiObject = self.u(className=className, resourceId=resourceId, text=text, instance=instance)
+
+            if className != "" and resourceId != "" and text == "" and description != "" and instance != -1:
+                uiObject = self.u(className=className, resourceId=resourceId, description=description,
+                                  instance=instance)
+
+            if className != "" and resourceId == "" and text != "" and description != "" and instance != -1:
+                uiObject = self.u(className=className, text=text, description=description,
+                                  instance=instance)
+
+            if className == "" and resourceId != "" and text != "" and description != "" and instance != -1:
+                uiObject = self.u(resourceId=resourceId, text=text, description=description,
+                                  instance=instance)
+
+            # 缺少两个元素的
+            if className == "" and resourceId == "" and text != "" and description != "" and instance != -1:
+                uiObject = self.u(text=text, description=description,
+                                  instance=instance)
+
+            if className == "" and resourceId != "" and text == "" and description != "" and instance != -1:
+                uiObject = self.u(resourceId=resourceId, description=description,
+                                  instance=instance)
+
+            if className == "" and resourceId != "" and text != "" and description == "" and instance != -1:
+                uiObject = self.u(resourceId=resourceId, text=text,
+                                  instance=instance)
+
+            if className == "" and resourceId != "" and text != "" and description != "" and instance == -1:
+                uiObject = self.u(resourceId=resourceId, text=text, description=description)
+
+            if className != "" and resourceId == "" and text == "" and description != "" and instance != -1:
+                uiObject = self.u(className=className, description=description,
+                                  instance=instance)
+
+            if className != "" and resourceId == "" and text != "" and description == "" and instance != -1:
+                uiObject = self.u(className=className, text=text,
+                                  instance=instance)
+
+            if className != "" and resourceId != "" and text == "" and description != "" and instance == -1:
+                uiObject = self.u(className=className, text=text, description=description)
+            if className != "" and resourceId != "" and text == "" and description == "" and instance != -1:
+                uiObject = self.u(className=className, resourceId=resourceId,
+                                  instance=instance)
+
+            if className != "" and resourceId != "" and text == "" and description == "" and instance != -1:
+                uiObject = self.u(className=className, resourceId=resourceId, description=description)
+
+            if className != "" and resourceId != "" and text != "" and description == "" and instance == -1:
+                uiObject = self.u(className=className, resourceId=resourceId, text=text)
+
+            # 缺少3个元素
+            if className == "" and resourceId == "" and text == "" and description != "" and instance != -1:
+                uiObject = self.u(description=description, instance=instance)
+
+            if className == "" and resourceId == "" and text != "" and description == "" and instance != -1:
+                uiObject = self.u(text=text, instance=instance)
+
+            if className == "" and resourceId == "" and text != "" and description != "" and instance == -1:
+                uiObject = self.u(text=text, description=description)
+
+            if className == "" and resourceId != "" and text == "" and description == "" and instance != -1:
+                uiObject = self.u(resourceId=resourceId, instance=instance)
+
+            if className == "" and resourceId != "" and text == "" and description != "" and instance == -1:
+                uiObject = self.u(resourceId=resourceId, description=description)
+
+            if className == "" and resourceId != "" and text != "" and description == "" and instance == -1:
+                uiObject = self.u(resourceId=resourceId, text=text)
+
+            if className != "" and resourceId == "" and text == "" and description == "" and instance != -1:
+                uiObject = self.u(className=className, instance=instance)
+
+            if className != "" and resourceId == "" and text == "" and description != "" and instance == -1:
+                uiObject = self.u(className=className, description=description)
+
+            if className != "" and resourceId == "" and text != "" and description == "" and instance == -1:
+                uiObject = self.u(className=className, text=text)
+
+            if className != "" and resourceId != "" and text == "" and description == "" and instance == -1:
+                uiObject = self.u(className=className, resourceId=resourceId)
+
+            # 缺少4个元素
+            if className == "" and resourceId == "" and text == "" and description == "" and instance != -1:
+                uiObject = self.u(instance=instance)
+
+            if className == "" and resourceId == "" and text == "" and description != "" and instance == -1:
+                uiObject = self.u(description=description)
+
+            if className == "" and resourceId == "" and text != "" and description == "" and instance == -1:
+                uiObject = self.u(text=text)
+
+            if className == "" and resourceId != "" and text == "" and description == "" and instance == -1:
+                uiObject = self.u(resourceId=resourceId)
+
+            if className != "" and resourceId == "" and text == "" and description == "" and instance == -1:
+                uiObject = self.u(className=className)
+            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):
+        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)))
+        # 确定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):
+            return "down-right"
+        if (dL_Ux < 0) and (dL_Uy > 0):
+            return "down-left"
+        if (dL_Ux > 0) and (dL_Uy < 0):
+            return "up-right"
+        if (dL_Ux < 0) and (dL_Uy < 0):
+            return "up-left"
+
+    '''
+    检测两个区域是否有重叠部分
+    '''
+
+    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
+
+    ''' 
+                 作用:在界面中 中 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):
+        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

BIN
ssat_sdk/resource/CIE_1976_UCS.png


+ 224 - 0
ssat_sdk/result_tool.py

@@ -0,0 +1,224 @@
+# -*- coding: utf-8 -*-
+import sys,os,time
+reload(sys)
+sys.setdefaultencoding('utf-8')
+from utils.test_task import *
+from utils.platform_Util import *
+from sat_environment import *
+from utils import *
+from core.process_sync import *
+import shutil,json
+from runner import runner_sender
+
+class ResultTool(ProcessSync):
+    Result_DIR = getResultDir()
+    Result_DETAIL = "detail"
+    Result_Total_XSL = r"config/all-result.xsl"
+    Result_Detail_XSL = r"config/case_result_detail.xsl"
+    Python_Home = sys.prefix
+
+    Action_DetailResult = "detail_result"
+    Action_CaseResult = "case_result"
+
+    Key_CaseName = "case_name"
+    Key_CaseResult = "case_result"
+    Key_ItemName = "item_name"
+    Key_ItemResult = "item_result"
+    Key_ItemScreen = "item_screen"
+    Key_ItemLog = "item_log"
+
+    def __init__(self, resultxml = "all-result.xml"):
+        print "Init ResultTool"
+        ProcessSync.__init__(self)
+        ResultTool.updateBaseDir(getResultDir())
+        self.Result_XMLFile = ResultTool.Result_DIR + "/" +resultxml
+        self.task = xml_util.parseAllResult(self.Result_XMLFile)
+        self.Case_Result = "NotExcuted"
+        self.detailItemIndex = 0
+        self.caseName = ""
+
+    def continueNextItem(self, itemName, itemDesc="None"):
+        self.detailItemIndex += 1
+        runner_sender.sendActionStart(self.tc.name, self.detailItemIndex, itemName, itemDesc)
+
+    def setNextItem(self, itemName, itemIndex, itemDesc="None"):
+        self.detailItemIndex = itemIndex
+        runner_sender.sendActionStart(self.tc.name, self.detailItemIndex, itemName, itemDesc)
+
+    @staticmethod
+    def checkResultDIR():
+        ResultTool.mkCHdir(ResultTool.Result_DIR)
+        ResultTool.mkCHdir(ResultTool.Result_DIR_DETAIL)
+        ResultTool.mkCHdir(ResultTool.Result_DIR_LOG)
+        ResultTool.mkCHdir(ResultTool.Result_DIR_SCREEN)
+        ResultTool.mkCHdir(ResultTool.Result_DIR_SCRIPTS)
+
+    @staticmethod
+    def mkCHdir(dir):
+        #print "mkCHdir,dir=", dir
+        if (os.path.exists(dir)):
+            return
+        #例如:D:/SAT/result_schedule_id_schedule_name_task_number//detail
+        #os.path.splite结果:('D:/SAT/result_schedule_id_schedule_name_task_number', 'detail')
+        #过滤//多个分割符的情况
+        preDir = os.path.split(dir)[0]
+        #print "mkdir,preDir=", preDir
+        if (not os.path.exists(preDir)):
+            ResultTool.mkCHdir(preDir)
+        #防止目录字符串最后带有/的情况,会出现两次创建同一个目录
+        #例如: D:/SAT/。会创建D:/SAT和D:/SAT/
+        if (not os.path.exists(dir)):
+            os.mkdir(dir)
+
+    @staticmethod
+    def copyInitFile():
+        Result_Total_XSL_Path = ResultTool.Python_Home + Environment.SDK_Relative_Path + ResultTool.Result_Total_XSL
+        Result_Detail_XSL_Path = ResultTool.Python_Home + Environment.SDK_Relative_Path + ResultTool.Result_Detail_XSL
+        shutil.copyfile(Result_Total_XSL_Path, ResultTool.Result_DIR + "/all-result.xsl")
+        shutil.copyfile(Result_Detail_XSL_Path, ResultTool.Result_DIR_DETAIL + "/case_result_detail.xsl")
+
+    @staticmethod
+    def updateBaseDir(dir):
+        print "updateBaseDir,dir=",dir
+        ResultTool.Result_DIR = unicode(dir)
+        ResultTool.Result_DIR_DETAIL = ResultTool.Result_DIR + "/" + ResultTool.Result_DETAIL
+        ResultTool.Result_DIR_LOG = ResultTool.Result_DIR + "/log"
+        ResultTool.Result_DIR_SCREEN = ResultTool.Result_DIR + "/screen"
+        ResultTool.Result_DIR_SCRIPTS = ResultTool.Result_DIR + "/scripts"
+        ResultTool.checkResultDIR()
+        ResultTool.copyInitFile()
+
+    @staticmethod
+    def setBaseDir(dir):
+        setResultDir(dir)
+
+    def getBaseDir(self):
+        return ResultTool.Result_DIR
+
+    def getDetailDir(self):
+        return ResultTool.Result_DIR_DETAIL
+
+    def getLogDir(self):
+        return ResultTool.Result_DIR_LOG
+
+    def getScreenDir(self):
+        return ResultTool.Result_DIR_SCREEN
+
+    def addTestCase(self, caseName):
+        tcase = self.task.getCaseByName(caseName)
+        if (tcase == None):
+            self.tc = TestCase()
+            self.tc.name = caseName
+            self.task.addTestCase(self.tc)
+        else:
+            self.tc = tcase
+        return self.tc
+
+    def getItemByName(self, itemlist, itemname):
+        for item in itemlist:
+            if (item.name == itemname):
+                return item
+        return None
+
+    def addItemPicture(self, casename, picpath):
+        # print "addItemPicture,",casename, picpath
+        destPicDir = os.path.join(self.getScreenDir(), casename)
+        self.mkCHdir(destPicDir)
+        destPicPath = os.path.join(destPicDir,os.path.basename(picpath))
+        if os.path.isdir(destPicPath):
+            print u"图片路径为空"
+            return ""
+        try:
+            shutil.copy(picpath,destPicPath)
+        except IOError, e:
+            print u"拷贝图片失败"
+            print e
+        return casename + "/" + os.path.basename(picpath), os.path.basename(picpath)
+
+    def saveDetailResult2(self, destxml, itemname, result, picNameList, logName, data="",remark=""):
+        print "saveDetailResult2,picNameList:", picNameList
+        print "saveDetailResult:",destxml
+        itemList = xml_util.parseDetailResult(destxml.decode('utf-8'))
+        item = self.getItemByName(itemList,itemname)
+        if (item == None):
+            item =  Item()
+            item.name = itemname
+            itemList.append(item)
+        item.result = result
+        del item.screenPathList[:]
+        for picName in picNameList:
+            item.screenPathList.append("../screen/" + picName)
+        if logName <> "":
+            item.logPath = "../log/" + logName
+        item.data =data
+        item.remark = remark
+        xml_util.saveDetailResult(destxml.decode('utf-8'), itemList)
+
+
+    """
+    picpathList:图片路径数组.最多存储6张图片
+    """
+    def saveDetailResult(self, xmlName, itemname, result, picpathList, logpath="", data="", remark=""):
+        xmlPath = ResultTool.Result_DIR_DETAIL + "/"+ xmlName
+        # print "saveDetailResult,xmlPath=", xmlPath
+        # print "saveDetailResult,picpathList:", picpathList
+        if (result == "Fail"):
+            self.Case_Result = result
+        #兼容之前的参数字符串定义
+        picList = []
+        if (type(picpathList) == type("") and picpathList <> "")\
+                or (type(picpathList) == type(u"") and picpathList <> u""):
+            # print u"screen字符串"
+            picList.append(picpathList)
+        else:
+            picList = picpathList
+        # print "saveDetailResult,picList:", picList
+        rePicNameList = []
+        picNameList = []
+        for picpath in picList:
+            if picpath.__len__() < 5:
+                break
+            rePicName, picName = self.addItemPicture(self.tc.name, picpath)
+            rePicNameList.append(rePicName)
+            picNameList.append(picName)
+        logname = os.path.basename(logpath)
+        self.saveDetailResult2(xmlPath, itemname, result,rePicNameList, logname, data, remark)
+        destPicDir = os.path.join(self.getScreenDir(), self.tc.name)
+        runner_sender.sendActionResult(self.tc.name, self.detailItemIndex, itemname, "", result, remark, destPicDir,"", picNameList,"",data)
+
+    def saveCaseResult2(self, result, detailFilePath):
+        self.tc.result = result
+        self.tc.rdetail = detailFilePath
+        #print "saveCaseResult2,Result_XMLFile=", self.Result_XMLFile
+        xml_util.saveAllResult(self.task, self.Result_XMLFile.decode('utf-8'))
+
+        #Packet case result
+        #兼容制定测试用例名,和不指定测试用例的情况
+
+    def saveCaseResult(self,result, detailFileName):
+        #detailFilePath = ResultTool.Result_DIR + detailFileName
+        detailFilePath = ResultTool.Result_DETAIL + "/"+detailFileName
+        self.Case_Result = result
+        self.saveCaseResult2(self.Case_Result, detailFilePath)
+        runner_sender.sendCaseResult(self.tc.name, self.detailItemIndex, result, "", "", "")
+
+if __name__ == "__main__":
+    # Result_XMLFile = "all-result.xml"
+    # detailXml = "test-result.xml"
+    # # ResultTool.setBaseDir(u"D:/SAT/中文测试/")
+    # reTool = ResultTool(Result_XMLFile)
+    # reTool.addTestCase("中文测试")
+    # reTool.saveDetailResult(detailXml, "测试", "Pass", "item2.png&item3.png", "log.txt")
+    # reTool.saveCaseResult("Fail", detailXml)
+    destxml = r"E:\SAT\results\detail\source_select_result.xml"
+    itemList = xml_util.parseDetailResult(destxml.decode('utf-8'))
+    print "itemList:",itemList
+    for item in itemList:
+        print "item.name:",item.name
+        print "item.data:",item.data
+        print "item.remark:",item.remark
+        print "item.result:",item.result,type(item.result)
+        print "item.screenPathList:",item.screenPathList
+        # itemScreenPathList = item.screenPathList
+        # imageName =
+        print "item.logPath:",item.logPath

+ 2 - 0
ssat_sdk/runner/__init__.py

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

+ 70 - 0
ssat_sdk/runner/runner_sender.py

@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+import contextlib
+import json
+import logging
+import socket
+import time
+
+import mmap
+import os, sys, time
+
+from multiprocessing import Lock
+
+from ssat_sdk import getRunnerTcpPort
+from ssat_sdk.utils import LoggingUtil
+
+# lock = Lock()
+
+_IsLoggerCreated = None
+
+
+def sendActionStart(testCaseName, stepIndex, stepName, stepDesc):
+    actionStartMessage = {'ReportType': "ActionStart",
+                          'data': {'testCaseName': testCaseName, 'stepIndex': stepIndex, 'stepName': stepName,
+                                   'stepDesc': stepDesc}}
+    actionStartMessageJson = json.dumps(actionStartMessage)
+    client(actionStartMessageJson)
+
+
+def sendActionResult(testCaseName, stepIndex, stepName, stepDesc, actionRet, failedReason, imageFileDir, videoFileDir,
+                     imageNameList, videoName, analysis):
+    actionResultMessage = {'ReportType': "ActionFinish", 'data':
+        {'testCaseName': testCaseName, 'stepIndex': stepIndex, 'stepName': stepName, 'stepDesc': stepDesc,
+         'actionRet': actionRet, 'failedReason': failedReason, 'imageFileDir': imageFileDir,
+         'videoFileDir': videoFileDir, 'imageName': imageNameList, 'videoName': videoName, 'analysis': analysis}}
+    actionResultMessageJson = json.dumps(actionResultMessage)
+    client(actionResultMessageJson)
+
+
+def sendCaseResult(testCaseName, stepIndex, caseResult, failedReason, logFileDir, logName):
+    caseResultMessage = {'ReportType': "CaseFinish",
+                         'data': {'testCaseName': testCaseName, 'stepIndex': stepIndex, 'caseResult': caseResult,
+                                  'failedReason': failedReason, 'logFileDir': logFileDir, 'logName': logName}}
+    caseResultMessageJson = json.dumps(caseResultMessage)
+    client(caseResultMessageJson)
+    time.sleep(1)  # 确保程序结束时结果能发送成功。
+
+
+def client(message):
+    # lock.acquire()
+    # tcp方式传结果给Runner端
+    LoggingUtil.caseResult(message)
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    port = getRunnerTcpPort()
+    try:
+        sock.connect(('127.0.0.1', port))
+        sock.sendall(message)
+    except Exception, e:
+        print u"无Runner接收结果。Error:", e, '   runner_sender_port:', port
+    finally:
+        sock.close()
+
+    # 内存共享方式
+    # LoggingUtil.caseResult(message)
+    # with contextlib.closing(mmap.mmap(-1, 4096, tagname='caseresult', access=mmap.ACCESS_WRITE)) as m:
+    #     # for i in range(1, 10001):
+    #     m.seek(0)
+    #     m.write(message)
+    #     m.flush()
+    # lock.release()
+    # time.sleep(1)

+ 50 - 0
ssat_sdk/sample_tool.py

@@ -0,0 +1,50 @@
+#-*- coding: utf-8 -*-
+"""
+此工具用于抓取各种声音和图片样本
+"""
+import sys,os,time
+reload(sys)
+sys.setdefaultencoding('utf-8')
+
+from TST.CaptureCard  import *
+from TST.SourceGenInput import *
+from ssat_sdk.tv_operator import *
+
+rcu = TvOperator()
+
+class SampleTool() :
+    HDMItimingNums = (65, 66, 67, 68, 69, 70, 71, 72, 73, 74)
+    patternNums = (11, 21, 22, 23, 37, 41, 44, 47, 54,
+                   56, 81, 85, 88, 134, 215, 304, 355)
+
+
+    def takeTimingPattern(self, destDir,tNums, pNums):  # 检查pattern
+        cc = CaptureCard()
+        sg = SourceGenInput()
+        result = 0
+        bol = False
+        timingIndex = 0
+        patIndex = 0
+        for tNum in tNums:
+            print "切换timing:", tNum
+            sg.setTiming(tNum)
+            for pNum in pNums:
+                print "切换pattern:",pNum
+                sg.setPattern(pNum)
+                #确保退出info信息显示
+                time.sleep(3)
+                rcu.sendKey("EXIT")
+                picPath = destDir + "/" + str(tNum) + "-" + str(pNum) +".jpg"
+                print "抓取图片:",picPath
+                cc.takePicture(picPath)
+                patIndex = patIndex + 1
+                if (patIndex == pNums.__len__()):
+                    patIndex = 0
+                    break
+            timingIndex = timingIndex + 1
+        cc.close()
+
+SP_DIR = "D:/TST/test1/standard_pattern/"
+if __name__ == "__main__":
+    samTool = SampleTool()
+    samTool.takeTimingPattern(SP_DIR, SampleTool.HDMItimingNums, SampleTool.patternNums)

+ 895 - 0
ssat_sdk/sat_environment.py

@@ -0,0 +1,895 @@
+# -*- coding:utf-8 -*-
+import sys, os, time
+
+reload(sys)
+sys.setdefaultencoding("utf-8")
+
+from utils.platform_Util import *
+from ConfigParser import *
+import json
+
+class TConfig:
+    def __init__(self, ini_path):
+        self.path = ini_path
+
+    # 保存修改;
+    def save_config(self, cfgParser):
+        with open(self.path, 'wb') as configfile:
+            if cfgParser:
+                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):
+        # 读取配置文件;
+        cfgParser = ConfigParser()
+        cfgParser.read(self.path)
+        if cfgParser.has_option(section, option):
+            return cfgParser.get(section, option)
+        return None
+
+    # 获取参数值;
+    def get_int(self, section, option):
+        # 读取配置文件;
+        cfgParser = ConfigParser()
+        cfgParser.read(self.path)
+        if cfgParser.has_option(section, option):
+            return cfgParser.getint(section, option)
+        return None
+
+    # 获取参数值;
+    def get_float(self, section, option):
+        # 读取配置文件;
+        cfgParser = ConfigParser()
+        cfgParser.read(self.path)
+        if cfgParser.has_option(section, option):
+            return cfgParser.getfloat(section, option)
+        return None
+
+    # 获取参数值;
+    def get_boolean(self, section, option):
+        # 读取配置文件;
+        cfgParser = ConfigParser()
+        cfgParser.read(self.path)
+        if cfgParser.has_option(section, option):
+            return cfgParser.getboolean(section, option)
+        return None
+
+    # 获取键值;
+    def get_options(self, section):
+        # 读取配置文件;
+        cfgParser = ConfigParser()
+        cfgParser.read(self.path)
+        if cfgParser.has_section(section):
+            return cfgParser.options(section)
+        return None
+
+    # 获取所有sections;
+    def get_sections(self):
+        # 读取配置文件;
+        cfgParser = ConfigParser()
+        cfgParser.read(self.path)
+        return cfgParser.sections()
+
+    # 获取指定section所有items;
+    def get_items(self, section):
+        # 读取配置文件;
+        cfgParser = ConfigParser()
+        cfgParser.read(self.path)
+        if cfgParser.has_section(section):
+            return cfgParser.items(section)
+        return None
+
+    # 添加section;
+    def add_section(self, section):
+        # 读取配置文件;
+        cfgParser = ConfigParser()
+        cfgParser.read(self.path)
+        if cfgParser.has_section(section) is False:
+            cfgParser.add_section(section)
+        # 保存修改;
+        self.save_config(cfgParser)
+
+    # 设置值;
+    def set_value(self, section, option, value):
+        # 读取配置文件;
+        cfgParser = ConfigParser()
+        cfgParser.read(self.path)
+        if cfgParser.has_section(section) is False:
+            cfgParser.add_section(section)
+        cfgParser.set(section, option, value)
+        # 保存修改;
+        self.save_config(cfgParser)
+
+    # 删除指定option;
+    def del_option(self, section, option):
+        # 读取配置文件;
+        cfgParser = ConfigParser()
+        cfgParser.read(self.path)
+        cfgParser.remove_option(section, option)
+        # 保存修改;
+        self.save_config(cfgParser)
+
+    # 删除section;
+    def del_section(self, section):
+        # 读取配置文件;
+        cfgParser = ConfigParser()
+        cfgParser.read(self.path)
+        cfgParser.remove_section(section)
+        # 保存修改;
+        self.save_config(cfgParser)
+
+    def has_section(self, section):
+        # 读取配置文件;
+        cfgParser = ConfigParser()
+        cfgParser.read(self.path)
+        return cfgParser.has_section(section)
+
+    def has_option(self, section, option):
+        # 读取配置文件;
+        cfgParser = ConfigParser()
+        cfgParser.read(self.path)
+        return cfgParser.has_option(section, option)
+
+
+try:
+    # Python_Home = os.environ["PYTHONHOME"].split(";")[0]
+    Python_Home = sys.prefix
+    # Resource_CFG_Path = Python_Home + Environment.SDK_Relative_Path + "config/resource.cfg"
+    Resource_CFG_Path = Python_Home + Environment.SDK_Relative_Path + "config/resource_run.cfg"
+    Resource_CFG_Local_Path = Python_Home + Environment.SDK_Relative_Path + "config/resource.cfg"
+    if not os.path.exists(Resource_CFG_Path):
+        print 'server_run.cfg文件不存在,复制server.cfg文件'
+        shutil.copy(Resource_CFG_Local_Path, Resource_CFG_Path)
+    # Resource_CFG_Path = "config/resource.cfg"
+    # 获取22293的配置路径
+    COMConfigPath = os.path.join(Python_Home, 'Tools', 'Config', 'COMConfig.txt')
+    Home_Init = True
+except KeyError, e:
+    print "KeyError:", e.message
+    Home_Init = False
+CFG_DICT = {}
+
+def getResourceConfigPath():
+    return Resource_CFG_Path
+
+def mkCHdir(dir):
+    # print "mkCHdir,dir=", dir
+    if (os.path.exists(dir)):
+        return dir
+    # 例如:D:/SAT/result_schedule_id_schedule_name_task_number//detail
+    # os.path.splite结果:('D:/SAT/result_schedule_id_schedule_name_task_number', 'detail')
+    # 过滤//多个分割符的情况
+    preDir = os.path.split(dir)[0]
+    # print "mkdir,preDir=", preDir
+    if (not os.path.exists(preDir)):
+        mkCHdir(preDir)
+    # 防止目录字符串最后带有/的情况,会出现两次创建同一个目录
+    # 例如: D:/SAT/。会创建D:/SAT和D:/SAT/
+    if (not os.path.exists(dir)):
+        os.mkdir(dir)
+    return dir
+
+
+def setLineParam(line):
+    global CFG_DICT
+    lineArr = line.split("=")
+    # print "setLineParam,lineArr:", lineArr
+    if (lineArr.__len__() == 2):
+        CFG_DICT[lineArr[0].strip(" ")] = lineArr[1].strip(" ")
+
+
+def loadResourceCfg():
+    global CFG_DICT
+    if Home_Init == False:
+        print "Not found config file,Please set the os path of PYTHONHOME"
+        return False
+    cfgFile = open(Resource_CFG_Path, "r", -1)
+    strline = "init"
+    while (strline <> None and strline <> ""):
+        strline = cfgFile.readline().strip("\n").strip("\r")
+        # print "line:", strline
+        info = strline.decode("utf-8", "error")
+        # 防止文本读取结束时,“#”判断出错
+        if (info.__len__() > 1 and info[0] <> "#"):
+            setLineParam(info)
+    cfgFile.close()
+    return True
+
+
+g_cfgParser = None
+
+
+def parseResourceCfg():
+    global g_cfgParser
+    global CFG_DICT
+    if Home_Init == False:
+        print "Not found config file,Please set the os path of PYTHONHOME"
+        return False
+    g_cfgParser = TConfig(Resource_CFG_Path)
+    return True
+
+
+parseResourceCfg()
+
+
+def getOCRIpAddr():
+    try:
+        return g_cfgParser.get_value("COMM", "Env_OCR_IP_ADDR")
+    except Exception, e:
+        print e
+        return ""
+
+
+def getOCRPort():
+    try:
+        return g_cfgParser.get_int("COMM", "Env_OCR_PORT")
+    except Exception, e:
+        print e
+        return -1
+
+
+def getSerialCOM():
+    try:
+        return g_cfgParser.get_value("COMM", "Serial_Communicator_Port")
+    except Exception, e:
+        print e
+        return ""
+
+
+def getSATHome():
+    try:
+        return g_cfgParser.get_value("COMM", "SAT_HOME")
+    except Exception, e:
+        print e
+        return ""
+
+
+def getSATTmpDIR():
+    satHome = getSATHome()
+    return os.path.join(satHome, "tmp")
+
+
+def getSATTranslationDIR():
+    satHome = getSATHome()
+    return os.path.join(satHome, "translation")
+
+
+def getSATRCUDIR():
+    satHome = getSATHome()
+    return os.path.join(satHome, "resource", "RCU")
+
+
+def getSATRCUGeneralDIR():
+    SATRCUDIR = getSATRCUDIR()
+    return os.path.join(SATRCUDIR, "general")
+
+
+def getSATRCUProductDIR():
+    SATRCUDIR = getSATRCUDIR()
+    return os.path.join(SATRCUDIR, "product")
+
+
+def getSATSmokingDIR():
+    satHome = getSATHome()
+    return os.path.join(satHome, "smoking")
+
+
+def getSATSmokingDownloaDIR():
+    SATSmokingDIR = getSATSmokingDIR()
+    return os.path.join(SATSmokingDIR, "download")
+
+
+def getSATSmokingOTAAPKDIR():
+    SATSmokingDIR = getSATSmokingDIR()
+    return os.path.join(SATSmokingDIR, "ota_apk")
+
+
+def getSATMenuTreeDIR():
+    satHome = getSATHome()
+    return os.path.join(satHome, "resource", "MenuTree")
+
+
+def getOCRTmpDir():
+    tmpDir = getSATTmpDIR()
+    return os.path.join(tmpDir, "ocr")
+
+
+def getOCRErrDir():
+    tmpDir = getSATTmpDIR()
+    return os.path.join(tmpDir, "ocr_err")
+
+
+def getSATProjectCfgDIR():
+    return os.path.join(getSATHome(), "resource", "projectCfg")
+
+
+def getResourceBaseDIR():
+    satHome = getSATHome()
+    return os.path.join(satHome, "resource")
+
+
+def getHDMIResourceDIR():
+    return os.path.join(getResourceBaseDIR(), "HDMI")
+
+
+def getHDMIstdTPDIR():
+    return os.path.join(getHDMIResourceDIR(), "std_timing_pattern")
+
+
+def getAVResourceDIR():
+    return os.path.join(getResourceBaseDIR(), "AV")
+
+
+def getAVstdTPDIR():
+    return os.path.join(getAVResourceDIR(), "std_timing_pattern")
+
+
+def getVGAResourceDIR():
+    return os.path.join(getResourceBaseDIR(), "VGA")
+
+
+def getVGAstdTPDIR():
+    return os.path.join(getVGAResourceDIR(), "std_timing_pattern")
+
+
+def getYpbprResourceDIR():
+    return os.path.join(getResourceBaseDIR(), "YPBPR")
+
+
+def getYpbprstdTPDIR():
+    return os.path.join(getYpbprResourceDIR(), "std_timing_pattern")
+
+
+def getDTVResourceDIR():
+    return os.path.join(getResourceBaseDIR(), "DTV")
+
+
+def getDTVstdTPDIR():
+    return os.path.join(getDTVResourceDIR(), "std_ts")
+
+
+def getATVResourceDIR():
+    return os.path.join(getResourceBaseDIR(), "ATV")
+
+
+def getATVstdTPDIR():
+    return os.path.join(getATVResourceDIR(), "std_ts")
+
+
+def getResultDir():
+    try:
+        return g_cfgParser.get_value("COMM", "SAT_RESULT_DIR")
+    except Exception, e:
+        print e
+        return "Error"
+
+
+def getPathModelDIR():
+    return os.path.join(getSATHome(), "model")
+
+
+def getAbnormalDir():
+    return os.path.join(getSATHome(), "abnormal")
+
+
+def initDirs():
+    print mkCHdir(getSATHome())
+    print mkCHdir(getResourceBaseDIR())
+    print mkCHdir(getSATTmpDIR())
+    print mkCHdir(getSATMenuTreeDIR())
+    print mkCHdir(getHDMIResourceDIR())
+    print mkCHdir(getHDMIstdTPDIR())
+    print mkCHdir(getAVResourceDIR())
+    print mkCHdir(getAVstdTPDIR())
+    print mkCHdir(getVGAResourceDIR())
+    print mkCHdir(getVGAstdTPDIR())
+    print mkCHdir(getYpbprResourceDIR())
+    print mkCHdir(getYpbprstdTPDIR())
+    print mkCHdir(getDTVResourceDIR())
+    print mkCHdir(getDTVstdTPDIR())
+    print mkCHdir(getATVResourceDIR())
+    print mkCHdir(getATVstdTPDIR())
+    print mkCHdir(getResultDir())
+    print mkCHdir(getPathModelDIR())
+    print mkCHdir(getAbnormalDir())
+    print mkCHdir(getOCRTmpDir())
+    print mkCHdir(getSATTranslationDIR())
+    print mkCHdir(getSATRCUDIR())
+    print mkCHdir(getSATRCUGeneralDIR())
+    print mkCHdir(getSATRCUProductDIR())
+    print mkCHdir(getSATSmokingDIR())
+
+    print mkCHdir(getSATSmokingDownloaDIR())
+    print mkCHdir(getSATSmokingOTAAPKDIR())
+
+
+initDirs()
+
+
+def setSATHome(dir):
+    try:
+        g_cfgParser.set_value("COMM", "SAT_HOME", dir)
+        # g_cfgParser.write(open(Resource_CFG_Path, "w"))
+    except Exception, e:
+        print e
+
+
+def setResultDir(dir):
+    try:
+        g_cfgParser.set_value("COMM", "SAT_RESULT_DIR", dir)
+        # g_cfgParser.write(open(Resource_CFG_Path, "w"))
+    except Exception, e:
+        print e
+
+
+def getUB530_Port():
+    return g_cfgParser.get_value("COMM", "ub530_port")
+
+
+def getTG39_Port():
+    return g_cfgParser.get_value("COMM", "tg39_port")
+
+
+def get22293_Port():
+    f = open(COMConfigPath)
+    data = f.read()
+    f.close()
+    data_dict = eval(data)
+    sourcegeninput = data_dict['SourceGenInput']
+    return sourcegeninput
+
+def getRCUDeviceList():
+    try:
+        return eval(g_cfgParser.get_value("devices", "rcudevice_list"))
+    except Exception,e:
+        return ['redrat3','redrat4']
+
+def getRCUDeviceSelected():
+    try:
+        return g_cfgParser.get_value("devices", "rcudevice_selected")
+    except Exception,e:
+        return 'redrat3'
+
+def writeRCUDeviceSelected(rcu_device):
+    g_cfgParser.set_value('devices', 'rcudevice_selected', rcu_device)
+
+def getRCUSelected():
+    return g_cfgParser.get_value("devices", "rcu_selected")
+
+
+def getCcardSelected():
+    return g_cfgParser.get_value("devices", "ccard_selected")
+
+
+def getATVSelected():
+    return g_cfgParser.get_value("devices", "atv_selected")
+
+
+def getDTVSelected():
+    return g_cfgParser.get_value("devices", "dtv_selected")
+
+
+def getAtv_list():
+    return eval(g_cfgParser.get_value("devices", "atv_list"))
+
+
+def getDtv_list():
+    return eval(g_cfgParser.get_value("devices", "dtv_list"))
+
+
+def writeDtv_list(dtv_list):
+    g_cfgParser.set_value('devices', 'dtv_list', str(dtv_list))
+
+
+def getCcard_list():
+    return eval(g_cfgParser.get_value("devices", "ccard_list"))
+
+
+def writeCcard_list(ccard_list):
+    g_cfgParser.set_value('devices', 'ccard_list', str(ccard_list))
+
+
+def getVoicecard_list():
+    return eval(g_cfgParser.get_value("devices", "voicecard_list"))
+
+
+def getSignalcard_list():
+    return eval(g_cfgParser.get_value("devices", "signalcard_list"))
+
+
+def getSerialport_list():
+    return eval(g_cfgParser.get_value("devices", "serialport_list"))
+
+
+def writeRCUSelected(rcu_selected_value):
+    g_cfgParser.set_value('devices', 'rcu_selected', rcu_selected_value)
+
+
+def writeCcardSelected(ccard_selected_value):
+    g_cfgParser.set_value('devices', 'ccard_selected', ccard_selected_value)
+
+
+def writeATVSelected(atv_selected_value):
+    g_cfgParser.set_value('devices', 'atv_selected', atv_selected_value)
+
+
+def writeDTVSelected(dtv_selected_value):
+    g_cfgParser.set_value('devices', 'dtv_selected', dtv_selected_value)
+
+
+def writeTG39_Port(port_number):
+    g_cfgParser.set_value('COMM', 'tg39_port', 'COM' + port_number)
+
+
+def write22293_Port(port_number):
+    f = open(COMConfigPath)
+    data = f.read()
+    data_dict = eval(data)
+    f.close()
+    f1 = open(COMConfigPath, 'w')
+    data_dict['SourceGenInput'] = 'COM' + port_number
+    f1.write(str(data_dict))
+    f1.close()
+
+
+def writeSerialCOM(port_number):
+    g_cfgParser.set_value('COMM', 'serial_communicator_port', 'COM' + port_number)
+
+
+def writeMenuTreeRootDir(menuTreeRootDir):
+    try:
+        g_cfgParser.add_section('MenuTree')
+    except Exception, e:
+        print e
+    g_cfgParser.set_value('MenuTree', 'menuTreeRootDir', menuTreeRootDir)
+
+
+def writeMenuTreeSelectedChip(selectedChip):
+    try:
+        g_cfgParser.add_section('MenuTree')
+    except Exception, e:
+        print e
+    g_cfgParser.set_value('MenuTree', 'menuTreeSelectedChip', selectedChip)
+
+
+def writeMenuTreeSelectedStyle(selectedStyle):
+    try:
+        g_cfgParser.add_section('MenuTree')
+    except Exception, e:
+        print e
+    g_cfgParser.set_value('MenuTree', 'menuTreeSelectedStyle', selectedStyle)
+
+
+def writeMenuTreeSelectedChannel(selectedChannel):
+    try:
+        g_cfgParser.add_section('MenuTree')
+    except Exception, e:
+        print e
+    g_cfgParser.set_value('MenuTree', 'menuTreeSelectedChannel', selectedChannel)
+
+
+def writeMenuTreeSelectedTVUI(selectedTVUI):
+    try:
+        g_cfgParser.add_section('MenuTree')
+    except Exception, e:
+        print e
+    g_cfgParser.set_value('MenuTree', 'menuTreeSelectedTVUI', selectedTVUI)
+
+
+def getRCUSelectedFilePath():
+    # rootPath = os.path.join(Python_Home, 'Tools', 'redhathub', 'data')
+    rootPath = getSATRCUProductDIR()
+    fileName = getRCUSelected() + '.xml'
+    filePath = os.path.join(rootPath, fileName)
+    print 'filePath:', filePath
+    return filePath
+
+
+def getBaiduOCR_APPID():
+    return g_cfgParser.get_value("baidu_ocr", "APP_ID")
+
+
+def getBaiduOCR_APIKey():
+    return g_cfgParser.get_value("baidu_ocr", "API_KEY")
+
+
+def getBaiduOCR_SecretKey():
+    return g_cfgParser.get_value("baidu_ocr", "SECRET_KEY")
+
+
+def getMenuTreeRootDir():
+    value = None
+    try:
+        value = g_cfgParser.get_value('MenuTree', 'menuTreeRootDir')
+        return value
+    except Exception, e:
+        print e
+    return value
+
+
+def getMenuTreeSelectedChip():
+    value = None
+    try:
+        value = g_cfgParser.get_value('MenuTree', 'menuTreeSelectedChip')
+        return value
+    except Exception, e:
+        print e
+    return value
+
+
+def getMenuTreeSelectedStyle():
+    value = None
+    try:
+        value = g_cfgParser.get_value('MenuTree', 'menuTreeSelectedStyle')
+        return value
+    except Exception, e:
+        print e
+    return value
+
+
+def getMenuTreeSelectedChannel():
+    value = None
+    try:
+        value = g_cfgParser.get_value('MenuTree', 'menuTreeSelectedChannel')
+        return value
+    except Exception, e:
+        print e
+    return value
+
+
+def getMenuTreeSelectedTVUI():
+    value = None
+    try:
+        value = g_cfgParser.get_value('MenuTree', 'menuTreeSelectedTVUI')
+    except Exception, e:
+        print e
+    if not value:
+        return -1
+    return int(value)
+
+
+def getMenuTreeSelectedTreePath():
+    return os.path.join(getCurrentMenuTreeDir(), 'uitree.ini')
+
+
+def getMenuTreeSelectedProjectCfgPath():
+    return os.path.join(getSATProjectCfgDIR(), getMenuTreeSelectedChip(), getMenuTreeSelectedStyle())
+    # return os.path.join(getSATMenuTreeDIR(), getMenuTreeSelectedChip(), getMenuTreeSelectedStyle())
+
+def getCurrentMenuTreeDir():
+    return os.path.join(getSATMenuTreeDIR(), getMenuTreeSelectedChip(), getMenuTreeSelectedStyle())
+
+def getMenuTree3SelectedProjectCfgPath():
+    # return os.path.join(getSATProjectCfgDIR(), getMenuTreeSelectedChip(), getMenuTreeSelectedStyle())
+    return os.path.join(getCurrentMenuTreeDir())
+
+
+def getMenuTree3SelectedPExcelPath():
+    print u"MenuTree3框架加载的非tv表格路径为:", os.path.join(getCurrentMenuTreeDir(), "MenuTree.xls")
+    return os.path.join(getCurrentMenuTreeDir(), "MenuTree.xls")
+
+def getMenuTree3SelectedTvExcelPath():
+    # return os.path.join(getSATProjectCfgDIR(), getMenuTreeSelectedChip(), getMenuTreeSelectedStyle())
+    channel = getMenuTreeSelectedChannel()
+    if not channel or str(channel) == "":
+        print u"未能读取到tv表格!!MenuTree3框架将使用默认的表格路径:", os.path.join(getCurrentMenuTreeDir(),
+                                                    "MenuTree.xls")
+        return os.path.join(getCurrentMenuTreeDir(), "MenuTree.xls")
+    else:
+        print u"当前加载的TV制式为%s,MenuTree3框架载入的TV表格路径为:"%channel, os.path.join(getCurrentMenuTreeDir(),
+                                                    "MenuTree_" + channel + ".xls")
+        return os.path.join(getCurrentMenuTreeDir(),
+                            "MenuTree_" + channel + ".xls")
+
+    return os.path.join(getCurrentMenuTreeDir())
+
+def getChroma22293():
+    try:
+        return json.loads(g_cfgParser.get_value("COMM", "Chroma22293"))
+    except Exception, e:
+        print e
+        return {}
+
+
+def setChroma22293(strJson):
+    g_cfgParser.set_value('COMM', 'Chroma22293', strJson)
+
+
+# 获取Runner端TCPServer的port
+def getRunnerTcpPort():
+    port = 9900
+    prot_cfg_str = readConfigFile('COMM', 'runner_tcp_port')
+    if prot_cfg_str:
+        prot_cfg_int = int(prot_cfg_str)
+        port = prot_cfg_int
+    # print 'port:', port, type(port)
+    return port
+
+
+# 写入Runner端TCPServer的port
+def writeRunnerTcpPort(port):
+    writeConfigFile('COMM', 'runner_tcp_port', str(port))
+
+
+# 获取Tester端 设备监听TCPServer的port
+def getTesterListenTcpPort():
+    port = 20000
+    try:
+        prot_cfg_str = readConfigFile('COMM', 'tester_listen_tcp_port')
+    except Exception:
+        prot_cfg_str = None
+    if prot_cfg_str:
+        prot_cfg_int = int(prot_cfg_str)
+        port = prot_cfg_int
+    # print 'port:', port, type(port)
+    return port
+
+
+# 写入脚本中全步骤截图的开关
+def writeRestart(isSendKeyTakePicture):
+    writeConfigFile('COMM', 'isRestart', str(isSendKeyTakePicture))
+
+
+# 获取脚本中全步骤截图的开关
+def getIsRestart():
+    # isSendKeyTakePicture = "False"
+    try:
+        isSendKeyTakePicture = readConfigFile('COMM', 'isRestart')
+    except Exception:
+        isSendKeyTakePicture = "False"
+
+    return isSendKeyTakePicture
+
+
+# 写入脚本中全步骤截图的开关
+def writeIsSendKeyTakePicture(isSendKeyTakePicture):
+    writeConfigFile('COMM', 'isSendKeyTakePicture', str(isSendKeyTakePicture))
+
+
+# 获取脚本中全步骤截图的开关
+def getIsSendKeyTakePicture():
+    # isSendKeyTakePicture = "False"
+    try:
+        isSendKeyTakePicture = readConfigFile('COMM', 'isSendKeyTakePicture')
+    except Exception:
+        isSendKeyTakePicture = "False"
+
+    return isSendKeyTakePicture
+
+
+# 写入Tester界面中中全步骤截图的开关
+def writeIsSendKeyTakePictureTester(isSendKeyTakePicture):
+    writeConfigFile('COMM', 'isSendKeyTakePicture_tester', str(isSendKeyTakePicture))
+
+
+# 读取Tester界面中中全步骤截图的开关
+def getIsSendKeyTakePictureTester():
+    # isSendKeyTakePicture = "False"
+    try:
+        isSendKeyTakePicture = readConfigFile('COMM', 'isSendKeyTakePicture_tester')
+    except Exception:
+        isSendKeyTakePicture = "False"
+
+    return isSendKeyTakePicture
+
+
+# 写入Runner端TCPServer的port
+def writeTesterListenTcpPort(port):
+    writeConfigFile('COMM', 'tester_listen_tcp_port', str(port))
+
+
+# 写入配置文件接口
+def writeConfigFile(section, key, value):
+    try:
+        g_cfgParser.add_section(section)
+    except Exception, e:
+        print e
+    g_cfgParser.set_value(section, key, value)
+
+
+# 读取配置文件接口
+def readConfigFile(session, key):
+    value = None
+    try:
+        value = g_cfgParser.get_value(session, key)
+        return value
+    except Exception, e:
+        print e
+    return value
+
+
+# 读取音频设备列表;
+def getVoicecards():
+    try:
+        return json.loads(g_cfgParser.get_value("devices", "voicecards"))
+    except Exception, e:
+        print e
+        return {}
+
+
+# 写入音频设备列表;
+def setVoicecards(strJson):
+    g_cfgParser.set_value('devices', 'voicecards', strJson)
+
+
+'''
+获取视频采集卡型号
+'''
+
+
+def getCCard_Selected():
+    return readConfigFile('devices', 'ccard_selected')
+
+
+def getSoundList():
+    try:
+        sound_list_str = g_cfgParser.get_value("Sound", "sound_list")
+        if type(sound_list_str) == type(''):
+            sound_list = eval(sound_list_str)
+        else:
+            sound_list = sound_list_str
+        if sound_list == []:
+            sound_list = [1000, 1000]
+            writeSoundList(sound_list)
+        return sound_list
+    except Exception, e:
+        print e
+        sound_list = [1000, 1000]
+        writeSoundList(sound_list)
+        return sound_list
+
+
+def writeSoundList(sound_list):
+    writeConfigFile('Sound', 'sound_list', str(sound_list))
+
+def getOtaParamUrl_dict():
+    return eval(g_cfgParser.get_value("URL", "getParam"))
+
+def getAdbDeviceSatatus():
+    Online = "Online"
+    options = g_cfgParser.get_options("ADBSTATUS")
+    print "getAdbDeviceSatatus,options:",options
+    if options is None:
+        return False
+    for option in options:
+        status = g_cfgParser.get_value("ADBSTATUS",option)
+        print "getAdbDeviceSatatus,%s:"%option, status
+        if status == Online:
+            return True
+    return False
+
+
+if __name__ == "__main__":
+    dict = getOtaParamUrl_dict()
+    print "CN:",dict["CN"]
+    print "ME:",dict["ME"]
+    # print "OCR IP addr:", getOCRIpAddr()
+    # print "OCR port:", getOCRPort()
+    # print "Serial port:", getSerialCOM()
+    # print "SAT Home:", getSATHome()
+    # # setResultDir("D:/SAT/result_schedule_id_schedule_name_task_number/")
+    # # print "Result DIR:", getResultDir()
+    #
+    # print 'getTG39_Port:', getTG39_Port()
+    # print 'getRCUSelected:', getRCUSelected()
+    # print 'getAtv_list:', getAtv_list(), type(getAtv_list())
+    # writeRCUSelected('RC322')
+    # list = getChroma22293()
+    # for item in list:
+    #     print item["name"],item["port"]
+    # print list
+
+    # getDtv_list()
+    # getRunnerTcpPort()
+    # writeRunnerTcpPort(9966)
+    # getRunnerTcpPort()

+ 61 - 0
ssat_sdk/serial_tool.py

@@ -0,0 +1,61 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+
+from TST.SerialBoard import *
+
+'''
+继承自TST serial,接口类废弃
+'''
+class SerialTool():
+
+    def __init__(self):
+        self.serialBoard = SerialBoard()
+
+    '''
+    返回当前串口板的端口号
+    [return] str Portname
+    '''
+    @staticmethod
+    def GetPortName():
+        return SerialBoard.GetPortName()
+
+    '''
+    打开串口
+    [return]Bool;
+    返回Bool 型,说明串口打开是否成功
+    '''
+    def open(self):
+        return self.serialBoard.open()
+
+    '''
+    关闭串口
+    [return] Bool;
+    返回Bool型,说明串口关闭是否成功
+    '''
+    def close(self):
+        return self.close()
+
+    '''
+    新建txt文件用以存储Log
+    [return]str LogFilePath
+    返回新建的txt文件路径
+    '''
+    def logFilePath(self):
+        return self.serialBoard.logFilePath()
+
+    '''
+    打开串口打印
+    1)当Recordtype为 both时,串口打印写入指定文件的同时在命令行同步显示
+    2)当Recordtype为OSD时,只在命令行显示串口打印
+    3)当Recordtype为Filewrite时,串口打印不在命令行显示,只写入文件
+
+    [in] str Recordtype
+    recordtype: ‘both’,'OSD','Filewrite' 三种类型
+    [in]str LogFilepath
+    当Recordtype为‘OSD’时,此参数不传参
+    当Recordtype为‘both’和‘Filewrite’时,必须传参,否则报错
+    指定串口打印写入文件的路径
+    [Return] Bool;
+    '''
+    def recordLogStart(self, recordType, logFilePath):
+        return self.serialBoard.recordLogStart(recordType, logFilePath)

+ 284 - 0
ssat_sdk/service/Chroma22293.py

@@ -0,0 +1,284 @@
+# -*- coding: UTF-8 -*-
+import serial
+import time
+from ssat_sdk.sat_environment import *
+from ssat_sdk.utils import LoggingUtil
+import inspect
+from ssat_sdk.utils.AbnormalClient import abnormal_client
+
+pyFileName = os.path.split(__file__)[-1]
+def get_current_function_name():
+    return inspect.stack()[1][3]
+
+class Chroma22293():
+    # 构造函数
+    def __init__(self, port):
+        self.loop = 0
+        self.timeout = 0.2
+        self.ser = serial.Serial()
+        self.ser.port = port
+        self.ser.baudrate = 115200
+        self.ser.bytesize = 8
+        self.ser.parity = serial.PARITY_NONE
+        self.ser.stopbits = 1
+        self.ser.timeout = 1  # 读超时;
+        self.ser.writeTimeout = 0.5  # 写超时;
+        self.className = self.__class__.__name__
+
+    # 析构函数;
+    def __del__(self):
+        self.close()
+    
+    def sendAbnormal(self, msg):
+        curport = 'COM'+str(self.ser.port)
+        dic = getChroma22293()
+        if dic['devices'][0]['port'] == curport:
+            abnormal_client.sendAbnormal('signalcard', msg)
+        else:
+            abnormal_client.sendAbnormal('signalcard_2', msg)
+
+    def open(self):
+        try:
+            self.ser.open()
+            if self.ser.is_open == True:
+                # print "%s : Open Success" % self.ser.port
+                LoggingUtil.getDebugLogger().info(
+                    pyFileName, 
+                    self.className, 
+                    get_current_function_name(), 
+                    str(self.ser.port)+'Open Success')
+                print self.ser.get_settings()
+                print "rts=" + str(self.ser.rts)
+                print "cts=" + str(self.ser.cts)
+            else:
+                # print "%s : Open Fail" % self.ser.port
+                LoggingUtil.getDebugLogger().info(
+                    pyFileName, 
+                    self.className, 
+                    get_current_function_name(), 
+                    str(self.ser.port)+'Open Fail')
+            # 返回结果;
+            return self.ser.is_open
+        except Exception, e:
+            # print u'打开串口失败,描述:', e
+            LoggingUtil.getDebugLogger().info(
+                pyFileName, 
+                self.className, 
+                get_current_function_name(), 
+                '打开串口失败,描述:'+str(e))
+            
+            self.sendAbnormal(str(e))
+            return False
+
+    def writeCmd(self, cmdstr):
+        try:
+            len = self.ser.write(cmdstr + b"\r")  # 注意后面要回车键(\r),命令才会发送出去执行
+            print "Sending cmd:" + cmdstr + " len=" + str(len)
+            return True
+        except Exception, e:
+            self.sendAbnormal(str(e))
+            # print u'写串口失败,描述:', e
+            LoggingUtil.getDebugLogger().info(
+                pyFileName, 
+                self.className, 
+                get_current_function_name(), 
+                '写串口失败,描述:'+str(e))
+            #重新打开串口;
+            self.close()
+            self.open()
+            return False
+
+    def readResult(self):
+        """
+        If the result is normal, "OK;.." will be output.
+            OK;[CR][LF]                     正常        
+        If the result is an error, the following message will be output.
+            NG: SYNTAX ERRO;[CR][LF]        指令语法错误
+            NG: BOUNDARY ERROR;[CR][LF]     输入参数超出范围
+            NG: EXECUTE ERROR;[CR][LF]      系统执行指令时发生错误
+        """
+        try:
+            self.loop = 0
+            time.sleep(self.timeout)
+            while self.ser.in_waiting == 0:
+                time.sleep(self.timeout)
+                self.loop += 1
+                if self.loop == 10:
+                    break
+            result = self.ser.readlines()
+            print "cmd result:" + str(result)
+            if len(result) == 0:
+                result = ['ng1: read none', 'ng2: read none']
+            return result
+        except Exception, e:
+            self.sendAbnormal(str(e))
+            # print u'读串口失败,错误描述:', e
+            LoggingUtil.getDebugLogger().info(
+                pyFileName, 
+                self.className, 
+                get_current_function_name(), 
+                '读串口失败,错误描述:'+str(e))
+            #重新打开串口;
+            self.close()
+            self.open()
+            return ['ng1: SerialException', 'ng2: SerialException']
+
+    def execmd(self, cmdstr):
+        if self.writeCmd(cmdstr):
+            return self.readResult()
+        else:
+            return ['ng1: SerialException', 'ng2: SerialException']
+
+    def close(self):
+        self.ser.close()
+        if self.ser.is_open == False:
+            # print "%s :Close Success" % self.ser.port
+            LoggingUtil.getDebugLogger().info(
+                pyFileName, 
+                self.className, 
+                get_current_function_name(), 
+                str(self.ser.port)+'Close Success')
+        else:
+            # print "%s :Close Fail" % self.ser.port
+            LoggingUtil.getDebugLogger().info(
+                pyFileName, 
+                self.className, 
+                get_current_function_name(), 
+                str(self.ser.port)+'Close Fail')
+
+    def __del__(self):
+        if self.ser.is_open == True:
+            self.ser.close()
+
+    def parseResult(self, retStrArr):
+        for retStr in retStrArr:
+            if "ok;" in retStr.lower():
+                return True
+        return False
+
+    # 返回配置文件;
+    def getConfigName(self):
+        pass
+
+    # 返回机型;
+    def getDeviceName(self):
+        cmd = 'report model;'
+        return self.execmd(cmd)
+
+    # 返回软件版本号,如V 1.0
+    def getDeviceSoft(self):
+        cmd = 'report ver;'
+        return self.execmd(cmd)
+
+    # 做BUF测试,无误返回ok;
+    def getStatus(self):
+        cmd = 'dummy;'
+        result = self.execmd(cmd)
+        return self.parseResult(result)
+
+    def setPattern(self, param):
+        cmd = 'run ptn %s;' % param
+        result = self.execmd(cmd)
+        self.getStatus()
+        return self.parseResult(result)
+
+    def setTiming(self, param):
+        cmd = 'run tim %s;' % param
+        result = self.execmd(cmd)
+        self.getStatus()
+        return self.parseResult(result)
+
+    def setTimingPattern(self, param1, param2):
+        # timing
+        cmd = 'run tim %s;' % param1
+        result1 = self.execmd(cmd)
+        self.getStatus()
+        time.sleep(0.5)
+        # patten
+        cmd = 'run ptn %s;' % param2
+        result2 = self.execmd(cmd)
+        self.getStatus()
+        # result;
+        ret = self.parseResult(result1) and self.parseResult(result2)
+        if ret == False:
+            LoggingUtil.getDebugLogger().info(
+                pyFileName, 
+                self.className, 
+                get_current_function_name(), 
+                'setTimingPattern失败结果:'+str(result1)+str(result2))
+        return ret
+
+    def setBlueOFF(self):
+        cmd = 'b off;'
+        result = self.execmd(cmd)
+        return self.parseResult(result)
+
+    def setBuleON(self):
+        cmd = 'b on;'
+        result = self.execmd(cmd)
+        return self.parseResult(result)
+
+    def setGreenOFF(self):
+        cmd = 'g off;'
+        result = self.execmd(cmd)
+        return self.parseResult(result)
+
+    def setGreenON(self):
+        cmd = 'g on;'
+        result = self.execmd(cmd)
+        return self.parseResult(result)
+
+    def setRedOFF(self):
+        cmd = 'r off;'
+        result = self.execmd(cmd)
+        return self.parseResult(result)
+
+    def setRedON(self):
+        cmd = 'r on;'
+        result = self.execmd(cmd)
+        return self.parseResult(result)
+
+    def setKeyBoardLock(self):
+        cmd = 'kb lock on;'
+        result = self.execmd(cmd)
+        return self.parseResult(result)
+
+    def setKeyBoardUnLock(self):
+        cmd = 'kb lock off;'
+        result = self.execmd(cmd)
+        return self.parseResult(result)
+
+
+if __name__ == "__main__":
+    pass
+    ser = Chroma22293("COM6")
+    if ser.open():
+        print ser.getDeviceName() #使用这个命令,导致其他命令无法正常使用;
+        print ser.getDeviceSoft()
+        print ser.getStatus()
+        #
+        print ser.setPattern(5)
+        print ser.setTiming(15)
+        print ser.setTimingPattern(2,12)
+        print ser.setBlueOFF()
+        print ser.setBuleON()
+        print ser.setGreenOFF()
+        print ser.setGreenON()
+        print ser.setRedOFF()
+        print ser.setRedON()
+        print ser.setKeyBoardLock()
+        print ser.setKeyBoardUnLock()
+
+        ser.close()
+
+    # listObj = {}
+    # list = getChroma22293()
+    # print list
+    # for item in list:
+    #     obj = Chroma22293(item["port"])
+    #     # obj.open()
+    #     if obj.open():
+    #         listObj[item["name"]] = obj
+
+    # print listObj
+    # listObj["Google"].getDeviceName()

+ 543 - 0
ssat_sdk/service/TC_UB530_service.py

@@ -0,0 +1,543 @@
+# -*- coding:utf-8 -*-
+
+from ssat_sdk.sat_environment import *
+from ssat_sdk.core.ibinder_listener import *
+from ssat_sdk.service.service_config import *
+from ssat_sdk.utils import string_util
+from ssat_sdk.utils import platform_Util
+from ssat_sdk.picture.image_util import ImageUtil
+
+import sys, os
+from PyQt4.QtGui import *
+from PyQt4.QtCore import *
+from ctypes.wintypes import HWND, LPCSTR, UINT
+from ctypes import *
+import time
+import threading
+import thread
+import psutil
+import numpy as np
+import cv2 as cv
+from ssat_sdk.utils.AbnormalClient import abnormal_client
+
+CardType = getCCard_Selected()
+print "Captured CardType:",CardType
+if "TC-UB530".lower() == CardType.lower():
+    QCAP = CDLL('QCAP.X86.DLL')
+ULONG = c_int32
+PVOID = c_void_p
+device0 = c_void_p(1)
+m_bNoSignal = c_bool(False)
+m_pColorSpaceType = c_ulong(0)
+m_pWidth = c_ulong(0)
+m_pHeight = c_ulong(0)
+m_pIsInterleaved = c_bool(False)
+m_pFrameRate = c_double(0)
+m_pChannels = c_ulong(0)
+m_pBitsPerSample = c_ulong(0)
+m_pSampleFrequency = c_ulong(0)
+m_strFormatChangedOutput = QString("INFO...")
+
+Mirror_Vertical = 0
+g_fileName = ''
+g_command = None
+g_params = None
+
+# Create the C callable callback
+PF_NO_SIGNAL_DETECTED_CALLBACK = CFUNCTYPE(c_ulong, c_void_p, c_long, c_long, c_void_p)
+PF_SIGNAL_REMOVED_CALLBACK = CFUNCTYPE(c_ulong, c_void_p, c_long, c_long, c_void_p)
+PF_FORMAT_CHANGED_CALLBACK = CFUNCTYPE(c_ulong, c_void_p, c_ulong, c_ulong, c_ulong, c_ulong, c_int, c_double, c_ulong,
+                                       c_ulong, c_ulong, c_void_p)
+PF_VIDEO_PREVIEW_CALLBACK = CFUNCTYPE(c_ulong, c_void_p, c_double, c_void_p, c_ulong, c_void_p)
+PF_AUDIO_PREVIEW_CALLBACK = CFUNCTYPE(c_ulong, c_void_p, c_double, c_void_p, c_ulong, c_void_p)
+PF_SNAPSHOT_DONE_CALLBACK = CFUNCTYPE(c_void_p, c_char_p,c_void_p)
+
+# 截图、录屏回调函数;
+PF_SNAPSHOT_DONE_CALLBACK = CFUNCTYPE(None, c_void_p, c_char_p, c_void_p)
+PF_SNAPSHOT_STREAM_CALLBACK = CFUNCTYPE(None, c_void_p, c_char_p, c_void_p, c_ulong, c_void_p)
+
+class QWindow(QWidget):
+    def __init__(self):
+        print "%r" % super(QWindow, self);
+        super(QWindow, self).__init__()
+        self.setWindowTitle("Preview")
+        self.setWindowIcon(QIcon('image.ico'))
+        self.setGeometry(200, 50, 800, 500)
+        self.setWindowFlags(
+            Qt.WindowMinimizeButtonHint |  # 使能最小化按钮
+            Qt.WindowCloseButtonHint |  # 使能关闭按钮
+            Qt.WindowMaximizeButtonHint |  # 使能最大化按钮
+            Qt.WindowStaysOnTopHint)  # 窗体总在最前端
+        self.show()
+
+
+class QWindow1(QWidget):
+    def __init__(self):
+        self.savePath = "D:\\1.png"
+        print "%r" % super(QWindow1, self);
+        super(QWindow1, self).__init__()
+        self.imageUtil = ImageUtil()
+        self.setWindowTitle(u"UB530控制界面")
+        self.setWindowIcon(QIcon('image.ico'))
+        self.setGeometry(1002, 50, 550, 300)
+
+        self.label = QLabel("Info :", self)
+        self.label.setGeometry(3, 20, 30, 20);
+        self.label.setFont(QFont('Arial', 10, 0, False))
+
+        self.line_edit_signal_info = QLineEdit("INFO...", self);
+        self.line_edit_signal_info.setReadOnly(True)
+        self.line_edit_signal_info.setGeometry(38, 20, 310, 20);
+        self.line_edit_signal_info.setFont(QFont('Arial', 10, 0, False))
+
+        self.btn_video_input = QPushButton("Video Input", self);
+        self.btn_video_input.setGeometry(38, 50, 150, 50);
+        self.btn_video_input.setFont(QFont('Arial', 12, 0, False))
+        self.btn_video_input.clicked.connect(self.on_process_video_input)
+
+        self.btn_video_mirror = QPushButton(u"翻转", self);
+        self.btn_video_mirror.setGeometry(400, 50, 50, 50);
+        self.btn_video_mirror.setFont(QFont('Arial', 12, 0, False))
+        self.btn_video_mirror.clicked.connect(self.on_process_video_mirror)
+
+        self.btn_audio_input = QPushButton("Audio Input", self);
+        self.btn_audio_input.setGeometry(200, 50, 150, 50);
+        self.btn_audio_input.setFont(QFont('Arial', 12, 0, False))
+        self.btn_audio_input.clicked.connect(self.on_process_audio_input)
+
+        self.btn_snapshot_bmp = QPushButton("Snapshot BMP", self);
+        self.btn_snapshot_bmp.setGeometry(38, 110, 150, 50);
+        self.btn_snapshot_bmp.setFont(QFont('Arial', 12, 0, False))
+        self.btn_snapshot_bmp.clicked.connect(self.on_process_snapshot_bmp)
+
+        self.btn_snapshot_jpg = QPushButton("Snapshot JPG", self);
+        self.btn_snapshot_jpg.setGeometry(200, 110, 150, 50);
+        self.btn_snapshot_jpg.setFont(QFont('Arial', 12, 0, False))
+        self.btn_snapshot_jpg.clicked.connect(self.on_process_snapshot_jpg)
+
+        self.btn_record_start_1_1 = QPushButton("Start Record 1-1", self);
+        self.btn_record_start_1_1.setGeometry(38, 170, 150, 50);
+        self.btn_record_start_1_1.setFont(QFont('Arial', 12, 0, False))
+        self.btn_record_start_1_1.clicked.connect(self.on_process_record_start_1_1)
+
+        self.btn_record_stop_1_1 = QPushButton("Stop Record 1-1", self);
+        self.btn_record_stop_1_1.setGeometry(200, 170, 150, 50);
+        self.btn_record_stop_1_1.setFont(QFont('Arial', 12, 0, False))
+        self.btn_record_stop_1_1.clicked.connect(self.on_process_record_stop_1_1)
+
+        self.btn_record_start_1_2 = QPushButton("Start Record 1-2", self);
+        self.btn_record_start_1_2.setGeometry(38, 230, 150, 50);
+        self.btn_record_start_1_2.setFont(QFont('Arial', 12, 0, False))
+        self.btn_record_start_1_2.clicked.connect(self.on_process_record_start_1_2)
+
+        self.btn_record_stop_1_2 = QPushButton("Stop Record 1-2", self);
+        self.btn_record_stop_1_2.setGeometry(200, 230, 150, 50);
+        self.btn_record_stop_1_2.setFont(QFont('Arial', 12, 0, False))
+        self.btn_record_stop_1_2.clicked.connect(self.on_process_record_stop_1_2)
+
+        self.show()
+
+
+        def on_no_signal_detected(pDevice, nVideoInput, nAudioInput, pUserData):
+            print "------------no signal detected callback----------------"
+            m_bNoSignal = True;
+            return 0
+
+        def on_signal_removed(pDevice, nVideoInput, nAudioInput, pUserData):
+            print "------------signal removed callback----------------"
+            m_bNoSignal = True;
+            return 0
+
+        def on_format_changed(pDevice, nVideoInput, nAudioInput, nVideoWidth, nVideoHeight, bVideoIsInterleaved,
+                              dVideoFrameRate, nAudioChannels, nAudioBitsPerSample, nAudioSampleFrequency, pUserData):
+            print "-on_process_format_changed (%d, %d, %d, %d, %d, %f, %d, %d, %d, %r)" % (
+            nVideoInput, nAudioInput, nVideoWidth, nVideoHeight, bVideoIsInterleaved, dVideoFrameRate, nAudioChannels,
+            nAudioBitsPerSample, nAudioSampleFrequency, pUserData)
+            if nVideoWidth == 0 and nVideoHeight == 0:
+                m_bNoSignal = True
+            else:
+                m_bNoSignal = False
+            return 0
+
+        def on_video_preview(pDevice, dSampleTime, pFrameBuffer, nFrameBufferLen, pUserData):
+            # print("-on_video_preview (%f, %r, %d)" % (dSampleTime, pFrameBuffer, nFrameBufferLen));
+            return 0;
+
+        def on_audio_preview(pDevice, dSampleTime, pFrameBuffer, nFrameBufferLen, pUserData):
+            # print("-on_audio_preview (%f, %r, %d)" % (dSampleTime, pFrameBuffer, nFrameBufferLen));
+            return 0;
+        
+        def on_snap_shot_done(pDevice, pszFilePathName, pUserData=None):
+            print 'xxxxxxxxxxxxxxxxxx on_snap_shot_done,pszFilePathName=', c_char_p(pszFilePathName).value
+        
+        def on_snap_stream_done(pDevice, pszFilePathName, pStreamBuffer, StreamBufferLen, pUserData=None):
+            print 'xxxxxxxxxxxxxxxxxx on_snap_stream_done,pszFilePathName=', c_char_p(pszFilePathName).value
+
+
+        def fun_timer():
+            global timer
+            timer = threading.Timer(1, fun_timer)
+            timer.start()
+            if m_bNoSignal == True:
+                self.line_edit_signal_info.setText("INFO...");
+            else:
+                QCAP.QCAP_GET_VIDEO_CURRENT_INPUT_FORMAT(device0, byref(m_pColorSpaceType), byref(m_pWidth),
+                                                         byref(m_pHeight), byref(m_pIsInterleaved),
+                                                         byref(m_pFrameRate));
+
+                QCAP.QCAP_GET_AUDIO_CURRENT_INPUT_FORMAT(device0, byref(m_pChannels), byref(m_pBitsPerSample),
+                                                         byref(m_pSampleFrequency));
+
+                if m_pIsInterleaved == True:
+                    m_strFormatChangedOutput = "%d" % m_pWidth.value + " x %d" % m_pHeight.value + " I@ %0.1f" % m_pFrameRate.value + "FPS, %d" % m_pChannels.value + " CH x %d" % m_pBitsPerSample.value + "BITS x %d" % m_pSampleFrequency.value
+                else:
+                    m_strFormatChangedOutput = "%d" % m_pWidth.value + " x %d" % m_pHeight.value + " P@ %0.1f" % m_pFrameRate.value + "FPS, %d" % m_pChannels.value + " CH x %d" % m_pBitsPerSample.value + "BITS x %d" % m_pSampleFrequency.value
+
+                self.line_edit_signal_info.setText(m_strFormatChangedOutput);
+
+        #注册截图回调函数
+        snapshotDoneCallBack = PF_SNAPSHOT_DONE_CALLBACK(self.on_bmp_snapshot_done_callback)
+
+        self.m_pNoSignalDetectedCB = PF_NO_SIGNAL_DETECTED_CALLBACK(on_no_signal_detected)
+
+        self.m_pSignalRemovedCB = PF_SIGNAL_REMOVED_CALLBACK(on_signal_removed)
+
+        self.m_pFormatChangedCB = PF_FORMAT_CHANGED_CALLBACK(on_format_changed)
+
+        self.m_pVideoPreviewCB = PF_VIDEO_PREVIEW_CALLBACK(on_video_preview)
+
+        self.m_pAudioPreviewCB = PF_AUDIO_PREVIEW_CALLBACK(on_audio_preview)
+        self.m_pSnapShotDoneCB = PF_SNAPSHOT_DONE_CALLBACK(on_snap_shot_done)
+        self.m_pSnapShotStreamCB = PF_SNAPSHOT_STREAM_CALLBACK(on_snap_stream_done)
+
+        print "widget.winId():", widget.winId()
+        QCAP.QCAP_CREATE("CY3014 USB", 0, c_int32(widget.winId()), byref(device0), 1, 0)
+
+        QCAP.QCAP_REGISTER_FORMAT_CHANGED_CALLBACK(device0, self.m_pFormatChangedCB, None)
+
+        QCAP.QCAP_REGISTER_NO_SIGNAL_DETECTED_CALLBACK(device0, self.m_pNoSignalDetectedCB, None)
+
+        # QCAP.QCAP_REGISTER_SNAPSHOT_DONE_CALLBACK(device0,snapshotDoneCallBack, None)
+
+        QCAP.QCAP_REGISTER_SIGNAL_REMOVED_CALLBACK(device0, self.m_pSignalRemovedCB, None)
+
+        QCAP.QCAP_REGISTER_VIDEO_PREVIEW_CALLBACK(device0, self.m_pVideoPreviewCB, None)
+
+        QCAP.QCAP_REGISTER_AUDIO_PREVIEW_CALLBACK(device0, self.m_pAudioPreviewCB, None)
+
+        # QCAP.QCAP_REGISTER_SNAPSHOT_DONE_CALLBACK(device0, self.m_pSnapShotDoneCB, None)
+        # QCAP.QCAP_REGISTER_SNAPSHOT_STREAM_CALLBACK(device0, self.m_pSnapShotStreamCB, None)
+
+        QCAP.QCAP_SET_VIDEO_INPUT(device0, 2)
+
+        QCAP.QCAP_RUN(device0)
+
+        timer = threading.Timer(1, fun_timer)
+
+        timer.start()
+
+    def on_bmp_snapshot_done_callback(self, device,filePath):
+        print "on_bmp_snapshot_done_callback"
+
+    def on_process_snapshot_bmp(self):
+        global g_fileName
+        g_fileName = "D:/Snapshot.BMP"
+        if self.btn_snapshot_bmp.isEnabled():
+            ret = QCAP.QCAP_SNAPSHOT_BMP(device0, g_fileName, 1, 0)
+            print "on_process_snapshot_bmp:", ret
+
+
+    def on_process_snapshot_jpg(self):
+        global g_fileName
+        g_fileName = "D:/Snapshot.JPG"
+        if self.btn_snapshot_jpg.isEnabled():
+            ret = QCAP.QCAP_SNAPSHOT_JPG(device0, g_fileName, 100, 1, 0)
+            print "on_process_snapshot_jpg:", ret
+
+    def on_process_record_start_1_1(self):
+        if self.btn_record_start_1_1.isEnabled():
+            QCAP.QCAP_SET_VIDEO_RECORD_PROPERTY(device0, 0, 0, 0, 1, 8000, 10 * 1024 * 1024, 30, 0, 0, 0)
+            QCAP.QCAP_SET_AUDIO_RECORD_PROPERTY(device0, 0, 0, 1)
+            QCAP.QCAP_START_RECORD(device0, 0, "REC_CH1_1.MP4", 7, 0, 0, 0, 0)
+
+    def on_process_record_stop_1_1(self):
+        if self.btn_record_stop_1_1.isEnabled():
+            QCAP.QCAP_STOP_RECORD(device0, 0, 1, 0)
+
+    def on_process_record_start_1_2(self):
+        if self.btn_record_start_1_2.isEnabled():
+            QCAP.QCAP_SET_VIDEO_RECORD_PROPERTY(device0, 1, 0, 0, 1, 8000, 8 * 1024 * 1024, 30, 0, 0, 0)
+            QCAP.QCAP_SET_AUDIO_RECORD_PROPERTY(device0, 1, 0, 1)
+            QCAP.QCAP_START_RECORD(device0, 1, "REC_CH1_2.MP4", 7, 0, 0, 0, 0)
+
+    def on_process_record_stop_1_2(self):
+        if self.btn_record_stop_1_2.isEnabled():
+            QCAP.QCAP_STOP_RECORD(device0, 1, 1, 0)
+
+    def on_process_video_input(self):
+        if self.btn_video_input.isEnabled():
+            window_video_input.show()
+
+    def on_process_video_mirror(self):
+        print "on_process_video_mirror"
+        global Mirror_Vertical
+        # QCAP.QCAP_STOP(device0)
+        Mirror_Vertical += 1
+        if Mirror_Vertical%2 == 0:
+            QCAP.QCAP_SET_VIDEO_MIRROR(device0,False, False)
+        else:
+            QCAP.QCAP_SET_VIDEO_MIRROR(device0, True, True)
+        # QCAP.QCAP_RUN(device0)
+
+    def on_process_audio_input(self):
+        if self.btn_audio_input.isEnabled():
+            window_audio_input.show()
+
+    # close event (close window)
+    def closeEvent(self, event):
+        timer.cancel()
+        if device0 != 0:
+            QCAP.QCAP_STOP(device0)
+            QCAP.QCAP_DESTROY(device0)
+        widget.close()
+
+
+class VideoInputWindow(QWidget):
+    def __init__(self):
+        super(VideoInputWindow, self).__init__()
+        self.setWindowTitle("Set Video Input")
+        self.setGeometry(480, 200, 400, 300)
+
+        self.rb_HDMI = QRadioButton("HDMI", self)
+        self.rb_HDMI.setGeometry(20, 20, 150, 30);
+        self.rb_HDMI.setFont(QFont('Arial', 12, 0, False))
+        self.rb_HDMI.setFocusPolicy(Qt.NoFocus)
+        self.rb_HDMI.toggled.connect(self.on_process_video_input_hdmi)
+
+        self.rb_DVID = QRadioButton("DVI-D", self)
+        self.rb_DVID.setGeometry(20, 55, 150, 30);
+        self.rb_DVID.setFont(QFont('Arial', 12, 0, False))
+        self.rb_DVID.setFocusPolicy(Qt.NoFocus)
+        self.rb_DVID.toggled.connect(self.on_process_video_input_dvid)
+
+        self.rb_COMPONENTS = QRadioButton("COMPONENTS", self)
+        self.rb_COMPONENTS.setGeometry(20, 90, 150, 30);
+        self.rb_COMPONENTS.setFont(QFont('Arial', 12, 0, False))
+        self.rb_COMPONENTS.setFocusPolicy(Qt.NoFocus)
+        self.rb_COMPONENTS.toggled.connect(self.on_process_video_input_components)
+
+        self.rb_DVIA = QRadioButton("DVI-A", self)
+        self.rb_DVIA.setGeometry(20, 125, 150, 30);
+        self.rb_DVIA.setFont(QFont('Arial', 12, 0, False))
+        self.rb_DVIA.setFocusPolicy(Qt.NoFocus)
+        self.rb_DVIA.toggled.connect(self.on_process_video_input_dvia)
+
+        self.rb_SDI = QRadioButton("SDI", self)
+        self.rb_SDI.setGeometry(20, 160, 150, 30);
+        self.rb_SDI.setFont(QFont('Arial', 12, 0, False))
+        self.rb_SDI.setFocusPolicy(Qt.NoFocus)
+        self.rb_SDI.toggled.connect(self.on_process_video_input_sdi)
+
+        self.rb_COMPOSITE = QRadioButton("COMPOSITE", self)
+        self.rb_COMPOSITE.setGeometry(20, 195, 150, 30);
+        self.rb_COMPOSITE.setFont(QFont('Arial', 12, 0, False))
+        self.rb_COMPOSITE.setFocusPolicy(Qt.NoFocus)
+        self.rb_COMPOSITE.toggled.connect(self.on_process_video_input_composite)
+
+        self.rb_S_VIDEO = QRadioButton("S-VIDEO", self)
+        self.rb_S_VIDEO.setGeometry(20, 230, 150, 30);
+        self.rb_S_VIDEO.setFont(QFont('Arial', 12, 0, False))
+        self.rb_S_VIDEO.setFocusPolicy(Qt.NoFocus)
+        self.rb_S_VIDEO.toggled.connect(self.on_process_video_input_svideo)
+
+        self.rb_AUTO = QRadioButton("AUTO", self)
+        self.rb_AUTO.setGeometry(20, 265, 150, 30);
+        self.rb_AUTO.setFont(QFont('Arial', 12, 0, False))
+        self.rb_AUTO.setFocusPolicy(Qt.NoFocus)
+        self.rb_AUTO.toggled.connect(self.on_process_video_input_auto)
+
+        self.btn_OK = QPushButton("OK", self);
+        self.btn_OK.setGeometry(250, 265, 100, 30);
+        self.btn_OK.setFont(QFont('Arial', 12, 0, False))
+        self.btn_OK.clicked.connect(self.on_process_video_input_ok)
+
+    def on_process_video_input_hdmi(self):
+        if self.rb_HDMI.isEnabled():
+            QCAP.QCAP_SET_VIDEO_INPUT(device0, 2)
+
+    def on_process_video_input_dvid(self):
+        if self.rb_DVID.isEnabled():
+            QCAP.QCAP_SET_VIDEO_INPUT(device0, 3)
+
+    def on_process_video_input_components(self):
+        if self.rb_COMPONENTS.isEnabled():
+            QCAP.QCAP_SET_VIDEO_INPUT(device0, 4)
+
+    def on_process_video_input_dvia(self):
+        if self.rb_DVIA.isEnabled():
+            QCAP.QCAP_SET_VIDEO_INPUT(device0, 5)
+
+    def on_process_video_input_sdi(self):
+        if self.rb_SDI.isEnabled():
+            QCAP.QCAP_SET_VIDEO_INPUT(device0, 6)
+
+    def on_process_video_input_composite(self):
+        if self.rb_COMPOSITE.isEnabled():
+            QCAP.QCAP_SET_VIDEO_INPUT(device0, 0)
+
+    def on_process_video_input_svideo(self):
+        if self.rb_S_VIDEO.isEnabled():
+            QCAP.QCAP_SET_VIDEO_INPUT(device0, 1)
+
+    def on_process_video_input_auto(self):
+        if self.rb_AUTO.isEnabled():
+            QCAP.QCAP_SET_VIDEO_INPUT(device0, 7)
+
+    def on_process_video_input_ok(self):
+        self.close()
+
+
+class AudioInputWindow(QWidget):
+    def __init__(self):
+        super(AudioInputWindow, self).__init__()
+        self.setWindowTitle("Set Audio Input")
+        self.setGeometry(480, 200, 400, 100)
+
+        self.rb_EMBEDDED_AUDIO = QRadioButton("EMBEDDED AUDIO", self)
+        self.rb_EMBEDDED_AUDIO.setGeometry(20, 20, 200, 30);
+        self.rb_EMBEDDED_AUDIO.setFont(QFont('Arial', 12, 0, False))
+        self.rb_EMBEDDED_AUDIO.setFocusPolicy(Qt.NoFocus)
+        self.rb_EMBEDDED_AUDIO.toggled.connect(self.on_process_audio_input_embedded)
+
+        self.rb_LINE_IN = QRadioButton("LINE-IN", self)
+        self.rb_LINE_IN.setGeometry(20, 55, 200, 30);
+        self.rb_LINE_IN.setFont(QFont('Arial', 12, 0, False))
+        self.rb_LINE_IN.setFocusPolicy(Qt.NoFocus)
+        self.rb_LINE_IN.toggled.connect(self.on_process_audio_input_line_in)
+
+        self.btn_OK = QPushButton("OK", self);
+        self.btn_OK.setGeometry(250, 55, 100, 30);
+        self.btn_OK.setFont(QFont('Arial', 12, 0, False))
+        self.btn_OK.clicked.connect(self.on_process_audio_input_ok)
+
+    def on_process_audio_input_embedded(self):
+        if self.rb_EMBEDDED_AUDIO.isEnabled():
+            QCAP.QCAP_SET_AUDIO_INPUT(device0, 0)
+
+    def on_process_audio_input_line_in(self):
+        if self.rb_LINE_IN.isEnabled():
+            QCAP.QCAP_SET_AUDIO_INPUT(device0, 1)
+
+    def on_process_audio_input_ok(self):
+        self.close()
+
+
+class TC_UB530Service():
+    Service_Name = "ub530"
+    def __init__(self):
+        self.status = 0
+        self.serviceConfig = ServiceConfig()
+        # thread.start_new_thread(self.checkPP,())
+
+    def initUB530(self):
+        print u"初始化UB530设备"
+        ibinder = ListenerBinder(port=30300)
+        self.listener, port = ibinder.createListener()
+        self.serviceConfig.setUB530ListenerPort(port)
+        print u"UB530启动成功,UB530 Port:", port
+
+        # 注册回调函数;
+        self.m_pSnapShotDoneCB = PF_SNAPSHOT_DONE_CALLBACK(self.on_snap_shot_done)
+        self.m_pSnapShotStreamCB = PF_SNAPSHOT_STREAM_CALLBACK(self.on_snap_stream_done)
+        QCAP.QCAP_REGISTER_SNAPSHOT_DONE_CALLBACK(device0, self.m_pSnapShotDoneCB, None)
+        QCAP.QCAP_REGISTER_SNAPSHOT_STREAM_CALLBACK(device0, self.m_pSnapShotStreamCB, None)
+
+        self.status = 1
+        while (True):
+            try:
+                conn = self.listener.accept()
+                cmdLine = conn.recv()
+            except Exception, e:
+                print u"UB530接收指令失败", e
+                abnormal_client.sendAbnormal('ccard',str(e))
+                continue
+
+            print u"UB530接收指令:", cmdLine
+            result = self.execCommand(cmdLine)
+            try:
+                conn.send(result)
+            except Exception, e:
+                abnormal_client.sendAbnormal('ccard',str(e))
+                print e
+
+    def execCommand(self, cmdLine):
+        global g_command, g_params
+        g_command, g_params = cmdLine.split("::")
+        print "excCommand:Mirror_Vertical%2:",Mirror_Vertical%2
+        if (string_util.strcmp("snapshoot", g_command) or string_util.strcmp("captureByTime", g_command)):
+            global g_fileName
+            g_fileName = g_params
+            if g_fileName.lower().endswith("bmp"):
+                return QCAP.QCAP_SNAPSHOT_BMP(device0, "BMP", 1, 0)
+            elif g_fileName.lower().endswith("jpg") or g_fileName.lower().endswith("png"):
+                return QCAP.QCAP_SNAPSHOT_JPG(device0, "JPG", 100, 1, 0)
+            else:
+                return -1
+        elif (string_util.strcmp("isalive", g_command)):
+            return 1
+
+    # 截图完成后的回调函数;
+    def on_snap_shot_done(self, pDevice, pszFilePathName, pUserData=None):
+        print 'on_snap_shot_done,pszFilePathName=', pszFilePathName
+        
+    # 截图完成后的流操作函数;
+    def on_snap_stream_done(self, pDevice, pszFilePathName, pStreamBuffer, StreamBufferLen, pUserData=None):
+        buffer = string_at(pStreamBuffer, StreamBufferLen)
+        if (string_util.strcmp("snapshoot", g_command)):
+            if Mirror_Vertical % 2:
+                img = cv.imdecode(np.frombuffer(buffer, dtype=np.uint8, count=-1),cv.IMREAD_COLOR)  # cv.IMREAD_COLOR/ANYDEPTH
+                imgUtil = ImageUtil()
+                dstImg = imgUtil.rotaImg180(img)
+                cv.imwrite(g_fileName, dstImg)
+            else:
+                fp = open(g_fileName, 'wb')
+                fp.write(buffer)
+                fp.close()
+
+    def checkPP(self):
+        while(1):
+            if ppid <> -1:
+                hasPP = platform_Util.checkProcess(ppid)
+                print "checkPP:", ppid, hasPP
+                if not hasPP:
+                    sys.exit(0)
+            time.sleep(3)
+
+    def startService(self):
+        global widget,widget1,window_video_input,window_audio_input
+        app = QApplication(sys.argv)
+        widget = QWindow()
+        widget1 = QWindow1()
+
+        window_video_input = VideoInputWindow()
+        window_video_input.hide()
+
+        window_audio_input = AudioInputWindow()
+        window_audio_input.hide()
+
+        thread.start_new_thread(self.initUB530, ())
+        # timer = threading.Timer(5, ub530Service.initUB530())
+        # timer.start()
+        sys.exit(app.exec_())
+        return self.status
+
+if __name__ == '__main__':
+    try:
+        ppid = sys.argv[1]
+    except Exception,e:
+        abnormal_client.sendAbnormal('ccard',str(e))
+        print u"No parameter parent process id."
+        ppid = -1
+
+    service = TC_UB530Service()
+    service.startService()

+ 2 - 0
ssat_sdk/service/__init__.py

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

+ 123 - 0
ssat_sdk/service/ccard_service.py

@@ -0,0 +1,123 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+import shutil
+
+import thread
+from threading import Lock
+
+from TST.CaptureCard import *
+
+from ssat_sdk.sat_environment import *
+from ssat_sdk.core.ibinder_listener import *
+from ssat_sdk.service.service_config import *
+from ssat_sdk.utils.AbnormalClient import abnormal_client
+
+class CCardService():
+    Service_Name = "ccard"
+    def __init__(self):
+        self.serviceConfig = ServiceConfig()
+        self.status = 0
+        self.capLock = Lock()
+        self.picTake = False
+        self.loopPicture = ""
+        self.pause = True
+        self.puaseTime = 0
+
+    def initCCard(self):
+        print u"初始化视频采集卡"
+        try:
+            self.ccard = CaptureCard()
+        except Exception,e:
+            abnormal_client.sendAbnormal('ccard', str(e))
+            print u"视频采集卡启动失败",e
+            self.status = 0
+            return 0,e.message
+        ibinder = ListenerBinder(port=30000)
+        self.listener, port = ibinder.createListener()
+        self.serviceConfig.setCCardListenerPort(port)
+        print u"视频采集卡启动成功,CCard Port:",port
+        self.status = 1
+        while (True):
+            try:
+                conn = self.listener.accept()
+                cmdLine = conn.recv()
+            except Exception,e:
+                abnormal_client.sendAbnormal('ccard', str(e))
+                print u"采集卡接受指令失败",e
+                continue
+            print u"CCard接收指令:", cmdLine
+            # cmdLine:指令+参数
+            result = self.execCommand(cmdLine)
+            try:
+                conn.send(result)
+            except Exception, e:
+                print e
+
+    def execCommand(self, cmdLine):
+        command, param = cmdLine.split("::")
+        if ("takepicture" == command):
+            return self.excuteCapPiture(param)
+        elif ("takePictureLoop" == command):
+            thread.start_new_thread(self.takePictureLoop,(param,))
+            return True
+        elif ("pausePictureLoop" == command):
+            self.puaseTime = int(param)
+            while (self.pause == False):
+                pass
+            return True
+        elif ("stopPictureLoop" == command):
+            self.picTake = False
+            return True
+
+
+    def closeCCard(self):
+        self.ccard.close()
+        self.ccard = None
+
+    def takePictureLoop(self, pic_path):
+        self.loopPicture = pic_path
+        self.picTake = True
+        # count = 0
+        while(self.picTake):
+            # count += 1
+            # print u"循环截图",count
+            self.pause = True
+            time.sleep(self.puaseTime)
+            self.pause = False
+            self.capLock.acquire()
+            self.capPicture(self.loopPicture)
+            self.capLock.release()
+
+    def excuteCapPiture(self, pic_path):
+        if self.picTake == True:
+            #确保循环截图图片存在
+            while os.path.isfile(self.loopPicture) <> True:
+                print u"循环截图,loopPicture:",os.path.isfile(self.loopPicture)
+            self.capLock.acquire()
+            try:
+                shutil.move(self.loopPicture, pic_path)
+                self.capLock.release()
+                return True
+            except Exception, e:
+                print e
+                abnormal_client.sendAbnormal('ccard', str(e))
+                self.capLock.release()
+                return False
+        else:
+            return self.capPicture(pic_path)
+
+    def capPicture(self, pic_path):
+        picname = os.path.split(pic_path)[1]
+        tmpPic = os.path.join(getSATTmpDIR(),picname)
+        self.ccard.takePicture(tmpPic)
+        try:
+            shutil.move(tmpPic, pic_path)
+            return True
+        except Exception,e:
+            print e
+            abnormal_client.sendAbnormal('ccard', str(e))
+            return False
+
+    def getStatus(self):
+        print u"CCard状态:", self.status
+        return self.status

+ 138 - 0
ssat_sdk/service/chroma_22293_service.py

@@ -0,0 +1,138 @@
+# -*- coding:utf-8 -*-
+import os
+import sys
+import time
+import thread
+import json
+#from TST.SourceGenInput import *
+from ssat_sdk.service.Chroma22293 import *
+#import Chroma22293
+
+from ssat_sdk.sat_environment import *
+from ssat_sdk.core.ibinder_listener import *
+from ssat_sdk.service.service_config import *
+from ssat_sdk.utils import LoggingUtil
+import inspect
+
+pyFileName = os.path.split(__file__)[-1]
+def get_current_function_name():
+    return inspect.stack()[1][3]
+
+class C22293Service():
+    Service_Name = "c22293"
+
+    def __init__(self):
+        self.serviceConfig = ServiceConfig()
+        self.status = 0
+        self.listObj = {}
+        self.default = ''
+        self.className = self.__class__.__name__
+
+    def initC22293(self):
+        print u"初始化22293采集卡"
+        try:
+            # self.SG = SourceGenInput()
+            parseResourceCfg()
+            list = getChroma22293()
+            self.default = list["default"]
+            for item in list["devices"]:
+                obj = Chroma22293(item["port"])
+                if obj.open():
+                    self.listObj[item["name"]] = obj
+                else:
+                    LoggingUtil.getDebugLogger().info(
+                        pyFileName, 
+                        self.className, 
+                        get_current_function_name(), 
+                        '22293启动失败'+str(item["name"])+str(item["port"]))
+                    # print u"22293启动失败:%s,%s" % (item["name"], item["port"])
+        except Exception, e:
+            # print u"22293启动失败", e
+            LoggingUtil.getDebugLogger().info(
+                pyFileName, 
+                self.className, 
+                get_current_function_name(), 
+                '22293启动失败'+str(e))
+            self.status = 0
+            return 0, e.message
+        ibinder = ListenerBinder(port=30200)
+        self.listener, port = ibinder.createListener()
+        self.serviceConfig.setC22293ListenerPort(port)
+        # print u"22293启动成功,22293 Port:", port
+        LoggingUtil.getDebugLogger().info(
+                pyFileName, 
+                self.className, 
+                get_current_function_name(), 
+                '22293启动成功,22293 Port:'+str(port))
+        self.status = 1
+        while (True):
+            try:
+                conn = self.listener.accept()
+                cmdLine = conn.recv()
+            except Exception, e:
+                # print u"22293接受指令失败", e
+                LoggingUtil.getDebugLogger().info(
+                    pyFileName, 
+                    self.className, 
+                    get_current_function_name(), 
+                    '22293接受指令失败:'+str(e))
+                continue
+            print u"22293接收指令:", cmdLine
+            # cmdLine:指令+参数 json {"command":"setPattern", "param":[]}
+            result = self.execCommand(cmdLine)
+            try:
+                conn.send(result)
+            except Exception, e:
+                # print e
+                LoggingUtil.getDebugLogger().info(
+                    pyFileName, 
+                    self.className, 
+                    get_current_function_name(), 
+                    '22293发送指令失败:'+str(e))
+
+    def execCommand(self, cmdLine):
+        data = json.loads(cmdLine)
+        command = data["command"]
+        param = data["param"]
+        device = data["device"]
+        # print u'deviceName:', device
+        if len(device) == 0:
+            device = self.default
+        print u'deviceName=>', device
+        if device in self.listObj.keys():
+            if ("getDeviceName" == command):
+                return self.listObj[device].getDeviceName()
+            elif ("getDeviceSoft" == command):
+                return self.listObj[device].getDeviceSoft()
+            elif ("getStatus" == command):
+                return self.listObj[device].getStatus()
+            elif ("setPattern" == command):
+                return self.listObj[device].setPattern(param[0])
+            elif ("setTiming" == command):
+                return self.listObj[device].setTiming(param[0])
+            elif ("setTimingPattern" == command):
+                return self.listObj[device].setTimingPattern(param[0], param[1])
+            elif ("setBlueOFF" == command):
+                return self.listObj[device].setBlueOFF()
+            elif ("setBuleON" == command):
+                return self.listObj[device].setBuleON()
+            elif ("setGreenOFF" == command):
+                return self.listObj[device].setGreenOFF()
+            elif ("setGreenON" == command):
+                return self.listObj[device].setGreenON()
+            elif (" setRedOFF" == command):
+                return self.listObj[device].setRedOFF()
+            elif ("setRedON" == command):
+                return self.listObj[device].setRedON()
+            elif ("setKeyBoardLock" == command):
+                return self.listObj[device].setKeyBoardLock()
+            elif ("setKeyBoardUnLock" == command):
+                return self.listObj[device].setKeyBoardUnLock()
+        else:
+            # print u'没有这个设备名称=%s,请检查是否书写正确' %device
+            LoggingUtil.getDebugLogger().info(
+                    pyFileName, 
+                    self.className, 
+                    get_current_function_name(), 
+                    '没有这个设备名称:'+str(device))
+            return False

+ 94 - 0
ssat_sdk/service/redrat_service.py

@@ -0,0 +1,94 @@
+# -*- coding: UTF-8 -*-
+import sys
+import os
+from os import path, access, R_OK
+import subprocess
+from subprocess import CalledProcessError
+import time
+from ssat_sdk.service.service_config import *
+from ssat_sdk.sat_environment import *
+
+
+class RedRatService():
+    # 类静态变量
+    Service_Name = "redrat"
+    HubCmd_exe = sys.prefix + r"/Tools/RedHatHub/RedRatHubCmd.exe"
+    cmdline = ""
+    child = None
+
+    def __init__(self):
+        self.status = 0
+        self.serviceConfig = ServiceConfig()
+        rcuDevice = getRCUDeviceSelected()
+        if (rcuDevice.lower() == "redrat3"):
+            RedRatService.HubCmd_exe = sys.prefix + r"/Tools/RedHatHub/RedRatHubCmd.exe"
+        elif(rcuDevice.lower() == "redrat4"):
+            RedRatService.HubCmd_exe = sys.prefix + r"/Tools/RedRatHub-V4.28/RedRatHubCmd.exe"
+        if path.exists(RedRatService.HubCmd_exe) and path.isfile(RedRatService.HubCmd_exe) and access(
+                RedRatService.HubCmd_exe, R_OK):
+            print r"RedRatHubCmd.exe : exists and is readable"
+        else:
+            print r"RedRatHubCmd.exe : file is missing or is not readable, please check if Python27\Tools\RedRat\RedRatHubCmd.exe is exists"
+
+        # 强制结束所有RedRatHubCmd.exe
+        self.taskkill()
+        print "RedRatHubCmd Start!"
+
+    def taskkill(self):
+        # 强制结束任务管理器所有 强制结束所有RedRatHubCmd.exe 进程
+        try:
+            command = 'taskkill /F /IM RedRatHubCmd.exe'
+            os.system(command)
+        except Exception, e:
+            print u"没有杀死RedRatHubCmd进程:", e
+
+    def isRunning(self, signalxml, port, conns):
+        if RedRatService.child is not None and RedRatService.child.poll() is None:
+            return True
+        else:
+            return False
+
+    def interrupt(self):
+        if RedRatService.child is not None and RedRatService.child.poll() is None:
+            RedRatService.child.kill()
+    def getChild(self):
+        return RedRatService.child
+
+    def initRedRat(self):
+        print u"初始化红老鼠设备"
+        self.interrupt()
+        # signalxml = self.serviceConfig.getRedRatHubSignalXml()
+        signalxml = getRCUSelectedFilePath()
+        port = self.serviceConfig.getRedRatHubPort()
+        print 'port:', port
+
+        if signalxml is None:
+            print u"初始化红老鼠失败,默认信号文件未填"
+            self.status = 0
+            return 0, u"初始化红老鼠失败,默认信号文件未填"
+        if path.exists(RedRatService.HubCmd_exe) and path.isfile(RedRatService.HubCmd_exe) and access(
+                RedRatService.HubCmd_exe, R_OK):
+            # 启动exe
+            RedRatService.cmdline = '"' + RedRatService.HubCmd_exe + '" "' + signalxml + '" --port=' + str(port)
+            print 'RedRatService.cmdline :', RedRatService.cmdline
+            RedRatService.child = subprocess.Popen(RedRatService.cmdline, stdout=subprocess.PIPE)
+            self.status = 1
+            return 1, ''
+            # if RedRatService.child is not None and RedRatService.child.poll() is None:
+            #     print 'paodaozhili'
+            #     self.status = 1
+            #     print RedRatService.child.stdout.read()
+            #     # return 1, ''
+            # else:
+            #     self.status = 0
+            #     return 0, u'正常退出'
+        else:
+            self.status = 0
+            return 0, 'RedRatHubCmd.exe文件不存在'
+
+    def help(self):
+        output = subprocess.check_output(RedRatService.HubCmd_exe + " " + "-?")
+        print "\n*******RedRatService CMD help**********" + output
+
+    def getStatus(self):
+        return self.status

+ 112 - 0
ssat_sdk/service/service_config.py

@@ -0,0 +1,112 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+import shutil
+
+from ssat_sdk.utils import Environment
+from ConfigParser import *
+import thread
+
+try:
+    Python_Home = sys.prefix
+    # Server_CFG_Path = Python_Home + Environment.SDK_Relative_Path + u"config/server.cfg"
+    Server_CFG_Path = Python_Home + Environment.SDK_Relative_Path + u"config/server_run.cfg"
+    Server_CFG_Local_Path = Python_Home + Environment.SDK_Relative_Path + u"config/server.cfg"
+    # Server_CFG_Path = "../config/server.cfg"
+    Home_Init = True
+except KeyError, e:
+    print "KeyError:", e.message
+
+class ServiceConfig():
+    ServiceModel = "service"
+    CFGParser = ConfigParser()
+    CFGParser.read(Server_CFG_Path)
+
+    def __init__(self):
+        if not os.path.exists(Server_CFG_Path):
+            print 'server_run.cfg文件不存在,复制server.cfg文件'
+            shutil.copy(Server_CFG_Local_Path, Server_CFG_Path)
+        print u"Server配置文件:", Server_CFG_Path
+
+    def writeCFG(self):
+        self.CFGParser.write(open(Server_CFG_Path, "w"))
+
+    def setCCardListenerPort(self, listener_port):
+        self.CFGParser.set("service","ccard_port", listener_port)
+        self.writeCFG()
+
+    def getCCardListenerPort(self):
+        value = self.CFGParser.getint("service", "ccard_port")
+        return value
+
+    def setC22293ListenerPort(self, listener_port):
+        self.CFGParser.set("service","c22293_port", str(listener_port))
+        self.writeCFG()
+
+    def getC22293ListenerPort(self):
+        value = self.CFGParser.get("service", "c22293_port")
+        value_int = int(value)
+        return value_int
+
+    def setSDKServer(self, onOff):
+        self.CFGParser.set("service","onOff", onOff)
+        self.writeCFG()
+
+    def getSDKServer(self):
+        return self.CFGParser.get("service", "onOff")
+
+    def setSDKListenerPort(self, listener_port):
+        self.CFGParser.set("service", "sdk_port", listener_port)
+        self.writeCFG()
+
+    def getSDKListenerPort(self):
+        value = self.CFGParser.getint("service", "sdk_port")
+        return value
+
+    def setTG39ListenerPort(self, listener_port):
+        self.CFGParser.set("service", "tg39_port", str(listener_port))
+        self.writeCFG()
+
+    def getTG39ListenerPort(self):
+        value = self.CFGParser.get("service", "tg39_port")
+        value_int = int(value)
+        return value_int
+
+    def setUB530ListenerPort(self, listener_port):
+        self.CFGParser.set("service", "ub530_port", str(listener_port))
+        self.writeCFG()
+
+    def getUB530ListenerPort(self):
+        value = self.CFGParser.get("service", "ub530_port")
+        value_int = int(value)
+        return value_int
+
+    def setRedRatHubPort(self, listener_port):
+        self.CFGParser.set("service", "redrathub_port", listener_port)
+        self.writeCFG()
+
+    def getRedRatHubPort(self):
+		try:
+			return self.CFGParser.getint("service","redrathub_port")
+		except:
+			return 40000
+
+    def getRedRatHubSignalXml(self):
+        try:
+            return self.CFGParser.get("service","redrathub_signal")
+        except:
+            return None
+    
+    def setRedRatHubSignalXml(self, signal_xml):
+        self.CFGParser.set("service", "redrathub_signal", signal_xml)
+        self.writeCFG()
+
+def testCFG():
+    print "testCFG:"
+    serviceCFG = ServiceConfig()
+
+if __name__ == "__main__":
+    thread.start_new_thread(testCFG,())
+    thread.start_new_thread(testCFG,())
+    thread.start_new_thread(testCFG,())
+
+    time.sleep(3)

+ 249 - 0
ssat_sdk/service/service_manager.py

@@ -0,0 +1,249 @@
+# -*- coding:utf-8 -*-
+import os
+import sys
+import time
+from multiprocessing import Process
+import thread
+from multiprocessing.connection import Listener, Client
+
+from ssat_sdk.core.ibinder_listener import *
+from ccard_service import *
+from tg39_service import *
+from chroma_22293_service import *
+from redrat_service import *
+from sound_service import *
+from TC_UB530_service import *
+from ssat_sdk.utils import string_util
+import subprocess
+
+serviceConfig = ServiceConfig()
+
+
+class ServiceManager():
+    def __init__(self):
+        self.ccardService = None
+        self.tg39Service = None
+        self.c22293Service = None
+        self.redratService = None
+        self.soundService = None
+        self.ub530Service = None
+        self.errMsg = ""
+
+    def initService(self, service_name):
+        print u"启动Service:", service_name
+        if CCardService.Service_Name == service_name:
+            self.ccardService = CCardService()
+            staus, msg = self.ccardService.initCCard()
+            if staus == 0:
+                self.errMsg = msg
+        elif TG39Service.Service_Name == service_name:
+            self.tg39Service = TG39Service()
+            staus, msg = self.tg39Service.initTG39()
+            if staus == 0:
+                self.errMsg = msg
+        elif C22293Service.Service_Name == service_name:
+            self.c22293Service = C22293Service()
+            staus, msg = self.c22293Service.initC22293()
+            if staus == 0:
+                self.errMsg = msg
+        elif RedRatService.Service_Name == service_name:
+            self.redratService = RedRatService()
+            staus, msg = self.redratService.initRedRat()
+            if staus == 0:
+                self.errMsg = msg
+        elif SoundCollectionService.Service_Name == service_name:
+            self.soundService = SoundCollectionService()
+            staus, msg = self.soundService.initSoundCollection()
+            if staus == 0:
+                self.errMsg = msg
+        elif string_util.strcmp(TC_UB530Service.Service_Name, service_name):
+            baseDir = os.path.dirname(__file__)
+            exe = os.path.join(baseDir, "TC_UB530_service.py")
+            cmd = "python " + exe + " " + str(os.getpid())
+            self.ub530Service = subprocess.Popen(cmd)
+            # self.ub530Service = TC_UB530Service()
+            # self.ub530Service.startService()
+
+        print u"退出Service:", service_name
+
+    def startSDKServer(self):
+        print u"启动SDK服务"
+        self.onOff = True
+        self.service_process = Process(target=self.startBGServer, args=())
+        self.service_process.start()
+
+    def startSDKServer2(self):
+        print u"启动SDK服务"
+        self.onOff = True
+        self.startServerTerminal()
+
+    def startServerTerminal(self):
+        print u"启动Server控制台"
+        inline = ""
+        while inline <> "exit":
+            print u"输入命令:"
+            inline = raw_input()
+            if inline.__len__() < 1:
+                continue
+            self.parseCmd(inline)
+
+    def startBGServer(self):
+        print u"已启动后台服务进程"
+        # 采用listener监听服务指令
+        while (True):
+            binder = ListenerBinder(port=30500)
+            self.listener, port = binder.createListener()
+            serviceConfig.setSDKListenerPort(port)
+            conn = self.listener.accept()
+            cmd = conn.recv()
+            self.parseCmd(cmdLine=cmd)
+            # print u"服务指令:",cmd
+            conn.send(u"指令完成")
+
+    def parseCmd(self, cmdLine):
+        print u"SDK服务接受指令:", cmdLine
+        cmdParam = self.rmNULFrom(cmdLine.split(" "))
+        if (cmdParam.__len__() < 2):
+            print u"请输入正确命令"
+            return False
+        command = cmdParam[0]
+        param = cmdParam[1]
+        self.excuteCmd(command, param)
+
+    def excuteCmd(self, command, param):
+        if (command == "start"):
+            self.startServiceThread(param)
+
+    def startServiceThread(self, service_name):
+        if (service_name == CCardService.Service_Name):
+            if (self.ccardService == None) or (self.ccardService.getStatus() == 0):
+                thread.start_new_thread(
+                    self.initService, (CCardService.Service_Name,))
+            else:
+                print u"视频采集卡已经启动"
+
+        elif (service_name == TG39Service.Service_Name):
+            if self.tg39Service == None or (self.tg39Service.status == 0):
+                thread.start_new_thread(
+                    self.initService, (TG39Service.Service_Name,))
+            else:
+                print u"TG39已经启动"
+
+        elif (service_name == C22293Service.Service_Name):
+            if self.c22293Service == None or (self.c22293Service.status == 0):
+                thread.start_new_thread(
+                    self.initService, (C22293Service.Service_Name,))
+            else:
+                print u"c22293已经启动"
+        elif (service_name == "all"):
+            if self.ccardService == None or (self.ccardService.status == 0):
+                thread.start_new_thread(
+                    self.initService, (CCardService.Service_Name,))
+            else:
+                print u"视频采集卡已经启动"
+            if self.tg39Service == None or (self.tg39Service.status == 0):
+                thread.start_new_thread(
+                    self.initService, (TG39Service.Service_Name,))
+            else:
+                print u"TG39已经启动"
+        elif (service_name == RedRatService.Service_Name):
+            if self.redratService == None or (self.redratService.status == 0):
+                thread.start_new_thread(
+                    self.initService, (RedRatService.Service_Name,))
+            else:
+                print u"红老鼠已经启动"
+        elif (service_name == SoundCollectionService.Service_Name):
+            if self.soundService == None or (self.soundService.status == 0):
+                thread.start_new_thread(
+                    self.initService, (SoundCollectionService.Service_Name,))
+            else:
+                print u"声音采集器已经启动"
+        elif (service_name == TC_UB530Service.Service_Name):
+            # self.initService(TC_UB530Service.Service_Name)
+            if self.ub530Service is None:
+                thread.start_new_thread(
+                    self.initService, (TC_UB530Service.Service_Name,))
+            else:
+                print u"UB530视频采集器已经启动"
+
+    '''
+    return:0代表设备服务不可用,1代表设备服务正常运行;-1 代表没有此类设备。
+    '''
+
+    def getServiceStatus(self, service_name):
+        if TG39Service.Service_Name == service_name:
+            if self.tg39Service == None or (self.tg39Service.status == 0):
+                return 0
+            else:
+                return 1
+        elif CCardService.Service_Name == service_name:
+            if self.ccardService == None or self.ccardService.status == 0:
+                return 0
+            else:
+                return 1
+        elif C22293Service.Service_Name == service_name:
+            if self.c22293Service == None or self.c22293Service.status == 0:
+                return 0
+            else:
+                return 1
+        elif RedRatService.Service_Name == service_name:
+            if self.redratService == None or self.redratService.status == 0:
+                return 0
+            else:
+                return 1
+        elif (service_name == TC_UB530Service.Service_Name):
+            # self.initService(TC_UB530Service.Service_Name)
+            if self.ub530Service is None and self.ub530Service.poll() is None:
+                return 0
+            else:
+                return 1
+        return -1
+
+    def rmNULFrom(self, strArr):
+        destArr = []
+        for i in range(strArr.__len__()):
+            item = strArr[i]
+            if (item <> ''):
+                destArr.append(item)
+        return destArr
+
+
+class ServiceClient():
+    def __init__(self):
+        print u"初始化Server控制台"
+        self.port = serviceConfig.getSDKListenerPort()
+
+    def startTerminal(self):
+        print u"启动Server控制台"
+        inline = ""
+        while inline <> "exit":
+            print u"输入命令:"
+            inline = raw_input()
+            if inline.__len__() < 1:
+                continue
+            self.executeCommand(inline)
+
+    def executeCommand(self, inline):
+        # print u"执行命令:", inline
+        conn = Client(("localhost", self.port), authkey="sat")
+        conn.send(inline)
+        result = conn.recv()
+        print u"执行结果:", result
+        conn.close()
+
+
+if __name__ == "__main__":
+    # if (sys.argv.__len__() > 1) and ("server" == sys.argv[1]):
+    #     sManager = ServiceManager()
+    #     sManager.startSDKServer()
+    # elif (sys.argv.__len__() > 1) and ("client" == sys.argv[1]):
+    #     sClient = ServiceClient()
+    #     sClient.startTerminal()
+    # else:
+    #     sManager = ServiceManager()
+    #     sManager.startSDKServer2()
+    sManager = ServiceManager()
+    sManager.startServiceThread("ub530")
+
+    while 1:
+        pass

+ 148 - 0
ssat_sdk/service/sound_service.py

@@ -0,0 +1,148 @@
+# -*- coding:utf-8 -*-
+import os
+import sys
+import time
+# from device_manager import *
+from ssat_sdk.utils import LoggingUtil
+from ssat_sdk.device_manage import AudioCapture
+
+
+class SoundCollectionService():
+    # 设备是否已打开,成功为0;
+    IsOpen = -1
+    Service_Name = "sound_collection"
+
+    # 初始化
+    def __init__(self):
+        self.status = 0
+
+    # 初始化
+    def initSoundCollection(self):
+        if SoundCollectionService.IsOpen <> 0:
+            SoundCollectionService.IsOpen = AudioCapture.Initialize()
+
+        if SoundCollectionService.IsOpen <> 0:
+            LoggingUtil.printLog(u"打开声音采集器失败")
+            self.status = 0
+            # return 0,u"打开声音采集器失败"
+        else:
+            LoggingUtil.printLog(u"打开声音采集器成功")
+            self.status = 1
+            # return 1,""
+
+    # 是否静音(开始通道、采集时长、静音电压)
+    def IsMute(self, collectionTime, muteVoltage):
+        if SoundCollectionService.IsOpen <> 0:
+            SoundCollectionService.IsOpen = AudioCapture.Initialize()
+
+        percentMute = 0.0
+        if SoundCollectionService.IsOpen <> 0:
+            LoggingUtil.printLog(u"声音采集器未打开")
+        else:
+            percentMute = AudioCapture.IsMute(
+                0, collectionTime, muteVoltage)
+        # 返回静音百分比;
+        return percentMute
+
+    # 检测声音是否有中断;
+    def IsInterrupt(self, collectionTime, muteVoltage, interruptTime):
+        if SoundCollectionService.IsOpen <> 0:
+            SoundCollectionService.IsOpen = AudioCapture.Initialize()
+
+        isInterrupt = False
+        if SoundCollectionService.IsOpen <> 0:
+            LoggingUtil.printLog(u"声音采集器未打开")
+        else:
+            isInterrupt = AudioCapture.IsInterrupt(
+                0, collectionTime, muteVoltage, interruptTime)
+        # 返回静音百分比;
+        return isInterrupt
+
+    # 检测左右声道是否正常;
+    def SoundTrackDetection(self, collectionTime, muteVoltage, interruptTime):
+        if SoundCollectionService.IsOpen <> 0:
+            SoundCollectionService.IsOpen = AudioCapture.Initialize()
+
+        nResult = 0
+        if SoundCollectionService.IsOpen <> 0:
+            LoggingUtil.printLog(u"声音采集器未打开")
+        else:
+            nResult = AudioCapture.SoundTrackDetection(
+                0, collectionTime, muteVoltage, interruptTime)
+        # 返回静音百分比;
+        return nResult
+
+    # 退出初始化环境
+    def __def__(self):
+        if SoundCollectionService.IsOpen == 0:
+            SoundCollectionService.IsOpen = AudioCapture.ExitInitialize()
+
+
+# 测试用例
+if __name__ == "__main__":
+    # 原接口调用测试
+    if 0:
+        result = AudioCapture.Initialize()
+        if result == 0:
+            print u"初始化成功"
+        else:
+            print u"初始化失败"
+
+        if result == 0:
+            print u"\n#是否静音->"
+            time.sleep(1)
+            percentMute = AudioCapture.IsMute( 0, 8000, 1.0)
+            print u"静音百分比:", percentMute
+
+            time.sleep(2)
+            print u"\n#是否有停顿->"
+            time.sleep(1)
+            bInterrupt = AudioCapture.IsInterrupt(0, 8000, 1.0, 400)
+            if bInterrupt == True:
+                print u"有停顿"
+            else:
+                print u"无停顿"
+
+    # 类接口调用;
+    if 1:
+        soundcollection = SoundCollectionService()
+        soundcollection.initSoundCollection()
+        if SoundCollectionService.IsOpen == 0:
+            # 静音测试
+            if 1:
+                print u"\n#是否静音->"
+                time.sleep(1)
+                percentMute = soundcollection.IsMute( 8000, 1.0)
+                print u"静音百分比:", percentMute
+
+            # 停顿测试
+            if 1:
+                time.sleep(2)
+                print u"\n#是否有停顿->"
+                time.sleep(1)
+                bInterrupt = soundcollection.IsInterrupt( 5000, 1.0, 400)
+                if bInterrupt == True:
+                    print u"有停顿"
+                else:
+                    print u"无停顿"
+
+            # 声道检测
+            if 1:
+                print u"\n#声道检测"
+                nRet = soundcollection.SoundTrackDetection( 8000, 1.0, 400)
+                if nRet == 0:
+                    print u"声道正常"
+                elif nRet == 1:
+                    print u"采集失败"
+                elif nRet == -1:
+                    print u"左右声道完全无音"
+                elif nRet == -2:
+                    print u"左声道无音"
+                elif nRet == -3:
+                    print u"右声道无音"
+                elif nRet == -4:
+                    print u"左声道停顿"
+                elif nRet == -5:
+                    print u"右声道停顿"
+                elif nRet == -6:
+                    print u"左右声道都停顿"

+ 72 - 0
ssat_sdk/service/tg39_service.py

@@ -0,0 +1,72 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+from TST.CaptureCard import *
+from ssat_sdk.core.ibinder_listener import *
+from ssat_sdk.service.service_config import *
+from ssat_sdk.utils.serial_TG39BX1 import *
+from ssat_sdk.sat_environment import *
+from ssat_sdk.device_manage.Model_3116A import C3316A
+from ssat_sdk.utils import string_util
+
+class TG39Service():
+    Service_Name = "tg39"
+    # 当前选择的ATV码流仪;
+    ATV_SELECTIVE = getATVSelected()
+    def __init__(self):
+        self.serviceConfig = ServiceConfig()
+        self.status = 0
+
+    def selectiveATV(self):
+        port = getTG39_Port()
+        if string_util.strcmp(TG39Service.ATV_SELECTIVE,'TG39'):
+            self.tg39ser = Serial_TG39BX1(port)
+            try:
+                self.tg39ser.open()
+            except Exception, e:
+                print u"TG初始化失败", e
+                self.status = 0
+                return 0, e.message
+        elif string_util.strcmp(TG39Service.ATV_SELECTIVE,'3116'):
+            self.atv3116 = C3316A(port)
+            if self.atv3116.Open() == False:
+                print u"TG初始化失败", e
+                self.status = 0
+                return 0, '打开串口失败'
+        
+        return 1,'初始成功'
+    
+    def dealCommand(self, cmdLine):
+        if string_util.strcmp(TG39Service.ATV_SELECTIVE,'TG39'):
+            result = self.tg39ser.execmd(cmdLine)
+        elif string_util.strcmp(TG39Service.ATV_SELECTIVE,'3116'):
+            result = self.atv3116.sendCmds(cmdLine)  
+        # 返回结果;
+        return result 
+
+    def initTG39(self):
+        print u"初始化TG39设备"
+        status,message = self.selectiveATV()
+        if status == 0:
+            return status,message
+        ibinder = ListenerBinder(port=30100)
+        self.listener, port = ibinder.createListener()
+        self.serviceConfig.setTG39ListenerPort(port)
+        print u"TG39启动成功,TG39 Port:", port
+        self.status = 1
+        while (True):
+            try:
+                conn = self.listener.accept()
+                cmdLine = conn.recv()
+            except Exception, e:
+                print u"TG39接收指令失败", e
+                continue
+            print u"TG39接收指令:", cmdLine
+            result = self.dealCommand(cmdLine)
+            try:
+                conn.send(result)
+            except Exception, e:
+                print e
+            
+
+    def getStatus(self):
+        return self.status

+ 2 - 0
ssat_sdk/sound/__init__.py

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

+ 469 - 0
ssat_sdk/sound/audio_analysis.py

@@ -0,0 +1,469 @@
+# -*- coding:utf-8 -*-
+import os, sys, time
+from audio_recorder import READ_BUF_TIME,ARecorder
+from ssat_sdk.sat_environment import getSoundList
+import numpy as np
+import matplotlib.pyplot as plt
+import wave
+import thread
+import Queue
+
+
+def getSoundLevel():
+    hasSoundLevel, noSoundLevel = getSoundList()
+    return round(hasSoundLevel, 2)
+
+TH_POWER = getSoundLevel() #平均短时能有无声的阈值
+TH_PLOSIVE_TIME = 0.1 #短于0.1秒的声音,判断为爆破音,大于0.1秒,判断为正常声音
+TH_BLOCK_TIME = 0.1#声音间断间隔时间,单位s
+
+class AAnalysis():
+
+    def __init__(self):
+        self.varList = [] #记录各声道有无声变化
+        self.nChannels = 2
+        self.initStatus()
+
+    def initStatus(self):
+        self.power = 0
+        self.hasSound = False
+        self.hasBlock = False
+        self.hasPlosive = False
+        self.soundPowerCHS = []
+        self.allPowerCHS = []
+        self.fftPowerCHS = []
+
+    def showLanguage(self, time=5, waveFile=None):
+        if waveFile is None:
+            recorder = ARecorder()
+            waveFile = "test.wav"
+            recorder.recordWave(waveFile,time)
+        # 调用wave模块中的open函数,打开语音文件。
+        f = wave.open(waveFile, 'rb')
+
+        # 得到语音参数
+        params = f.getparams()
+        nchannels, sampwidth, framerate, nframes = params[:4]
+
+        # 得到的数据是字符串,需要将其转成int型
+        strData = f.readframes(nframes)
+        wavaData = np.fromstring(strData, dtype=np.int16)
+
+        # 归一化
+        wavaData = wavaData * 1.0 / max(abs(wavaData))
+
+        # .T 表示转置
+        wavaData = np.reshape(wavaData, [nframes, nchannels]).T
+        f.close()
+
+        # 绘制频谱
+        plt.specgram(wavaData[0], Fs=framerate, scale_by_freq=True, sides='default')
+        plt.ylabel('Frequency')
+        plt.xlabel('Time(s)')
+        plt.show()
+
+    def showFregForm(self,waveFile):
+        wf = wave.open(waveFile, "rb")
+        nframes = wf.getnframes()
+        framerate = wf.getframerate()
+        frame_data = wf.readframes(nframes)
+        wf.close()
+        time_data = np.fromstring(frame_data, dtype=np.int16)
+        time_data.shape = -1, 2
+        time_dataT = time_data.T
+        freq = [n for n in range(0, framerate)]
+        start = 0
+        end = framerate
+        time_dataT2 = time_dataT[0][start:start + end]
+        # self.time2Frequence(time_dataT2[2000:2000+1000])
+        c = self.time2Frequence(time_dataT2, 1)
+        print "showFregForm:c,", c.shape, np.max(np.abs(c)), np.average(np.abs(c))
+        for i in range(1,5):
+            start = framerate*i
+            end = framerate*(i+1)
+            print "showFregForm:start,end:",start,end
+            time_dataT2 = time_dataT[0][start:end]
+            c = self.time2Frequence(time_dataT2, 1)
+            print "showFregForm:c,", c.shape, np.max(np.abs(c)), np.average(np.abs(c)),time_dataT2.shape
+        plt.plot(freq[:], abs(c[:]), 'r')
+        plt.show()
+
+    def showWaveForm(self, waveFile):
+        # -*- coding: utf-8 -*-
+        # 打开WAV文档
+        f = wave.open(waveFile, "rb")
+        # 读取格式信息
+        # (nchannels, sampwidth, framerate, nframes, comptype, compname)
+        params = f.getparams()
+        nchannels, sampwidth, framerate, nframes = params[:4]
+        # 读取波形数据
+        print "nchannels, sampwidth, framerate, nframes:",nchannels, sampwidth, framerate, nframes
+        str_data = f.readframes(nframes)
+        f.close()
+        # 将波形数据转换为数组
+        print "str_data:",str_data.__len__()
+        wave_data = np.fromstring(str_data, dtype=np.int16)
+        print "wave_data.shape:",wave_data.shape
+        # 声道处理
+        wave_data.shape = -1, nchannels
+        wave_data = wave_data.T
+        start = 0
+        per_frames = int(framerate * READ_BUF_TIME)
+        chunks = int(nframes/framerate/READ_BUF_TIME)
+        for i in range(2,chunks):
+            start = per_frames*i
+            end = per_frames * (i + 1)
+            self.getFrameAVGPower(wave_data[0][start:end])
+            self.getFrameFFTPower(wave_data[0][start:end],READ_BUF_TIME)
+            # self.STE(wave_data[0][start:end])
+            # self.ZCR(wave_data[0][start:end])
+        self.STE(wave_data[0])
+        # 声道处理 End
+        print "channel 0:",wave_data[0].shape, len(wave_data[0])
+        print "channel 1:", wave_data[1].shape, len(wave_data[1])
+        # time = np.arange(0, nframes) * (1.0 / framerate)
+        time = np.arange(0, nframes) * (1.0 / framerate)
+        print "time:", time.shape
+        # 绘制波形
+        plt.subplot(211)
+        plt.plot(time, wave_data[0], "b")
+
+        plt.subplot(212)
+        plt.plot(time, wave_data[1], c="g")
+        plt.xlabel("time (seconds)")
+        plt.ylabel("power (hz)")
+        plt.show()
+
+    '''
+    计算帧短时能
+    '''
+    def STE(self, frameL):
+        amp = np.sum(np.abs(frameL))
+        print "STE amp:",amp
+        return amp
+
+    '''
+    将列表,转换成numpy的数组。
+    '''
+    def list2NP(self, srcList):
+        if srcList.__len__() > 0:
+            return np.array(srcList)
+        else:
+            return np.array([-1])
+
+    '''
+    计算有声的平均强度,即帧的强度超过有声阈值,才被统计
+    '''
+    def getSoundAVGPower(self, LR=False):
+        # print "getSoundAVGPower,self.soundPowerCHS:",self.soundPowerCHS
+        if LR is False:
+            if self.soundPowerCHS.__len__() < 1:
+                return 0
+            soundPowersL = self.list2NP(self.soundPowerCHS[0])
+            if self.soundPowerCHS.__len__() == 2:
+                soundPowersR = self.list2NP(self.soundPowerCHS[1])
+                soundPowers = np.where(soundPowersL > soundPowersR, soundPowersL, soundPowersR)
+                return np.average(soundPowers)
+            return np.average(soundPowersL)
+        else:
+            if self.soundPowerCHS.__len__() < 1:
+                return 0,0
+            soundPowersL = self.list2NP(self.soundPowerCHS[0])
+            soundPowersR = self.list2NP(self.soundPowerCHS[1])
+            return np.average(soundPowersL),np.average(soundPowersR)
+
+    '''
+    :return result,valueList。
+         result:-1代表声音检测异常;1表示单声道;2 表示双声道
+         valueList:-1代表没有声音,0代表仅左声道,1代表仅右声道,2代表左右声道有声
+    '''
+    def getLRVariation(self):
+        varList = []
+        if self.soundPowerCHS.__len__() < 1:
+            return -1, self.varList
+        elif self.soundPowerCHS.__len__() == 1:
+            return 1, self.varList
+        else:
+            return 2, self.varList
+
+    varLRCount = 0
+    def recLRVariation(self, channelPowers):
+        ret = self.calLRVar(channelPowers)
+        if self.varList.__len__() == 0:
+            self.varList.append(ret)
+        else:
+            if self.varList[self.varList.__len__()-1] <> ret:
+                self.varLRCount += 1
+                if self.varLRCount * self.frameTime > 0.5:#大于0.5秒钟声音,才算入变化
+                    self.varList.append(ret)
+            else:
+                self.varLRCount = 0
+
+    def calLRVar(self, channelPowers):
+        if channelPowers.__len__() == 1:
+            #单声道,左声道有声,或者无声
+            return 0 if channelPowers[0] >= TH_POWER else -1
+        else:
+            if channelPowers[0] >= TH_POWER and channelPowers[1] >= TH_POWER:
+                return 2
+            elif channelPowers[0] >= TH_POWER and channelPowers[1] < TH_POWER:
+                return 0
+            elif channelPowers[0] < TH_POWER and channelPowers[1] >= TH_POWER:
+                return 1
+            else:
+                return -1
+
+
+
+    '''
+    计算整个录音的声音平均强度,实际意义不太大,除非是固定幅度音频检测。
+    '''
+    def getTotalAVGPower(self, LR=False):
+        if LR is False:
+            if self.allPowerCHS.__len__() < 1:
+                return 0
+            soundPowersL = self.list2NP(self.allPowerCHS[0])
+            if self.allPowerCHS.__len__() == 2:
+                soundPowersR = self.list2NP(self.allPowerCHS[1])
+                soundPowers = np.where(soundPowersL > soundPowersR, soundPowersL, soundPowersR)
+                return np.average(soundPowers)
+            else:
+                return np.average(soundPowersL)
+        else:
+            if self.allPowerCHS.__len__() < 1:
+                return 0,0
+            soundPowersL = self.list2NP(self.allPowerCHS[0])
+            soundPowersR = self.list2NP(self.allPowerCHS[1])
+            return np.average(soundPowersL),np.average(soundPowersR)
+    '''
+    计算帧的平均声音强度
+    '''
+    def getFrameAVGPower(self, frameL):
+        avgPower = np.average(np.abs(frameL))
+        # print "getFrameAVGPower:",avgPower
+        return avgPower
+
+    '''
+        计算帧的平均声音强度
+        '''
+
+    def getFrameMaxPower(self, frameL):
+        maxPower = np.max(np.abs(frameL))
+        # print "getFrameMaxPower:",maxPower
+        return maxPower
+
+    '''
+    根据傅里叶变化,计算声音强度
+    '''
+    def getFrameFFTPower(self, frameL, timeL):
+        fftFreq = self.time2Frequence(frameL, timeL)
+        fftPower = np.max(np.abs(fftFreq))
+        # print "getFrameFFTPower:",fftPower
+        return fftPower
+
+    '''
+    计算整个录音过程的fft转换后得到的声音强度
+    '''
+    def getFFTPower(self, LR=False):
+        if LR is False:
+            if self.fftPowerCHS.__len__() < 1:
+                return 0
+            soundPowersL = self.list2NP(self.fftPowerCHS[0])
+            if self.fftPowerCHS.__len__() == 2:
+                soundPowersR = self.list2NP(self.fftPowerCHS[1])
+                soundPowers = np.where(soundPowersL > soundPowersR, soundPowersL, soundPowersR)
+                return np.average(soundPowers)
+            return np.average(soundPowersL)
+        else:
+            if self.fftPowerCHS.__len__() < 1:
+                return 0,0
+            soundPowersL = self.list2NP(self.fftPowerCHS[0])
+            soundPowersR = self.list2NP(self.fftPowerCHS[1])
+            return np.average(soundPowersL),np.average(soundPowersR)
+    '''
+    计算帧过零率
+    '''
+    def ZCR(self,curFrame):
+        # 过零率
+        tmp1 = curFrame[:-1] #
+        tmp2 = curFrame[1:]
+        sings = tmp1 * tmp2 <= 0 #帧左右错位1,如果相邻2个数一正一负,则相乘后小于<0,表示1次过零。
+        zcr = float(np.sum(sings)) / len(sings)
+        print "ZCR:",zcr
+        return zcr
+
+    '''
+    将声音时域数据,转换成频谱数据。
+    注意:如果帧时长,不是1秒,无法建立频谱图。
+    '''
+    def time2Frequence(self, frameL, timeL=1.0):
+        fftFreq = np.fft.fft(frameL) * 2 / len(frameL)
+        # freq = np.arange(0,len(frameL),1)
+        # plt.plot(freq[:], abs(fftFreq[:]), 'r')
+        # plt.show()
+        return fftFreq
+
+    '''
+    展现一帧音频的时域波形
+    '''
+    def showFrameTime(self, frameL, timeL, width):
+        print "showFrameTime,frameL:", frameL.shape, np.ndim(frameL)
+        ndim = np.ndim(frameL)
+        if ndim == 1:
+            frameCount = len(frameL)
+            time = np.arange(0, frameCount) * (timeL / frameCount)
+            print "showFrameTime,time:",time.shape
+            plt.plot(time, frameL, "g")
+            plt.xlabel("time (seconds)")
+            plt.ylabel("power (hz)")
+            plt.show()
+        elif ndim == 2:
+            rows, cols = frameL.shape
+            time = np.arange(0, cols) * (timeL / cols)
+            for i in range(rows):
+                plt.subplot(rows,1,i+1)
+                plt.plot(time, frameL[i])
+            plt.xlabel("time (seconds)")
+            plt.ylabel("power (hz)")
+            plt.show()
+
+
+
+    def getNPDtype(self, byteNum):
+        if byteNum == 1:
+            return np.int8
+        elif byteNum == 2:
+            return np.int16
+        else:
+            return np.int16
+
+    '''
+    用于音频录制过程中的帧分析。分析结束后,可以使用get类型接口,获取结果,例如:getSoundAVGPower
+    分析fft声音频谱时,timeL=1秒才有意义,才能建立频谱图。
+    声音有无判断:双声道,只要一个声道有声,判断为有声。
+    '''
+    def anaysisFrames(self, frameQue, channels, width, frameTime):
+        self.frameTime = frameTime
+        self.nChannels = channels
+        self.varList = []
+        soundCount = 0
+        noSoundCount = 0
+        self.soundPowerCHS = []
+        self.allPowerCHS = []
+        self.fftPowerCHS = []
+        for i in range(channels):
+            self.soundPowerCHS.append([])
+            self.allPowerCHS.append([])
+            self.fftPowerCHS.append([])
+        while self.isAF is True or frameQue.qsize() > 0:
+            frames = frameQue.get()
+            dtype = self.getNPDtype(width)
+            # print "anaysisFrames,dtype size:", len(frames), channels, dtype
+            frameL = np.fromstring(frames, dtype=dtype)
+            frameL.shape = -1,channels
+            frameL = frameL.T
+            channelPowers = []
+            for i in range(channels):
+                avgPower = self.getFrameMaxPower(frameL[i])
+                self.allPowerCHS[i].append(avgPower)
+                fftPower = self.getFrameFFTPower(frameL[i], frameTime)
+                self.fftPowerCHS[i].append(fftPower)
+                channelPowers.append(avgPower)
+                #每一帧各通道数值计算完毕,开始进行帧的有无声判断
+                if i == channels - 1:
+                    #获取当前帧的各通道最强声音强度。
+                    framePower = self.getCHMaxPower(channelPowers)
+                    #左右声道有无声的记录
+                    self.recLRVariation(channelPowers)
+                    del channelPowers
+                    if framePower >= TH_POWER:
+                        noSoundCount = 0
+                        soundCount += 1
+                        if soundCount * frameTime >= TH_PLOSIVE_TIME:
+                            self.hasSound = True
+                    else:
+                        # 爆破音检测。还缺失:有声音的情况下,爆破音检测。
+                        if soundCount * frameTime < TH_PLOSIVE_TIME and soundCount > 0:
+                            self.hasPlosive = True
+                        noSoundCount += 1
+                        soundCount = 0
+                        if noSoundCount * frameTime >= TH_BLOCK_TIME:
+                            self.hasBlock = True
+                #左右声道分开记录有声的帧的声音强度
+                if avgPower >= TH_POWER:
+                    self.soundPowerCHS[i].append(avgPower)
+
+    '''
+    返回当前帧的各通道的最大音量
+    '''
+    def getCHMaxPower(self, channelPowers):
+        channelPowers_np = self.list2NP(channelPowers)
+        return np.max(channelPowers_np)
+
+
+
+    '''开启音频后台分析线程'''
+    def startFramesAnalysis(self, frameQue, channels, width, buf_time):
+        self.initStatus()
+        self.isAF = True
+        thread.start_new_thread(self.anaysisFrames, (frameQue,channels,width, buf_time))
+    '''关闭音频后台分析,需要数据全部处理完后,线程才会停止'''
+    def endFramesAnalysis(self):
+        self.isAF = False
+
+    def getWavReader(self, waveFile):
+        wfReader = None
+        try:
+            wfReader = wave.open(waveFile,"rb")
+        except Exception as e:
+            print "Wave不存在",e
+        return wfReader
+
+    '''
+    分析wav音频文件后,可以使用get类型接口,获取结果,例如:getTotalAVGPower
+    分析fft声音频谱时,timeL=1秒才有意义,才能建立频谱图。
+    '''
+    def analyWav(self, waveFile, buf_time=READ_BUF_TIME):
+        self.frameTime = buf_time
+        self.initStatus()
+        frameQue = Queue.Queue()
+        wfReader = self.getWavReader(waveFile)
+        if wfReader is None:
+            return
+        nChannels, width, frameRate, nframes = wfReader.getparams()[0:4]
+        stepnFrames = int(frameRate*buf_time)
+        times = int(nframes/stepnFrames)
+        print "analyWav:",buf_time,stepnFrames,times
+        for i in range(times):
+            frameL = wfReader.readframes(stepnFrames)
+            frameQue.put_nowait(frameL)
+        # self.startFramesAnalysis(frameQue, nChannels, width,timeL)
+        # self.endFramesAnalysis()
+        self.isAF = False
+        self.anaysisFrames(frameQue, nChannels, width,buf_time)
+        wfReader.close()
+
+if __name__ == "__main__":
+    analysis = AAnalysis()
+    # waveFile = "test1.wav"
+    # waveFile = "eq_10khz_v0.wav"
+    # waveFile = "tv/DVB_DTV_automatic_search.wav"
+    waveFile = "wav_balance_v15.wav"
+    # waveFile = r"D:\sound\sound_preset\sound_preset_mode_music.wav"
+    # waveFile = r"D:\sound\5k\eq_5khz_v100.wav"
+    # waveFile = r"D:\sound\monitorSound_balance.wav"
+
+    # analysis.showLanguage(waveFile=waveFile)
+    # analysis.showFregForm(waveFile)
+    # analysis.showWaveForm(waveFile)
+
+    analysis.analyWav(waveFile)
+    print "sound,status hasSound,hasBlock,hasPlosive:",analysis.hasSound,analysis.hasBlock,analysis.hasPlosive
+    print "0,getSoundAVGPower:", analysis.getSoundAVGPower(LR=True)
+    print "0,getTotalAVGPower:", analysis.getTotalAVGPower(LR=True)
+    print "0,getFFTPower:",analysis.getFFTPower(LR=True)
+    #
+    # waveFile = "eq_10khz_v100.wav"
+    # analysis.analyWav(waveFile)
+    # print "100,avgPower:", analysis.getSoundAVGPower()
+    # print "100,fftPower:",analysis.getFFTPower()

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác