From dcbdb0e7da6a9985932aec16c76e13e7a4281207 Mon Sep 17 00:00:00 2001 From: Ivan Maslov Date: Fri, 13 Mar 2020 17:13:22 +0300 Subject: [PATCH] # Fix in RobotRDPActive (for production run) --- .../Settings/ControlPanel_RobotRDPActive.py | 2 +- .../pyOpenRPA/Tools/RobotRDPActive/CMDStr.py | 12 +- .../Tools/RobotRDPActive/Clipboard.py | 5 +- .../Tools/RobotRDPActive/Connector.py | 138 ++++++++++-- .../RobotRDPActive/ConnectorExceptions.py | 10 + .../GlobalDictSessionIndex_Defs.py | 56 ++++- .../pyOpenRPA/Tools/RobotRDPActive/Monitor.py | 203 ++++++++++++------ .../Tools/RobotRDPActive/Scheduler.py | 4 +- .../Tools/RobotRDPActive/__main__.py | 37 ++-- .../SettingsRobotRDPActiveExample.py | 13 +- 10 files changed, 367 insertions(+), 113 deletions(-) create mode 100644 Sources/pyOpenRPA/Tools/RobotRDPActive/ConnectorExceptions.py diff --git a/Orchestrator/Settings/ControlPanel_RobotRDPActive.py b/Orchestrator/Settings/ControlPanel_RobotRDPActive.py index 2ecce44f..1e76458b 100644 --- a/Orchestrator/Settings/ControlPanel_RobotRDPActive.py +++ b/Orchestrator/Settings/ControlPanel_RobotRDPActive.py @@ -63,7 +63,7 @@ def RenderRobotRDPActive(inGlobalConfiguration): ################################ #Session state lItemSessionState='Disconnected' - if lItem.get("FlagSessionIsActive",False): + if lItem.get("SessionIsWindowExistBool",False): lItemSessionState='Connected' lResultDict["BodyKeyValueList"].append({"Key":f"[{str(lRDPListIndex)}]{lLabelSessionFullScreen}{lLabelIsIgnored}{lItem.get('Host','localhost')}:{lItem.get('Port','--')}","Value":f"{lItem.get('Login','--')}, {lItem.get('SessionHex','--')}, State {lItemSessionState}, {lSetFullScreenA}, {lIgnoreIndexListLink}"}) lRDPListIndex = lRDPListIndex + 1 diff --git a/Sources/pyOpenRPA/Tools/RobotRDPActive/CMDStr.py b/Sources/pyOpenRPA/Tools/RobotRDPActive/CMDStr.py index 02a2651a..992e28e1 100644 --- a/Sources/pyOpenRPA/Tools/RobotRDPActive/CMDStr.py +++ b/Sources/pyOpenRPA/Tools/RobotRDPActive/CMDStr.py @@ -11,4 +11,14 @@ def ProcessStop(inProcessName, inFlagForceClose): lResult = f'taskkill /im "{inProcessName}" /fi "username eq %USERNAME%"' if inFlagForceClose: lResult+= " /F" - return lResult \ No newline at end of file + return lResult +# Send file from Host to Session RDP using shared drive in RDP (force copy) +def FileStoredSend(inHostFilePath, inRDPFilePath): + lHostFileAbsPath = os.path.join("\\\\tsclient", os.path.abspath(inHostFilePath).replace(":","")) # \\tsclient\C\path\to\file + lHostRDPFileAbsPath = os.path.abspath(inRDPFilePath) # File location in RDP + lResult = f'copy /Y "{lHostFileAbsPath}" "{lHostRDPFileAbsPath}"' +# Send file from Session RDP to Host using shared drive in RDP (force copy) +def FileStoredRecieve(inRDPFilePath, inHostFilePath): + lHostFileAbsPath = os.path.join("\\\\tsclient", os.path.abspath(inHostFilePath).replace(":","")) # \\tsclient\C\path\to\file + lHostRDPFileAbsPath = os.path.abspath(inRDPFilePath) # File location in RDP + lResult = f'copy /Y "{lHostRDPFileAbsPath}" "{lHostFileAbsPath}"' \ No newline at end of file diff --git a/Sources/pyOpenRPA/Tools/RobotRDPActive/Clipboard.py b/Sources/pyOpenRPA/Tools/RobotRDPActive/Clipboard.py index 1af5ea76..aa0c1a8c 100644 --- a/Sources/pyOpenRPA/Tools/RobotRDPActive/Clipboard.py +++ b/Sources/pyOpenRPA/Tools/RobotRDPActive/Clipboard.py @@ -2,15 +2,16 @@ import win32clipboard import keyboard # keyboard functions import time # Some operations need wait import random # random number for test +gWaitTextInClipboardSec = 1 # Second for wait text will be set in clipboard (for get operations) # set clipboard data def TextSet(inTextStr): win32clipboard.OpenClipboard() win32clipboard.EmptyClipboard() win32clipboard.SetClipboardText(inTextStr) win32clipboard.CloseClipboard() - # get clipboard data -def TextGet(): +def TextGet(inWaitTextInClipboardSec = gWaitTextInClipboardSec): + time.sleep(inWaitTextInClipboardSec) # Wait for clipboard will save win32clipboard.OpenClipboard() data = win32clipboard.GetClipboardData() win32clipboard.CloseClipboard() diff --git a/Sources/pyOpenRPA/Tools/RobotRDPActive/Connector.py b/Sources/pyOpenRPA/Tools/RobotRDPActive/Connector.py index 72e37915..a024d535 100644 --- a/Sources/pyOpenRPA/Tools/RobotRDPActive/Connector.py +++ b/Sources/pyOpenRPA/Tools/RobotRDPActive/Connector.py @@ -1,12 +1,13 @@ #Import parent folder to import current / other packages from pyOpenRPA.Robot import UIDesktop #Lib to access RDP window +from . import ConnectorExceptions # Exceptions classes import os #os for process run import uuid #temp id for Template.rdp import tempfile #Temporary location import time import subprocess from . import Clipboard # Clipboard functions get/set -import keyboard +import keyboard # Keyboard functions import time import random # random integers from win32api import GetSystemMetrics # Get Screen rect @@ -37,6 +38,7 @@ def Session(inRDPSessionConfiguration): #Remove temp file time.sleep(4) #Delete file after some delay - one way to delete and run the RDP before because RDP is not read file in one moment os.remove(lRDPFile) # delete the temp rdp + # Set the result return inRDPSessionConfiguration #Add login/ password to the windows credentials to run RDP def SessionLoginPasswordSet(inHost, inLogin, inPassword): @@ -79,6 +81,9 @@ def SessionConfigurationCreate(inConfiguration): return (lRDPCurrentFileFullPath, (lRDPCurrentFileFullPath.split("\\")[-1])[0:-4]) #RDPSessionStart def SessionRDPStart(inRDPFilePath): + #Disable certificate warning + lCMDString = 'reg add "HKEY_CURRENT_USER\\Software\\Microsoft\\Terminal Server Client" /v "AuthenticationLevelOverride" /t "REG_DWORD" /d 0 /f' + os.system(lCMDString) #run rdp session lItemArgs = [inRDPFilePath] subprocess.Popen(lItemArgs, shell=True) @@ -92,10 +97,13 @@ def SessionRDPStart(inRDPFilePath): {"title": "D&on't ask me again for connections to this computer", "friendly_class_name": "CheckBox"}], [{"title_re": f"{lRDPFileName}.*", - "class_name": "TscShellContainerClass", "backend": "win32"}] + "class_name": "TscShellContainerClass", "backend": "win32"},{"depth_start":3, "depth_end": 3, "class_name":"UIMainClass"}] ], 30 - ) + ) + #Enable certificate warning + lCMDString = 'reg add "HKEY_CURRENT_USER\\Software\\Microsoft\\Terminal Server Client" /v "AuthenticationLevelOverride" /t "REG_DWORD" /d 2 /f' + os.system(lCMDString) #Click if 0 is appear (RUS) if 0 in lWaitResult: #Check the box do not retry @@ -129,7 +137,24 @@ def SessionRDPStart(inRDPFilePath): ) # Raise exception if RDP is not active if len(lWaitResult) == 0: - raise Exception("Error when initialize the RDP session!") + raise ConnectorExceptions.SessionWindowNotExistError("Error when initialize the RDP session - No RDP windows has appreared!") + # Wait for init + time.sleep(3) + # Check RDP responsibility + lDoCheckResponsibilityBool = True + lDoCheckResponsibilityCountMax = 20 + lDoCheckResponsibilityCountCurrent = 0 + while lDoCheckResponsibilityBool: + # Check if counter is exceed - raise exception + if lDoCheckResponsibilityCountCurrent >= lDoCheckResponsibilityCountMax: + raise ConnectorExceptions.SessionWindowNotResponsibleError("Error when initialize the RDP session - RDP window is not responding!") + # Check responding + lDoCheckResponsibilityBool = not SystemRDPIsResponsible() + # Wait if is not responding + if lDoCheckResponsibilityBool: + time.sleep(3) + # increase the couter + lDoCheckResponsibilityCountCurrent+=1 #Prepare little window SessionScreen100x550(lRDPFileName) return None @@ -139,6 +164,7 @@ def SessionScreenFull(inSessionHex): lRDPWindow = UIDesktop.UIOSelector_Get_UIO([{"title_re": f"{inSessionHex}.*", "backend": "win32"}]) lRDPWindow.set_focus() lRDPWindow.maximize() + #time.sleep(0.5) if not SessionIsFullScreen(inSessionHex): lRDPWindow.type_keys("^%{BREAK}") time.sleep(0.5) @@ -155,29 +181,30 @@ def SessionScreen100x550(inSessionHex): time.sleep(0.5) lRDPWindow.move_window(10,10,550,100) return None +# Session - close window +def SessionClose(inSessionHexStr): + #Close window + UIDesktop.UIOSelector_Get_UIO([{"title_re": f"{inSessionHexStr}.*", "backend": "win32"}]).close() #Type command in CMD -# inFlagDoCrossCheck: True - Do check that CMD is executed (the text response will not be available) +# inSessionHex - SessionHex to catch window # inModeStr "LISTEN", "CROSSCHECK", "RUN" # "LISTEN" - Get result of the cmd command in result TODO get home script # "CROSSCHECK" - Check if the command was successufully sent TODO get home script # "RUN" - Run without crosscheck and get clipboard -# return ... +# return { +# "OutStr": <> # Result string +# "IsResponsibleBool": True|False # Flag is RDP is responsible - works only when inModeStr = CROSSCHECK +# } # example Connector.SessionCMDRun("4d1e48f3ff6c45cc810ea25d8adbeb50","start notepad", "RUN") -def SessionCMDRun(inSessionHex,inCMDCommandStr, inModeStr="RUN"): - lCMDPostFixStr = "" # Case default "RUN" - if inModeStr == "CROSSCHECK": - lCMDPostFixStr = f"| echo {str(random.randrange(999,9999999))} | clip" - elif inModeStr == "LISTEN": - lCMDPostFixStr = f"| clip" +def SessionCMDRun(inSessionHex,inCMDCommandStr = "echo 1", inModeStr="CROSSCHECK"): + # Init the result dict + lResult = {"OutStr": None,"IsResponsibleBool":True} + # Enter full screen mode SessionScreenFull(inSessionHex) time.sleep(2) - UIDesktop.UIOSelector_Get_UIO([{"title_re": f"{inSessionHex}.*", "backend": "win32"}]) - keyboard.press_and_release('win+r') - time.sleep(1) - keyboard.write(f"cmd /c {inCMDCommandStr} {lCMDPostFixStr}") - time.sleep(1) - # TODo cross check from clipboard - keyboard.press_and_release('enter') + # Run CMD operations + lResult = SystemCMDRun(inCMDCommandStr = inCMDCommandStr, inModeStr = inModeStr) + # Exit fullscreen mode SessionScreen100x550(inSessionHex) # Check if session is in Full screen mode # Return True - is in fullscreen @@ -196,3 +223,76 @@ def SessionIsFullScreen(inSessionHexStr): if lSessionHeight == lHeight and lSessionWeight == lWeight: lResult = True return lResult +# Check if RDP session is responsible (check with random combination in cmd) +# Attention - function will be work fine if RDP will be in full screen mode!!! (see def SessionScreenFull) +# Return True - is responsible; False - is not responsible +#Type command in CMD +# inFlagDoCrossCheck: True - Do check that CMD is executed (the text response will not be available) +# inModeStr "LISTEN", "CROSSCHECK", "RUN" +# "LISTEN" - Get result of the cmd command in result TODO get home script +# "CROSSCHECK" - Check if the command was successufully sent TODO get home script +# "RUN" - Run without crosscheck and get clipboard +# inClipboardTimeoutSec # Second for wait when clipboard will changed +# return { +# "OutStr": <> # Result string +# "IsResponsibleBool": True|False # Flag is RDP is responsible - works only when inModeStr = CROSSCHECK +# } +# example Connector.SessionCMDRun("4d1e48f3ff6c45cc810ea25d8adbeb50","start notepad", "RUN") +def SystemCMDRun(inCMDCommandStr = "echo 1", inModeStr="CROSSCHECK", inClipboardTimeoutSec = 5): + # Set random text to clipboard (for check purposes that clipboard text has been changed) + lClipboardTextOld = str(random.randrange(999,9999999)) + Clipboard.TextSet(lClipboardTextOld) + # Init the result dict + lResult = {"OutStr": None,"IsResponsibleBool":True} + lCrosscheckKeyStr = str(random.randrange(999,9999999)) + lCMDPostFixStr = "" # Case default "RUN" + if inModeStr == "CROSSCHECK": + lCMDPostFixStr = f"| echo {lCrosscheckKeyStr} | clip" + elif inModeStr == "LISTEN": + lCMDPostFixStr = f"| clip" + keyboard.press_and_release('win+r') + time.sleep(1) + # Remove old text + keyboard.press_and_release("ctrl+a") + keyboard.press_and_release("backspace") + # Write new text + keyboard.write(f"cmd /c {inCMDCommandStr} {lCMDPostFixStr}") + time.sleep(1) + # TODo cross check from clipboard + keyboard.press_and_release('enter') + # Get OutStr (Case CROSSCHECK and LISTEN) + if inModeStr == "CROSSCHECK" or inModeStr == "LISTEN": + lClipboardWaitTimeStartSec = time.time() + lResult["OutStr"] = Clipboard.TextGet() # Get text from clipboard + while lResult["OutStr"] == lClipboardTextOld and (time.time() - lClipboardWaitTimeStartSec) <= inClipboardTimeoutSec + lResult["OutStr"] = Clipboard.TextGet() # Get text from clipboard + time.sleep(0.5) # wait some time for the next operation + # Do crosscheck + if inModeStr == "CROSSCHECK": + if lResult["OutStr"] == f"{lCrosscheckKeyStr} \r\n\x00\x00\x00\x00\x00": + lResult["IsResponsibleBool"] = True + else: + lResult["IsResponsibleBool"] = False + # return the result + return lResult +# Check if current RDP is responsible +def SystemRDPIsResponsible(): + return SystemCMDRun(inCMDCommandStr = "echo 1", inModeStr="CROSSCHECK")["IsResponsibleBool"] +# Click OK on error messages +def SystemRDPWarningClickOk(): + # Try to click OK Error window in RUS version + while UIDesktop.UIOSelector_Exist_Bool([{"title": "Подключение к удаленному рабочему столу", "class_name": "#32770", "backend": "win32"}, + {"title": "ОК", "class_name": "Button"}]): + try: + UIDesktop.UIOSelector_Get_UIO([{"title": "Подключение к удаленному рабочему столу", "class_name": "#32770", "backend": "win32"}, + {"title": "ОК", "class_name": "Button"}]).click() + except Exception as e: + pass + # Try to click OK Error window in ENG version + while UIDesktop.UIOSelector_Exist_Bool([{"title": "Remote Desktop Connection", "class_name": "#32770", "backend": "win32"}, + {"title": "OK", "class_name": "Button"}]): + try: + UIDesktop.UIOSelector_Get_UIO([{"title": "Remote Desktop Connection", "class_name": "#32770", "backend": "win32"}, + {"title": "OK", "class_name": "Button"}]).click() + except Exception as e: + pass \ No newline at end of file diff --git a/Sources/pyOpenRPA/Tools/RobotRDPActive/ConnectorExceptions.py b/Sources/pyOpenRPA/Tools/RobotRDPActive/ConnectorExceptions.py new file mode 100644 index 00000000..3ae91409 --- /dev/null +++ b/Sources/pyOpenRPA/Tools/RobotRDPActive/ConnectorExceptions.py @@ -0,0 +1,10 @@ +##################################### +# RobotRDPActive Exceptions class +##################################### +class SessionWindowNotExistError(Exception): pass #Error when Window not exists +class SessionWindowNotResponsibleError(Exception): pass # Error when Window not responding +class HostNoGUIError(Exception): pass # Orchestrator session has no GUI +#try: +# raise SessionWindowNotResponsibleError("Test") +#except SessionWindowNotResponsibleError as e: +# print("Catched") diff --git a/Sources/pyOpenRPA/Tools/RobotRDPActive/GlobalDictSessionIndex_Defs.py b/Sources/pyOpenRPA/Tools/RobotRDPActive/GlobalDictSessionIndex_Defs.py index 9ddcc3ee..bc7d5e8f 100644 --- a/Sources/pyOpenRPA/Tools/RobotRDPActive/GlobalDictSessionIndex_Defs.py +++ b/Sources/pyOpenRPA/Tools/RobotRDPActive/GlobalDictSessionIndex_Defs.py @@ -1,18 +1,66 @@ # ATTENTION! HERE IS NO Relative import because it will be imported dynamically +# All function check the flag SessionIsWindowResponsibleBool == True else no cammand is processed +# All functions can return None, Bool or Dict { "IsSuccessful": True } from pyOpenRPA.Tools.RobotRDPActive import CMDStr # Create CMD Strings from pyOpenRPA.Tools.RobotRDPActive import Connector # RDP API def ProcessStartIfNotRunning(inGlobalDict, inSessionIndex, inProcessName, inFilePath, inFlagGetAbsPath=True): + lResult = True lCMDStr = CMDStr.ProcessStartIfNotRunning(inProcessName,inFilePath, inFlagGetAbsPath= inFlagGetAbsPath) # Calculate the session Hex lSessionHex = inGlobalDict["RDPList"][inSessionIndex]["SessionHex"] - # Run CMD - Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=lCMDStr, inModeStr="RUN") + # Check is Session is responsible + if inGlobalDict["RDPList"][inSessionIndex]["SessionIsWindowResponsibleBool"]: + # Run CMD + Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=lCMDStr, inModeStr="RUN") + else: + # Write in logger - warning + inGlobalDict["Logger"].warning(f"GlobalDictSessionIndex_Defs.ProcessStartIfNotRunning: SessionIndex: {str(inSessionIndex)}, ProcessName: {inProcessName}:: Session is not responsible!") + lResult = False # Set false result - function has not been done + return lResult # Create CMD str to stop process def ProcessStop(inGlobalDict, inSessionIndex, inProcessName, inFlagForceClose): + lResult = True lCMDStr = f'taskkill /im "{inProcessName}" /fi "username eq %USERNAME%"' if inFlagForceClose: lCMDStr+= " /F" # Calculate the session Hex lSessionHex = inGlobalDict["RDPList"][inSessionIndex]["SessionHex"] - # Run CMD - Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=lCMDStr, inModeStr="RUN") \ No newline at end of file + # Check is Session is responsible + if inGlobalDict["RDPList"][inSessionIndex]["SessionIsWindowResponsibleBool"]: + # Run CMD + Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=lCMDStr, inModeStr="RUN") + else: + # TODO Write in logger - warning + inGlobalDict["Logger"].warning(f"GlobalDictSessionIndex_Defs.ProcessStop: SessionIndex: {str(inSessionIndex)}, ProcessName: {inProcessName}:: Session is not responsible!") + lResult = False # Set false result - function has not been done + return lResult +# Send file from Host to Session RDP using shared drive in RDP +def FileStoredSend(inGlobalDict, inSessionIndex, inHostFilePath, inRDPFilePath): + lResult = True + lCMDStr = CMDStr.FileStoredSend(inHostFilePath = inHostFilePath, inRDPFilePath = inRDPFilePath): + # Calculate the session Hex + lSessionHex = inGlobalDict["RDPList"][inSessionIndex]["SessionHex"] + # Check is Session is responsible + if inGlobalDict["RDPList"][inSessionIndex]["SessionIsWindowResponsibleBool"]: + # Run CMD + Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=lCMDStr, inModeStr="LISTEN", inClipboardTimeoutSec = 120) + else: + # Write in logger - warning + inGlobalDict["Logger"].warning(f"GlobalDictSessionIndex_Defs.FileStoredSend: SessionIndex: {str(inSessionIndex)}, HostFilePath: {inHostFilePath}:: Session is not responsible!") + lResult = False # Set false result - function has not been done + return lResult +# Recieve file from Session RDP to Host using shared drive in RDP +def FileStoredRecieve(inGlobalDict, inSessionIndex, inRDPFilePath, inHostFilePath): + lResult = True + lCMDStr = CMDStr.FileStoredRecieve(inRDPFilePath = inRDPFilePath, inHostFilePath = inHostFilePath): + # Calculate the session Hex + lSessionHex = inGlobalDict["RDPList"][inSessionIndex]["SessionHex"] + # Check is Session is responsible + if inGlobalDict["RDPList"][inSessionIndex]["SessionIsWindowResponsibleBool"]: + # Run CMD + Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=lCMDStr, inModeStr="LISTEN", inClipboardTimeoutSec = 120) + else: + # Write in logger - warning + inGlobalDict["Logger"].warning(f"GlobalDictSessionIndex_Defs.FileStoredRecieve: SessionIndex: {str(inSessionIndex)}, HostFilePath: {inHostFilePath}:: Session is not responsible!") + lResult = False # Set false result - function has not been done + return lResult \ No newline at end of file diff --git a/Sources/pyOpenRPA/Tools/RobotRDPActive/Monitor.py b/Sources/pyOpenRPA/Tools/RobotRDPActive/Monitor.py index 025cf231..c1eb11b9 100644 --- a/Sources/pyOpenRPA/Tools/RobotRDPActive/Monitor.py +++ b/Sources/pyOpenRPA/Tools/RobotRDPActive/Monitor.py @@ -4,73 +4,150 @@ import os import time # Time wait operations import pdb import importlib # from dynamic import module +from . import ConnectorExceptions # Exceptions classes #Check for session is closed. Reopen if detected. Always keep session is active def Monitor(inGlobalDict, inListUpdateTimeout): lFlagWhile = True + lResponsibilityCheckLastSec = time.time() # Get current time for check interval while lFlagWhile: - # UIOSelector list init - lUIOSelectorList = [] - #Prepare selectors list for check - for lIndex, lItem in enumerate(inGlobalDict["RDPList"]): - lUIOSelectorList.append([{"title_re": f"{lItem['SessionHex']}.*", "backend": "win32"}]) - #Run wait command - lRDPDissappearList = UIDesktop.UIOSelectorsSecs_WaitDisappear_List(lUIOSelectorList, inListUpdateTimeout) - ########################################### - #Analyze if flag safeturn off is activated - if inGlobalDict.get("OrchestratorToRobotResetStorage",{}).get("SafeTurnOff",False): - lFlagWhile=False - #Set status disconnected for all RDP List - for lItem in inGlobalDict["RDPList"]: - lItem["FlagSessionIsActive"]=False - #Kill all RDP sessions - os.system('taskkill /F /im mstsc.exe') - #Return from function - return - ########################################### - ########################################### - for lItem in lRDPDissappearList: - inGlobalDict["RDPList"][lItem]["FlagSessionIsActive"] = False # Set flag that session is disconnected + try: + # UIOSelector list init + lUIOSelectorList = [] + #Prepare selectors list for check + for lIndex, lItem in enumerate(inGlobalDict["RDPList"]): + lUIOSelectorList.append([{"title_re": f"{lItem['SessionHex']}.*", "backend": "win32"}]) + #Run wait command + #import pdb #pdb.set_trace() - #Session start if it is not in ignore list - #add check for selector if it is not in ignoreIndexList - if lItem not in inGlobalDict["OrchestratorToRobotStorage"]["IgnoreIndexList"]: - try: - Connector.Session(inGlobalDict["RDPList"][lItem]) - inGlobalDict["RDPList"][lItem]["FlagSessionIsActive"] = True # Flag that session is started - except Exception: - pass - ########################################### - #Check if from Orchestrator full screen session is set - if inGlobalDict["OrchestratorToRobotStorage"]["FullScreenSessionIndex"] != inGlobalDict["FullScreenSessionIndex"]: - #Do some switches - #If full screen mode we have now - if inGlobalDict["FullScreenSessionIndex"] is not None: - if inGlobalDict["RDPList"][inGlobalDict["FullScreenSessionIndex"]]["FlagSessionIsActive"]: - Connector.SessionScreen100x550(inGlobalDict["RDPList"][inGlobalDict["FullScreenSessionIndex"]]["SessionHex"]) - #If new session is setted - if inGlobalDict["OrchestratorToRobotStorage"]["FullScreenSessionIndex"] is not None: - if inGlobalDict["RDPList"][inGlobalDict["OrchestratorToRobotStorage"]["FullScreenSessionIndex"]]["FlagSessionIsActive"]: - Connector.SessionScreenFull(inGlobalDict["RDPList"][inGlobalDict["OrchestratorToRobotStorage"]["FullScreenSessionIndex"]]["SessionHex"]) - #Set one to other equal - inGlobalDict["FullScreenSessionIndex"] = inGlobalDict["OrchestratorToRobotStorage"]["FullScreenSessionIndex"] - ########################################### - # Check ActivityList from orchestrator - for lActivityItem in inGlobalDict["OrchestratorToRobotResetStorage"]["ActivityList"]: - ################# - #Call function from Activity structure - ################################################ - lSubmoduleFunctionName = lActivityItem["DefName"] - lFileFullPath = lActivityItem["ModulePath"] # "path\\to\\module.py" - lModuleName = (lFileFullPath.split("\\")[-1])[0:-3] - lTechSpecification = importlib.util.spec_from_file_location(lModuleName, lFileFullPath) - lTechModuleFromSpec = importlib.util.module_from_spec(lTechSpecification) - lTechSpecificationModuleLoader = lTechSpecification.loader.exec_module(lTechModuleFromSpec) - if lSubmoduleFunctionName in dir(lTechModuleFromSpec): - # Run SettingUpdate function in submodule - #mGlobalDict = getattr(lTechModuleFromSpec, lSubmoduleFunctionName)() - getattr(lTechModuleFromSpec, lSubmoduleFunctionName)(*lActivityItem["ArgList"],**lActivityItem["ArgDict"]) - ################################################# - inGlobalDict["OrchestratorToRobotResetStorage"]["ActivityList"] = [] # Override the value - time.sleep(2) + lRDPDissappearList = UIDesktop.UIOSelectorsSecs_WaitDisappear_List(lUIOSelectorList, inListUpdateTimeout) + #print(lRDPDissappearList) + ########################################### + #Analyze if flag safeturn off is activated + if inGlobalDict.get("OrchestratorToRobotResetStorage",{}).get("SafeTurnOff",False): + lFlagWhile=False + #Set status disconnected for all RDP List + for lItem in inGlobalDict["RDPList"]: + lItem["SessionIsWindowExistBool"]=False + lItem["SessionIsWindowResponsibleBool"]=False + #Kill all RDP sessions + os.system('taskkill /F /im mstsc.exe') + #Return from function + return + ########################################### + ########################################### + for lItem in lRDPDissappearList: + inGlobalDict["RDPList"][lItem]["SessionIsWindowExistBool"] = False # Set flag that session is disconnected + inGlobalDict["RDPList"][lItem]["SessionIsWindowResponsibleBool"]=False + #pdb.set_trace() + #Session start if it is not in ignore list + #add check for selector if it is not in ignoreIndexList + if lItem not in inGlobalDict["OrchestratorToRobotStorage"]["IgnoreIndexList"]: + try: + Connector.Session(inGlobalDict["RDPList"][lItem]) + inGlobalDict["RDPList"][lItem]["SessionIsWindowExistBool"] = True # Flag that session is started + inGlobalDict["RDPList"][lItem]["SessionIsWindowResponsibleBool"]= True + # Write in logger - info + inGlobalDict["Logger"].info(f"SessionHex: {str(inGlobalDict['RDPList'][lItem]['SessionHex'])}:: Session has been initialized!") + # catch ConnectorExceptions.SessionWindowNotExistError + except ConnectorExceptions.SessionWindowNotExistError as e: + inGlobalDict["RDPList"][lItem]["SessionIsWindowExistBool"] = False # Set flag that session is disconnected + inGlobalDict["RDPList"][lItem]["SessionIsWindowResponsibleBool"]=False + # Write in logger - warning + inGlobalDict["Logger"].warning(f"SessionHex: {str(inGlobalDict['RDPList'][lItem]['SessionHex'])}:: Session is not exist!") + # catch ConnectorExceptions.SessionWindowNotResponsibleError + except ConnectorExceptions.SessionWindowNotResponsibleError as e: + inGlobalDict["RDPList"][lItem]["SessionIsWindowExistBool"] = True # Set flag that session is disconnected + inGlobalDict["RDPList"][lItem]["SessionIsWindowResponsibleBool"]=False + # Write in logger - warning + inGlobalDict["Logger"].warning(f"SessionHex: {str(inGlobalDict['RDPList'][lItem]['SessionHex'])}:: Session is not responsible!") + # general exceptions + except Exception as e: + # Write in logger - warning + inGlobalDict["Logger"].exception(f"!!! ATTENTION !!! Unrecognized error") + ####################### + # Click all warning messages + Connector.SystemRDPWarningClickOk() + ####################### + ########################################### + #Check if from Orchestrator full screen session is set + if inGlobalDict["OrchestratorToRobotStorage"]["FullScreenSessionIndex"] != inGlobalDict["FullScreenSessionIndex"]: + #Do some switches + #If full screen mode we have now + if inGlobalDict["FullScreenSessionIndex"] is not None: + if inGlobalDict["RDPList"][inGlobalDict["FullScreenSessionIndex"]]["SessionIsWindowExistBool"]: + Connector.SessionScreen100x550(inGlobalDict["RDPList"][inGlobalDict["FullScreenSessionIndex"]]["SessionHex"]) + #If new session is setted + if inGlobalDict["OrchestratorToRobotStorage"]["FullScreenSessionIndex"] is not None: + if inGlobalDict["RDPList"][inGlobalDict["OrchestratorToRobotStorage"]["FullScreenSessionIndex"]]["SessionIsWindowExistBool"]: + Connector.SessionScreenFull(inGlobalDict["RDPList"][inGlobalDict["OrchestratorToRobotStorage"]["FullScreenSessionIndex"]]["SessionHex"]) + #Set one to other equal + inGlobalDict["FullScreenSessionIndex"] = inGlobalDict["OrchestratorToRobotStorage"]["FullScreenSessionIndex"] + ########################################### + #################################### + ##### Block check responsibility interval [ResponsibilityCheckIntervalSec] + if inGlobalDict['ResponsibilityCheckIntervalSec']: # Do check if ResponsibilityCheckIntervalSec is not None + if (time.time - lResponsibilityCheckLastSec()) > inGlobalDict['ResponsibilityCheckIntervalSec']: + # Set new time + lResponsibilityCheckLastSec = time.time() + # Do responsibility check + for lIndex, lItem in enumerate(inGlobalDict["RDPList"]): + # Check RDP responsibility + lDoCheckResponsibilityBool = True + lDoCheckResponsibilityCountMax = 20 + lDoCheckResponsibilityCountCurrent = 0 + while lDoCheckResponsibilityBool: + # Enter full screen mode + Connector.SessionScreenFull(lItem['SessionHex']) + time.sleep(2) + # Check responding + lDoCheckResponsibilityBool = not Connector.SystemRDPIsResponsible() + # Check if counter is exceed - raise exception + if lDoCheckResponsibilityCountCurrent >= lDoCheckResponsibilityCountMax: + lItem["SessionIsWindowExistBool"] = False # Set flag that session is disconnected + lItem["SessionIsWindowResponsibleBool"]=False + # Session window is not responsible - restart RDP (close window here - next loop will reconnect) + Connector.SessionClose(lItem['SessionHex']) + # Turn off the loop + lDoCheckResponsibilityBool = False + else: + # Exit fullscreen mode + Connector.SessionScreen100x550(lItem['SessionHex']) + # Wait if is not responding + if lDoCheckResponsibilityBool: + time.sleep(3) + # increase the couter + lDoCheckResponsibilityCountCurrent+=1 + #################################### + # Check ActivityList from orchestrator + lActivityListNew = [] + for lActivityItem in inGlobalDict["OrchestratorToRobotResetStorage"]["ActivityList"]: + ################# + #Call function from Activity structure + ################################################ + lSubmoduleFunctionName = lActivityItem["DefName"] + lFileFullPath = lActivityItem["ModulePath"] # "path\\to\\module.py" + lModuleName = (lFileFullPath.split("\\")[-1])[0:-3] + lTechSpecification = importlib.util.spec_from_file_location(lModuleName, lFileFullPath) + lTechModuleFromSpec = importlib.util.module_from_spec(lTechSpecification) + lTechSpecificationModuleLoader = lTechSpecification.loader.exec_module(lTechModuleFromSpec) + if lSubmoduleFunctionName in dir(lTechModuleFromSpec): + # Run SettingUpdate function in submodule + #mGlobalDict = getattr(lTechModuleFromSpec, lSubmoduleFunctionName)() + lActivityItemResult=getattr(lTechModuleFromSpec, lSubmoduleFunctionName)(*lActivityItem["ArgList"],**lActivityItem["ArgDict"]) + lActivityItemResultType = type(lActivityItemResult) + # Check if Result is bool + if lActivityItemResultType is bool: + if not lActivityItemResult: + # Activity is not done - add to list (retry in future) + lActivityListNew.append(lActivityItem) + ################################################# + inGlobalDict["OrchestratorToRobotResetStorage"]["ActivityList"] = lActivityListNew # Override the value + except RuntimeError as e: + # case noGUI error passed - do nothing + # Write in logger - warning + inGlobalDict["Logger"].warning(f"Host session has lost the GUI") + finally: + # Wait for the next iteration + time.sleep(2) return None #TODO Def garbage window cleaner (if connection was lost) \ No newline at end of file diff --git a/Sources/pyOpenRPA/Tools/RobotRDPActive/Scheduler.py b/Sources/pyOpenRPA/Tools/RobotRDPActive/Scheduler.py index 4eb0e5f0..2f48aa9f 100644 --- a/Sources/pyOpenRPA/Tools/RobotRDPActive/Scheduler.py +++ b/Sources/pyOpenRPA/Tools/RobotRDPActive/Scheduler.py @@ -19,7 +19,7 @@ class Scheduler: # Init the threads lTimerMainThread = threading.Thread(target = self.TimerMainThreadRun) lTimerMainThread.start() # Start the Timer main thread - print (f"Class instance configuration: {self.mSchedulerDict}, Init has been completed") + #print (f"Class instance configuration: {self.mSchedulerDict}, Init has been completed") ######################## # Main timer thread - run when init class instance def TimerMainThreadRun(self): @@ -102,5 +102,5 @@ class Scheduler: #Timer.activityLoopStart(lItem["ActivityIntervalSeconds"], lActivityTimeEndDateTime, lItem["Activity"]) lTimer = Timer.RepeatedTimer(lItem["ActivityIntervalSeconds"], lActivityTimeEndDateTime, lDef, *lItem["Activity"]["ArgList"], **lItem["Activity"]["ArgDict"]) # it auto-starts, no need of rt.start() #Уснуть до следующего прогона - print (f"Loop has been completed") + #print (f"Loop has been completed") time.sleep(lDaemonLoopSeconds) \ No newline at end of file diff --git a/Sources/pyOpenRPA/Tools/RobotRDPActive/__main__.py b/Sources/pyOpenRPA/Tools/RobotRDPActive/__main__.py index 06e7b5c3..21d3244e 100644 --- a/Sources/pyOpenRPA/Tools/RobotRDPActive/__main__.py +++ b/Sources/pyOpenRPA/Tools/RobotRDPActive/__main__.py @@ -31,22 +31,21 @@ if lSubmoduleFunctionName in dir(lTechModuleFromSpec): from pyOpenRPA.Tools.RobotRDPActive import Connector from pyOpenRPA.Tools.RobotRDPActive import Monitor from pyOpenRPA.Tools.RobotRDPActive import Scheduler # Scheduler operations -#Disable certificate warning -lCMDString = 'reg add "HKEY_CURRENT_USER\\Software\\Microsoft\\Terminal Server Client" /v "AuthenticationLevelOverride" /t "REG_DWORD" /d 0 /f' -os.system(lCMDString) -#time.sleep() -for lConfigurationItem in mGlobalDict["RDPList"]: - try: - Connector.Session(lConfigurationItem) - lConfigurationItem["FlagSessionIsActive"]=True #Flag that session is started - except Exception: - pass -#Run monitor -#print(mGlobalDict) -Scheduler.Scheduler(mGlobalDict["Scheduler"]) # Init & Run Scheduler -Monitor.Monitor(mGlobalDict, 1) -#Enable certificate warning -lCMDString = 'reg add "HKEY_CURRENT_USER\\Software\\Microsoft\\Terminal Server Client" /v "AuthenticationLevelOverride" /t "REG_DWORD" /d 2 /f' -os.system(lCMDString) -#Close all thread from OrchestratorConnection -mGlobalDict["OrchestratorConnectorTerminateAll"]() \ No newline at end of file +#### Global error handler +try: + #time.sleep() + ######## Init the RDP List + for lConfigurationItem in mGlobalDict["RDPList"]: + lConfigurationItem["SessionIsWindowExistBool"]=False #Flag that session is not started + lConfigurationItem["SessionIsWindowResponsibleBool"]=False #Flag that session is not started + lConfigurationItem["SessionHex"]=" 77777sdfsdf77777dsfdfsf77777777" #Flag that session is not started + ########## + #Run monitor + Scheduler.Scheduler(mGlobalDict["Scheduler"]) # Init & Run Scheduler + Monitor.Monitor(mGlobalDict, 1) +except Exception as e: + # Write in logger - warning + mGlobalDict["Logger"].exception(f"!!! ATTENTION !!! Global error handler - look at code") +finally: + #Close all thread from OrchestratorConnection + mGlobalDict["OrchestratorConnectorTerminateAll"]() \ No newline at end of file diff --git a/Utils/RobotRDPActive/SettingsRobotRDPActiveExample.py b/Utils/RobotRDPActive/SettingsRobotRDPActiveExample.py index 94c7e5be..30827e68 100644 --- a/Utils/RobotRDPActive/SettingsRobotRDPActiveExample.py +++ b/Utils/RobotRDPActive/SettingsRobotRDPActiveExample.py @@ -4,6 +4,7 @@ from pyOpenRPA.Tools import RobotRDPActive # For RobotRDPActive folder purposes import os import logging import datetime +import sys #Definitions lOrchestratorHost="localhost" lOrchestratorPort=8081 @@ -29,10 +30,13 @@ def Settings(): "DepthBit": "32" # "32" or "24" or "16" or "15" }, "SharedDriveList":["c"], # List of the Root sesion hard drives + ###### Will updated in program ############ "SessionHex":"", # Hex is created when robot runs - "FlagSessionIsActive": False + "SessionIsWindowExistBool": False , # Flag if the RDP window is exist, old name "FlagSessionIsActive". Check every n seconds + "SessionIsWindowResponsibleBool": False # Flag if RDP window is responsible (recieve commands). Check every nn seconds. If window is Responsible - window is Exist too } ], + "ResponsibilityCheckIntervalSec": None, # Seconds interval when Robot check the RDP responsibility. if None - dont check "FullScreenSessionIndex": None, #Index of the current session which is full screened, None is no session in fullscreen "Logger": logging.getLogger("RobotRDPActive"), "OrchestratorToRobotStorage": { @@ -151,12 +155,17 @@ def Settings(): #Подготовка логгера Robot ######################### mRobotLogger=mDict["Logger"] - mRobotLogger.setLevel(logging.INFO) + mRobotLogger.setLevel(logging.INFO) #logging.DEBUG # create the logging file handler mRobotLoggerFH = logging.FileHandler("Reports\ReportRobotRDPActive_"+datetime.datetime.now().strftime("%Y_%m_%d")+".log") mRobotLoggerFormatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') mRobotLoggerFH.setFormatter(mRobotLoggerFormatter) # add handler to logger object mRobotLogger.addHandler(mRobotLoggerFH) + ####################Add console output + if mDict["Logs"]["FlagShowInConsole"]: + handler = logging.StreamHandler(sys.stdout) + handler.setFormatter(lRobotLoggerFormatter) + mRobotLogger.addHandler(handler) ############################################ return mDict \ No newline at end of file