From 499ce682f836b481ccf0f2af987397a7f19f21cd Mon Sep 17 00:00:00 2001 From: Ivan Maslov Date: Sun, 9 Jun 2019 17:05:10 +0300 Subject: [PATCH] =?UTF-8?q?#####=D0=92=D0=BD=D0=B8=D0=BC=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5#####UIOSelectorsSecs=5FWaitAppear=5FList=5F=D0=A4=D1=83?= =?UTF-8?q?=D0=BD=D0=BA=D1=86=D0=B8=D1=8F=20=D0=BE=D0=B6=D0=B8=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE=D1=8F=D0=B2=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D1=8D=D0=BB=D0=B5=D0=BC=D0=B5=D0=BD=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=20(=D1=82=D0=BA=20=D1=8D=D0=BB=D0=B5=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D1=8B=20=D0=BC=D0=BE=D0=B3=D1=83=D1=82=20=D0=B1=D1=8B?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BD=D0=B5=D0=B4=D0=BE=D1=81=D1=82=D1=83=D0=BF?= =?UTF-8?q?=D0=BD=D1=8B,=20=D0=BD=D0=B5=D0=B8=D0=B7=D0=B2=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D0=BD=D0=BE=20=D0=B2=20=D0=BA=D0=B0=D0=BA=D0=BE=D0=BC=20?= =?UTF-8?q?=D1=84=D1=80=D0=B5=D0=B9=D0=BC=D0=B2=D0=BE=D1=80=D0=BA=D0=B5=20?= =?UTF-8?q?=D0=BA=D0=B0=D0=B6=D0=B4=D1=8B=D0=B9=20=D0=B8=D0=B7=20=D0=BD?= =?UTF-8?q?=D0=B8=D1=85=20=D0=BC=D0=BE=D0=B6=D0=B5=D1=82=20=D0=BF=D0=BE?= =?UTF-8?q?=D1=8F=D0=B2=D0=B8=D1=82=D1=8C=D1=81=D1=8F)=20#TODO=20#StudioTr?= =?UTF-8?q?yCatch=D0=9F=D1=80=D0=B8=D0=9E=D1=82=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BA=D0=B5=D0=9D=D0=B5=D0=BA=D0=BE=D1=80=D1=80=D0=B5=D0=BA?= =?UTF-8?q?=D1=82=D0=BD=D0=BE=D0=B9=D0=9F=D0=BE=D1=81=D0=BB=D0=B5=D0=B4?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B8=20=D1=81=D0=B8=D0=BC=D0=B2=D0=BE=D0=BB=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Robot/GUI.py | 75 +++++++++++++++++++++++------------------ Robot/JSONNormalize.py | 22 +++++++----- Robot/Robot.py | 5 ++- Studio/JSONNormalize.py | 2 ++ Studio/Studio.py | 43 +++++++++++++++-------- 5 files changed, 91 insertions(+), 56 deletions(-) diff --git a/Robot/GUI.py b/Robot/GUI.py index eb1c9cde..a8f92c3b 100644 --- a/Robot/GUI.py +++ b/Robot/GUI.py @@ -18,12 +18,15 @@ from threading import Timer import datetime import logging import re +import copy #Создать файл логирования # add filemode="w" to overwrite if not os.path.exists("Reports"): os.makedirs("Reports") logging.basicConfig(filename="Reports\ReportRobotGUIRun_"+datetime.datetime.now().strftime("%Y_%m_%d__%H_%M_%S")+".log", level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") +#####Внимание####### +#TODO В перспективе нужно реализовать алгоритм определения разрядности не в Robot.py, а в GUI.py, тк начинают появляться функции, на входе в которые еще неизвестна разрядность элемента + селектор может охватить сразу два элемента из 2-х разных разрядностей - обрабатываться это должно непосредственно при выполнении #################################### #Info: GUI module of the Robot app (OpenRPA - Robot) @@ -108,9 +111,10 @@ def UIOSelector_Get_UIOList (inSpecificationList,inElement=None,inFlagRaiseExcep if inElement is None: #сформировать спецификацию на получение элемента lRootElementSpecification=[inSpecificationList[0]] - lRootElement=PWASpecification_Get_UIO(lRootElementSpecification) - if lRootElement is not None: - lChildrenList.append(lRootElement.wrapper_object()) + lRootElementList=PWASpecification_Get_UIO(lRootElementSpecification) + for lRootItem in lRootElementList: + if lRootItem is not None: + lChildrenList.append(lRootItem.wrapper_object()) #Елемент на вход поступил - выполнить его анализ else: #Получить список элементов @@ -136,7 +140,6 @@ def UIOSelector_Get_UIOList (inSpecificationList,inElement=None,inFlagRaiseExcep if 'depth_start' in inSpecificationList[0]: if inSpecificationList[0]["depth_start"]>1: lFlagGoCheck=False - #pdb.set_trace() #Циклический обход по детям, на предмет соответствия всем условиям for lChildrenItem in lElementChildrenList: #Обработка глубины depth (рекурсивный вызов для всех детей с занижением индекса глубины) @@ -149,7 +152,6 @@ def UIOSelector_Get_UIOList (inSpecificationList,inElement=None,inFlagRaiseExcep lChildrenItemNewSpecificationList[0]["depth_end"]=lChildrenItemNewSpecificationList[0]["depth_end"]-1 if 'depth_start' in lChildrenItemNewSpecificationList[0]: lChildrenItemNewSpecificationList[0]["depth_start"]=lChildrenItemNewSpecificationList[0]["depth_start"]-1 - #pdb.set_trace() #Циклический вызов для всех детей со скорректированной спецификацией lResultList.extend(UIOSelector_Get_UIOList(lChildrenItemNewSpecificationList,lChildrenItem,inFlagRaiseException)) #Фильтрация @@ -246,6 +248,8 @@ def UIOSelector_Exist_Bool (inSpecificationList): #inFlagWaitAllInMoment - доп. условие - ожидать появление всех UIOSelector одновременно #return: [0,1,2] - index of UIOSpecification, which is appear #old name - - +#####Внимание##### +##Функция ожидания появления элементов (тк элементы могут быть недоступны, неизвестно в каком фреймворке каждый из них может появиться) def UIOSelectorsSecs_WaitAppear_List (inSpecificationListList,inWaitSecs,inFlagWaitAllInMoment=False): lResultFlag=False lSecsSleep = 1 #Настроечный параметр @@ -294,7 +298,6 @@ def UIOSelectorSecs_WaitAppear_Bool (inSpecificationList,inWaitSecs): #return None (if Process not found), int 32, or int 64 def UIOSelector_Get_BitnessInt (inSpecificationList): lResult=None - #pdb.set_trace() #Получить объект Application (Для проверки разрядности) lRootElement=PWASpecification_Get_PWAApplication(inSpecificationList) if lRootElement is not None: @@ -315,43 +318,54 @@ def Get_OSBitnessInt (): ################################################################################################## #inControlSpecificationArray - List of dict, dict in pywinauto.find_windows notation #Backend selection - attribute "backend" ("win32" || "uia") in 1-st list element -#return UIO object +#return list of UIO object #old name - GetControl def PWASpecification_Get_UIO(inControlSpecificationArray): #Определение backend lBackend=mDefaultPywinautoBackend - #pdb.set_trace() if "backend" in inControlSpecificationArray[0]: lBackend=inControlSpecificationArray[0]["backend"] inControlSpecificationArray[0].pop("backend") #Подготовка входного массива - inControlSpecificationOriginArray=inControlSpecificationArray + inControlSpecificationOriginArray=copy.deepcopy(inControlSpecificationArray) inControlSpecificationArray=UIOSelector_SearchProcessNormalize_UIOSelector(inControlSpecificationArray) #Выполнить идентификацию объектов, если передан массив lResultList=[]; lTempObject=None if len(inControlSpecificationArray) > 0: - #Выполнить подключение к объекту - lRPAApplication = pywinauto.Application(backend=lBackend) - #Проверка разрядности - try: - lRPAApplication.connect(**inControlSpecificationArray[0]) - except Exception as e: - UIOSelector_TryRestore_Dict(inControlSpecificationArray) + #Сформировать выборку элементов, которые подходят под первый уровень спецификации + lSpecificationLvL1List = pywinauto.findwindows.find_elements(**inControlSpecificationArray[0]) + for lItem in lSpecificationLvL1List: + #Сделать независимую копию и установить информацию о process_id и handle + lItemControlSpecificationArray=copy.deepcopy(inControlSpecificationArray) + lItemControlSpecificationArray[0]["process_id"]=lItem.process_id + lItemControlSpecificationArray[0]["handle"]=lItem.handle + lItemControlSpecificationOriginArray=copy.deepcopy(inControlSpecificationOriginArray) + lItemControlSpecificationOriginArray[0]["process_id"]=lItem.process_id + lItemControlSpecificationOriginArray[0]["handle"]=lItem.handle + #Выполнить подключение к объекту + lRPAApplication = pywinauto.Application(backend=lBackend) + #Проверка разрядности try: - lRPAApplication.connect(**inControlSpecificationArray[0]) + lRPAApplication.connect(**lItemControlSpecificationArray[0]) except Exception as e: - lRPAApplication = None - if lRPAApplication is not None: - #lTempObject=lRPAApplication.window(**inControlSpecificationArray[0]) - #Скорректировано из-за недопонимания структуры - lTempObject=lRPAApplication - #Нормализация массива для целей выборки объекта (удаление конфликтующих ключей) - inControlSpecificationArray=UIOSelector_SearchUIONormalize_UIOSelector(inControlSpecificationOriginArray) - #Циклическое прохождение к недрам объекта - for lWindowSpecification in inControlSpecificationArray[0:]: - lTempObject=lTempObject.window(**lWindowSpecification) - return lTempObject + UIOSelector_TryRestore_Dict(lItemControlSpecificationArray) + try: + lRPAApplication.connect(**lItemControlSpecificationArray[0]) + except Exception as e: + lRPAApplication = None + if lRPAApplication is not None: + #lTempObject=lRPAApplication.window(**lItemControlSpecificationArray[0]) + #Скорректировано из-за недопонимания структуры + lTempObject=lRPAApplication + #Нормализация массива для целей выборки объекта (удаление конфликтующих ключей) + lItemControlSpecificationArray=UIOSelector_SearchUIONormalize_UIOSelector(lItemControlSpecificationOriginArray) + #Циклическое прохождение к недрам объекта + for lWindowSpecification in lItemControlSpecificationArray[0:]: + lTempObject=lTempObject.window(**lWindowSpecification) + #Добавить объект в результирующий массив + lResultList.append(lTempObject) + return lResultList ################################################################################################## #inControlSpecificationArray - List of dict, dict in pywinauto.find_windows notation #Backend selection - attribute "backend" ("win32" || "uia") in 1-st list element @@ -360,7 +374,6 @@ def PWASpecification_Get_UIO(inControlSpecificationArray): def PWASpecification_Get_PWAApplication(inControlSpecificationArray): #Определение backend lBackend=mDefaultPywinautoBackend - #pdb.set_trace() if "backend" in inControlSpecificationArray[0]: lBackend=inControlSpecificationArray[0]["backend"] inControlSpecificationArray[0].pop("backend") @@ -441,7 +454,6 @@ def UIOSelector_SearchChildByMouse_UIOTree(inElementSpecification): #Detect backend of the elements lFlagIsBackendWin32 = True #Если объект имеется (не None), то выполнить построение иерархии - #pdb.set_trace() if lElement is not None: if lElement.backend.name == 'uia': lFlagIsBackendWin32 = False @@ -1016,7 +1028,6 @@ def UIO_Highlight(lWrapperObject,colour='green',thickness=2,fill=win32defines.BS #Установить фокус на объект, чтобы было видно выделение lWrapperObject.set_focus() time.sleep(0.5) - #pdb.set_trace() # don't draw if dialog is not visible #if not lWrapperObject.is_visible(): # return @@ -1095,7 +1106,7 @@ if not mFlagIsDebug: lJSONInput = ProcessCommunicator.ProcessParentReadWaitObject() lProcessResponse["ActivitySpecificationDict"]=lJSONInput #Выполнить вызов функции - lProcessResponse["Result"]=JSONNormalize.JSONNormalizeDictListStrInt(locals()[lJSONInput['ActivityName']](*lJSONInput['ArgumentList'],**lJSONInput['ArgumentDict'])) + lProcessResponse["Result"]=JSONNormalize.JSONNormalizeDictListStrIntBool(locals()[lJSONInput['ActivityName']](*lJSONInput['ArgumentList'],**lJSONInput['ArgumentDict'])) except Exception as e: #Установить флаг ошибки lProcessResponse["ErrorFlag"]=True diff --git a/Robot/JSONNormalize.py b/Robot/JSONNormalize.py index 6224ce1d..ef540777 100644 --- a/Robot/JSONNormalize.py +++ b/Robot/JSONNormalize.py @@ -22,6 +22,7 @@ def JSONNormalizeDict(inDictionary): type(lItemValue) is int or type(lItemValue) is str or type(lItemValue) is list or + type(lItemValue) is bool or lItemValue is None): True==True else: @@ -47,6 +48,7 @@ def JSONNormalizeList(inList): if ( type(lItemValue) is int or type(lItemValue) is str or + type(lItemValue) is bool or lItemValue is None): lResult.append(lItemValue) #Если является словарем - вызвать функцию нормализации словаря @@ -65,15 +67,17 @@ def JSONNormalizeDictList(inDictList): if type(inDictList) is list: lResult=JSONNormalizeList(inDictList) return lResult; -def JSONNormalizeDictListStrInt(inDictListStrInt): +def JSONNormalizeDictListStrIntBool(inDictListStrIntBool): lResult=None - if type(inDictListStrInt) is dict: - lResult=JSONNormalizeDict(inDictListStrInt) - if type(inDictListStrInt) is list: - lResult=JSONNormalizeList(inDictListStrInt) - if type(inDictListStrInt) is str: - lResult=inDictListStrInt - if type(inDictListStrInt) is int: - lResult=inDictListStrInt + if type(inDictListStrIntBool) is dict: + lResult=JSONNormalizeDict(inDictListStrIntBool) + if type(inDictListStrIntBool) is list: + lResult=JSONNormalizeList(inDictListStrIntBool) + if type(inDictListStrIntBool) is str: + lResult=inDictListStrIntBool + if type(inDictListStrIntBool) is int: + lResult=inDictListStrIntBool + if type(inDictListStrIntBool) is bool: + lResult=inDictListStrIntBool return lResult; diff --git a/Robot/Robot.py b/Robot/Robot.py index 076d0786..b57a6fbc 100644 --- a/Robot/Robot.py +++ b/Robot/Robot.py @@ -88,7 +88,10 @@ def ActivityRun(inActivitySpecificationDict): if mProcessGUI_x64 is None: lFlagRun64=False else: - if inActivitySpecificationDict["ActivityName"].startswith("UIOSelector") or inActivitySpecificationDict["ActivityName"].startswith("PWASpecification"): + if inActivitySpecificationDict["ActivityName"]=="UIOSelectorsSecs_WaitAppear_List": + #Функция ожидания появления элементов (тк элементы могут быть недоступны, неизвестно в каком фреймворке каждый из них может появиться) + lFlagRun64=True + elif inActivitySpecificationDict["ActivityName"].startswith("UIOSelector") or inActivitySpecificationDict["ActivityName"].startswith("PWASpecification"): if len(inActivitySpecificationDict["ArgumentList"])>0: if len(inActivitySpecificationDict["ArgumentList"][0])>0: #Определение разрядности (32 и 64) для тех функций, где это необходимо diff --git a/Studio/JSONNormalize.py b/Studio/JSONNormalize.py index 651c1a4d..933b171b 100644 --- a/Studio/JSONNormalize.py +++ b/Studio/JSONNormalize.py @@ -22,6 +22,7 @@ def JSONNormalizeDict(inDictionary): type(lItemValue) is int or type(lItemValue) is str or type(lItemValue) is list or + type(lItemValue) is bool or lItemValue is None): True==True else: @@ -47,6 +48,7 @@ def JSONNormalizeList(inList): if ( type(lItemValue) is int or type(lItemValue) is str or + type(lItemValue) is bool or lItemValue is None): lResult.append(lItemValue) #Если является словарем - вызвать функцию нормализации словаря diff --git a/Studio/Studio.py b/Studio/Studio.py index 1ec71723..ee831cda 100644 --- a/Studio/Studio.py +++ b/Studio/Studio.py @@ -7,6 +7,7 @@ import zlib import os import ProcessCommunicator import sys +import traceback sys.path.append('../Robot') import Robot @@ -110,25 +111,39 @@ class testHTTPServer_RequestHandler(BaseHTTPRequestHandler): # Write content as utf-8 data self.wfile.write(bytes(message, "utf8")) if self.path == '/GUIAction': - #ReadRequest - lInputByteArrayLength = int(self.headers.get('Content-Length')) - lInputByteArray=self.rfile.read(lInputByteArrayLength) - #Превращение массива байт в объект - lInputObject=json.loads(lInputByteArray.decode('utf8')) + #Обернуть в try, чтобы вернуть ответ, что сообщение не может быть обработано + # pdb.set_trace() # Send response status code self.send_response(200) # Send headers self.send_header('Content-type','application/json') self.end_headers() - # Send message back to client - #{'functionName':'', 'argsArray':[]} - #pdb.set_trace() - lRequestObject=lInputObject - #Отправить команду роботу - lResponseObject=Robot.ActivityRun(lRequestObject) - message = json.dumps(lResponseObject) - # Write content as utf-8 data - self.wfile.write(bytes(message, "utf8")) + try: + #ReadRequest + lInputByteArrayLength = int(self.headers.get('Content-Length')) + lInputByteArray=self.rfile.read(lInputByteArrayLength) + #Превращение массива байт в объект + lInputObject=json.loads(lInputByteArray.decode('utf8')) + # Send message back to client + #{'functionName':'', 'argsArray':[]} + #pdb.set_trace() + lRequestObject=lInputObject + #Отправить команду роботу + lResponseObject=Robot.ActivityRun(lRequestObject) + message = json.dumps(lResponseObject) + except Exception as e: + #Установить флаг ошибки + lProcessResponse={"Result":None} + lProcessResponse["ErrorFlag"]=True + #Зафиксировать traceback + lProcessResponse["ErrorTraceback"]=traceback.format_exc() + #Зафиксировать Error message + lProcessResponse["ErrorMessage"]=str(e) + #lProcessResponse["ErrorArgs"]=str(e.args) + message = json.dumps(lProcessResponse) + finally: + # Write content as utf-8 data + self.wfile.write(bytes(message, "utf8")) if self.path == '/GUIActionList': #ReadRequest lInputByteArrayLength = int(self.headers.get('Content-Length'))