From 16eb821b0d4dc7847a8b3d34615ab3466adc58e0 Mon Sep 17 00:00:00 2001 From: Ivan Maslov Date: Thu, 10 Feb 2022 18:18:03 +0300 Subject: [PATCH] Orc: turn off the clipboard in RDP, AgentFileRecieve check if file not exists, ProcessorMonitorFix - print info about current item. New def to work with files in agent --- Sources/pyOpenRPA/Agent/__Agent__.py | 57 +++++++++++++----- .../Orchestrator/BackwardCompatibility.py | 5 ++ Sources/pyOpenRPA/Orchestrator/Processor.py | 6 +- .../Orchestrator/RobotRDPActive/Connector.py | 4 +- .../Orchestrator/RobotRDPActive/Processor.py | 3 +- .../Orchestrator/RobotRDPActive/Template.rdp | Bin 2426 -> 2462 bytes .../pyOpenRPA/Orchestrator/ServerSettings.py | 9 ++- .../Orchestrator/SettingsTemplate.py | 1 + .../Orchestrator/__Orchestrator__.py | 52 ++++++++++++++-- 9 files changed, 112 insertions(+), 25 deletions(-) diff --git a/Sources/pyOpenRPA/Agent/__Agent__.py b/Sources/pyOpenRPA/Agent/__Agent__.py index 68e3505c..4656b138 100644 --- a/Sources/pyOpenRPA/Agent/__Agent__.py +++ b/Sources/pyOpenRPA/Agent/__Agent__.py @@ -2,6 +2,9 @@ import threading, socket, getpass, sys, uuid, subprocess, base64, psutil, getpas from . import O2A, A2O # Data flow Orchestrator To Agent from . import Processor # Processor Queue from subprocess import CREATE_NEW_CONSOLE # Flag to create new process in another CMD +import os + +gSettings = None # Create binary file by the base64 string (safe for JSON transmition) def OSFileBinaryDataBase64StrCreate(inFilePathStr, inFileDataBase64Str,inGSettings = None): @@ -58,16 +61,37 @@ def OSFileBinaryDataBase64StrReceive(inFilePathStr, inGSettings=None): :param inGSettings: global settings of the Agent (singleton) :return: File content in string base64 format (use base64.b64decode to decode data). Return None if file is not exist """ - lFile = open(inFilePathStr, "rb") - lFileDataBytes = lFile.read() - lFile.close() - lFileDataBase64Str = base64.b64encode(lFileDataBytes).decode("utf-8") lL = inGSettings.get("Logger", None) if type(inGSettings) is dict else None - lMessageStr = f"AGENT Binary file {inFilePathStr} has been read." - if lL: lL.info(lMessageStr) - A2O.LogListSend(inGSettings=inGSettings, inLogList=[lMessageStr]) + lFileDataBase64Str = None + if os.path.exists(inFilePathStr): + lFile = open(inFilePathStr, "rb") + lFileDataBytes = lFile.read() + lFile.close() + lFileDataBase64Str = base64.b64encode(lFileDataBytes).decode("utf-8") + lMessageStr = f"OSFileBinaryDataBase64StrReceive: file {inFilePathStr} has been read" + if lL: lL.debug(lMessageStr) + #A2O.LogListSend(inGSettings=inGSettings, inLogList=[lMessageStr]) + else: + if lL: lL.debug(f"OSFileBinaryDataBase64StrReceive: file {inFilePathStr} is not exists - return None") return lFileDataBase64Str +def OSFileMTimeGet(inFilePathStr: str) -> float or None: + """ + Read file modification time timestamp format (float) + + :param inFilePathStr: File path to read + :return: timestamp (float) Return None if file is not exist + """ + global gSettings + lL = gSettings.get("Logger", None) if type(gSettings) is dict else None + lFileMTimeFloat = None + if os.path.exists(inFilePathStr): + lFileMTimeFloat = os.path.getmtime(inFilePathStr) + if lL: lL.debug(f"OSFileMTimeGet: file {inFilePathStr} has been read") + else: + if lL: lL.debug(f"OSFileMTimeGet: file {inFilePathStr} is not exists - return None") + return lFileMTimeFloat + def OSFileTextDataStrReceive(inFilePathStr, inEncodingStr="utf-8", inGSettings=None): """ Read text file in the agent GUI session @@ -77,13 +101,17 @@ def OSFileTextDataStrReceive(inFilePathStr, inEncodingStr="utf-8", inGSettings=N :param inGSettings: global settings of the Agent (singleton) :return: File text content in string format (use base64.b64decode to decode data). Return None if file is not exist """ - lFile = open(inFilePathStr, "r", encoding=inEncodingStr) - lFileDataStr = lFile.read() - lFile.close() + lFileDataStr = None lL = inGSettings.get("Logger", None) if type(inGSettings) is dict else None - lMessageStr = f"AGENT Text file {inFilePathStr} has been read." - if lL: lL.info(lMessageStr) - A2O.LogListSend(inGSettings=inGSettings, inLogList=[lMessageStr]) + if os.path.exists(inFilePathStr): + lFile = open(inFilePathStr, "r", encoding=inEncodingStr) + lFileDataStr = lFile.read() + lFile.close() + lMessageStr = f"OSFileTextDataStrReceive: file {inFilePathStr} has been read" + if lL: lL.info(lMessageStr) + #A2O.LogListSend(inGSettings=inGSettings, inLogList=[lMessageStr]) + else: + if lL: lL.info(f"OSFileTextDataStrReceive: file {inFilePathStr} is not exists - return None") return lFileDataStr # Send CMD to OS. Result return to log + Orchestrator by the A2O connection @@ -185,7 +213,8 @@ def ProcessWOExeUpperUserListGet(): # Main def def Agent(inGSettings): lL = inGSettings["Logger"] - + global gSettings + gSettings = inGSettings # Append Orchestrator def to ProcessorDictAlias lModule = sys.modules[__name__] lModuleDefList = dir(lModule) diff --git a/Sources/pyOpenRPA/Orchestrator/BackwardCompatibility.py b/Sources/pyOpenRPA/Orchestrator/BackwardCompatibility.py index b503c5da..7abd8a37 100644 --- a/Sources/pyOpenRPA/Orchestrator/BackwardCompatibility.py +++ b/Sources/pyOpenRPA/Orchestrator/BackwardCompatibility.py @@ -521,3 +521,8 @@ def Update(inGSettings): inGSettings["ManagersGitDict"]={} if lL: lL.warning( f"Backward compatibility (v1.2.4 to v1.2.7): Create new key: ManagersGitDict") # Log about compatibility + # ProcessorDict > ActivityItemNowDict + if "ActivityItemNowDict" not in inGSettings["ProcessorDict"]: + inGSettings["ProcessorDict"]["ActivityItemNowDict"]=None + if lL: lL.warning( + f"Backward compatibility (v1.2.4 to v1.2.7): Create new key: ProcessorDict > ActivityItemNowDict") # Log about compatibility diff --git a/Sources/pyOpenRPA/Orchestrator/Processor.py b/Sources/pyOpenRPA/Orchestrator/Processor.py index fd797a16..62e07788 100644 --- a/Sources/pyOpenRPA/Orchestrator/Processor.py +++ b/Sources/pyOpenRPA/Orchestrator/Processor.py @@ -29,7 +29,9 @@ def ProcessorRunSync(inGSettings, inRobotRDPThreadControlDict): lActivityItem = inGSettings["ProcessorDict"]["ActivityList"].pop(0) # Extract the first item from processor queue if lActivityItem.get("ThreadBool", False) is False: inRobotRDPThreadControlDict["ThreadExecuteBool"]=False # Stop the RobotRDPActive monitoring + inGSettings["ProcessorDict"]["ActivityItemNowDict"]=lActivityItem ActivityListExecute(inGSettings = inGSettings, inActivityList = [lActivityItem]) # execute the activity item + inGSettings["ProcessorDict"]["ActivityItemNowDict"]=None inRobotRDPThreadControlDict["ThreadExecuteBool"] = True # Continue the RobotRDPActive monitoring else: ProcessorRunAsync(inGSettings = inGSettings, inActivityList=[lActivityItem]) @@ -139,8 +141,8 @@ def ProcessorMonitorRunSync(inGSettings): lActiveTimeStart = time.time() try: while True: - if len(inGSettings["ProcessorDict"]["ActivityList"])>0: - lItemDict = inGSettings["ProcessorDict"]["ActivityList"][0] + if inGSettings["ProcessorDict"]["ActivityItemNowDict"] is not None: + lItemDict = inGSettings["ProcessorDict"]["ActivityItemNowDict"] if "GUIDStr" not in lItemDict: lGUIDStr = str(uuid.uuid4()) # generate new GUID lItemDict["GUIDStr"] = lGUIDStr diff --git a/Sources/pyOpenRPA/Orchestrator/RobotRDPActive/Connector.py b/Sources/pyOpenRPA/Orchestrator/RobotRDPActive/Connector.py index 1142344d..481a111c 100644 --- a/Sources/pyOpenRPA/Orchestrator/RobotRDPActive/Connector.py +++ b/Sources/pyOpenRPA/Orchestrator/RobotRDPActive/Connector.py @@ -79,13 +79,15 @@ def SessionConfigurationCreate(inConfiguration): lDriveStoreDirectStr = "" for lItem in inConfiguration['SharedDriveList']: lDriveStoreDirectStr+=f"{lItem.upper()}:\\;" # Attention - all drives must be only in upper case!!! - #Replace {Width}, {Height}, {BitDepth}, {HostPort}, {Login} + #Replace {Width}, {Height}, {BitDepth}, {HostPort}, {Login} {redirectclipboard} + lRedirectClipboardStr = "1" if inConfiguration.get('RedirectClipboardBool',True) == True else "0" lRDPTemplateFileContent = lRDPTemplateFileContent.replace("{Width}", str(inConfiguration.get('Screen',{}).get("Width",1680))) lRDPTemplateFileContent = lRDPTemplateFileContent.replace("{Height}", str(inConfiguration.get('Screen',{}).get("Height",1050))) lRDPTemplateFileContent = lRDPTemplateFileContent.replace("{BitDepth}", inConfiguration.get('Screen',{}).get("DepthBit","32")) lRDPTemplateFileContent = lRDPTemplateFileContent.replace("{HostPort}", lHostPort) lRDPTemplateFileContent = lRDPTemplateFileContent.replace("{Login}", inConfiguration['Login']) lRDPTemplateFileContent = lRDPTemplateFileContent.replace("{SharedDriveList}", lDriveStoreDirectStr) + lRDPTemplateFileContent = lRDPTemplateFileContent.replace("{redirectclipboard}", lRedirectClipboardStr) #Save template to temp file lRDPCurrentFileFullPath = os.path.join(tempfile.gettempdir(), f"{uuid.uuid4().hex}.rdp") open(lRDPCurrentFileFullPath, "w", encoding="utf-16-le").write(lRDPTemplateFileContent) diff --git a/Sources/pyOpenRPA/Orchestrator/RobotRDPActive/Processor.py b/Sources/pyOpenRPA/Orchestrator/RobotRDPActive/Processor.py index 8ec9a493..bdbb9efe 100644 --- a/Sources/pyOpenRPA/Orchestrator/RobotRDPActive/Processor.py +++ b/Sources/pyOpenRPA/Orchestrator/RobotRDPActive/Processor.py @@ -10,7 +10,7 @@ import psutil gSettings = None # Gsettings will be initialized after the import module # Create new RDPSession in RobotRDPActive -def RDPSessionConnect(inRDPSessionKeyStr, inHostStr, inPortStr, inLoginStr, inPasswordStr): +def RDPSessionConnect(inRDPSessionKeyStr, inHostStr, inPortStr, inLoginStr, inPasswordStr, inRedirectClipboardBool = True): global gSettings # ATTENTION - dont connect if RDP session is exist if inRDPSessionKeyStr not in gSettings["RobotRDPActive"]["RDPList"]: @@ -27,6 +27,7 @@ def RDPSessionConnect(inRDPSessionKeyStr, inHostStr, inPortStr, inLoginStr, inPa "DepthBit": "32" # "32" or "24" or "16" or "15", example "32" }, "SharedDriveList": ["c"], # List of the Root sesion hard drives, example ["c"] + "RedirectClipboardBool": inRedirectClipboardBool, # True - share clipboard to RDP; False - else ###### Will updated in program ############ "SessionHex": "77777sdfsdf77777dsfdfsf77777777", # Hex is created when robot runs, example "" "SessionIsWindowExistBool": False, # Flag if the RDP window is exist, old name "FlagSessionIsActive". Check every n seconds , example False diff --git a/Sources/pyOpenRPA/Orchestrator/RobotRDPActive/Template.rdp b/Sources/pyOpenRPA/Orchestrator/RobotRDPActive/Template.rdp index aeeb6d1ba600945c54a0e897f3bd4a5d3bae368d..0e7ae9af992b186268760ff50b0a2119a33b1068 100644 GIT binary patch delta 24 gcmew*G*5U#5i4W$WKULQ#@fkyS;RMYv3_6%0Buk delta 16 YcmbOy{7Yy<5$og^EMlA2uzq3&06Z%O(*OVf diff --git a/Sources/pyOpenRPA/Orchestrator/ServerSettings.py b/Sources/pyOpenRPA/Orchestrator/ServerSettings.py index 4408c569..35b96a45 100644 --- a/Sources/pyOpenRPA/Orchestrator/ServerSettings.py +++ b/Sources/pyOpenRPA/Orchestrator/ServerSettings.py @@ -424,9 +424,12 @@ def pyOpenRPA_Agent_A2O(inRequest, inGSettings): lActivityReturnItemValue = lInput["ActivityReturnDict"][lActivityReturnItemKeyStr] # Create item in gSettings inGSettings["AgentActivityReturnDict"][lActivityReturnItemKeyStr]=SettingsTemplate.__AgentActivityReturnDictItemCreate__(inReturn=lActivityReturnItemValue) - lLogStr = "0 bytes" - if lActivityReturnItemValue is not None: - lLogStr = f"{len(lActivityReturnItemValue)} bytes" + lLogStr = "x bytes" + try: + if lActivityReturnItemValue is not None: + lLogStr = f"{len(lActivityReturnItemValue)} bytes" + except Exception as e: + pass if lL: lL.debug(f"SERVER: pyOpenRPA_Agent_A2O:: Has recieved result of the activity items from agent! ActivityItem GUID Str: {lActivityReturnItemKeyStr}; Return value len: {lLogStr}") # Delete the source activity item from AgentDict if lAgentDictItemKeyTurple in inGSettings["AgentDict"]: diff --git a/Sources/pyOpenRPA/Orchestrator/SettingsTemplate.py b/Sources/pyOpenRPA/Orchestrator/SettingsTemplate.py index 7999b7fa..6515d697 100644 --- a/Sources/pyOpenRPA/Orchestrator/SettingsTemplate.py +++ b/Sources/pyOpenRPA/Orchestrator/SettingsTemplate.py @@ -163,6 +163,7 @@ def __Create__(): # "GUIDStr": ..., # GUID of the activity # }, ], + "ActivityItemNowDict": None, # Activity Item which is executing now "AliasDefDict": {}, # Storage for def with Str alias. To use it see pyOpenRPA.Orchestrator.ControlPanel "CheckIntervalSecFloat": 1.0, # Interval for check gSettings in ProcessorDict > ActivityList "ExecuteBool": True, # Flag to execute thread processor diff --git a/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py b/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py index 2916dd7a..d9ed17fa 100644 --- a/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py +++ b/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py @@ -348,6 +348,45 @@ def AgentOSFileBinaryDataBase64StrReceive(inHostNameStr, inUserStr, inFilePathSt #Send item in AgentDict for the futher data transmition return AgentActivityItemAdd(inGSettings=inGSettings, inHostNameStr=inHostNameStr, inUserStr=inUserStr, inActivityItemDict=lActivityItemDict) + +def AgentOSFileBinaryDataReceive(inHostNameStr, inUserStr, inFilePathStr): + """ + Read binary file from agent (synchronious) + + :param inGSettings: Global settings dict (singleton) + :param inHostNameStr: + :param inUserStr: + :param inFilePathStr: File path to read + :return: file data bytes + """ + lFileDataBytes = None + inGSettings = GSettingsGet() # Set the global settings + # Check thread + if OrchestratorIsInited() == False: + if inGSettings["Logger"]: inGSettings["Logger"].warning(f"AgentOSFileBinaryDataReceive run before orc init - activity will be append in the processor queue.") + lResult = { + "Def": AgentOSFileBinaryDataReceive, # def link or def alias (look gSettings["Processor"]["AliasDefDict"]) + "ArgList": [], # Args list + "ArgDict": {"inHostNameStr":inHostNameStr, "inUserStr":inUserStr, "inFilePathStr":inFilePathStr}, # Args dictionary + "ArgGSettings": None, # Name of GSettings attribute: str (ArgDict) or index (for ArgList) + "ArgLogger": None # Name of GSettings attribute: str (ArgDict) or index (for ArgList) + } + inGSettings["ProcessorDict"]["ActivityList"].append(lResult) + else: # In processor - do execution + lActivityItemDict = { + "Def":"OSFileBinaryDataBase64StrReceive", # def alias (look pyOpeRPA.Agent gSettings["ProcessorDict"]["AliasDefDict"]) + "ArgList":[], # Args list + "ArgDict":{"inFilePathStr":inFilePathStr}, # Args dictionary + "ArgGSettings": "inGSettings", # Name of GSettings attribute: str (ArgDict) or index (for ArgList) + "ArgLogger": None # Name of GSettings attribute: str (ArgDict) or index (for ArgList) + } + + #Send item in AgentDict for the futher data transmition + lGUIDStr = AgentActivityItemAdd(inGSettings=inGSettings, inHostNameStr=inHostNameStr, inUserStr=inUserStr, inActivityItemDict=lActivityItemDict) + lFileBase64Str = AgentActivityItemReturnGet(inGUIDStr=lGUIDStr) + if lFileBase64Str is not None: lFileDataBytes = base64.b64decode(lFileBase64Str) + return lFileDataBytes + def AgentOSFileTextDataStrReceive(inHostNameStr, inUserStr, inFilePathStr, inEncodingStr="utf-8", inGSettings = None): """ Read text file in the agent GUI session @@ -1987,7 +2026,7 @@ def SchedulerActivityTimeAddWeekly(inTimeHHMMStr="23:55:", inWeekdayList=None, i # # # # # # # # # # # # # # # # # # # # # # # def RDPTemplateCreate(inLoginStr, inPasswordStr, inHostStr="127.0.0.1", inPortInt = 3389, inWidthPXInt = 1680, inHeightPXInt = 1050, - inUseBothMonitorBool = False, inDepthBitInt = 32, inSharedDriveList=None): + inUseBothMonitorBool = False, inDepthBitInt = 32, inSharedDriveList=None, inRedirectClipboardBool=True): """ Create RDP connect dict item/ Use it connect/reconnect (Orchestrator.RDPSessionConnect) @@ -2010,6 +2049,7 @@ def RDPTemplateCreate(inLoginStr, inPasswordStr, inHostStr="127.0.0.1", inPortIn # "Host": "127.0.0.1", "Port": "3389", "Login": "USER_99", "Password": "USER_PASS_HERE", # "Screen": { "Width": 1680, "Height": 1050, "FlagUseAllMonitors": False, "DepthBit": "32" }, # "SharedDriveList": ["c"], + # "RedirectClipboardBool": True, # True - share clipboard to RDP; False - else # ###### Will updated in program ############ # "SessionHex": "77777sdfsdf77777dsfdfsf77777777", # Hex is created when robot runs, example "" # "SessionIsWindowExistBool": False, "SessionIsWindowResponsibleBool": False, "SessionIsIgnoredBool": False @@ -2024,6 +2064,7 @@ def RDPTemplateCreate(inLoginStr, inPasswordStr, inHostStr="127.0.0.1", inPortIn :param inUseBothMonitorBool: True - connect to the RDP with both monitors. False - else case :param inDepthBitInt: Remote desktop bitness. Available: 32 or 24 or 16 or 15, example 32 :param inSharedDriveList: Host local disc to connect to the RDP session. Example: ["c", "d"] + :param inRedirectClipboardBool: # True - share clipboard to RDP; False - else :return: { "Host": inHostStr, # Host address, example "77.77.22.22" @@ -2038,6 +2079,7 @@ def RDPTemplateCreate(inLoginStr, inPasswordStr, inHostStr="127.0.0.1", inPortIn "DepthBit": str(inDepthBitInt) # "32" or "24" or "16" or "15", example "32" }, "SharedDriveList": inSharedDriveList, # List of the Root sesion hard drives, example ["c"] + "RedirectClipboardBool": True, # True - share clipboard to RDP; False - else ###### Will updated in program ############ "SessionHex": "77777sdfsdf77777dsfdfsf77777777", # Hex is created when robot runs, example "" "SessionIsWindowExistBool": False, @@ -2050,6 +2092,7 @@ def RDPTemplateCreate(inLoginStr, inPasswordStr, inHostStr="127.0.0.1", inPortIn """ if inSharedDriveList is None: inSharedDriveList = ["c"] if inPortInt is None: inPortInt = 3389 + if inRedirectClipboardBool is None: inRedirectClipboardBool = True lRDPTemplateDict= { # Init the configuration item "Host": inHostStr, # Host address, example "77.77.22.22" "Port": str(inPortInt), # RDP Port, example "3389" @@ -2062,7 +2105,8 @@ def RDPTemplateCreate(inLoginStr, inPasswordStr, inHostStr="127.0.0.1", inPortIn "FlagUseAllMonitors": inUseBothMonitorBool, # True or False, example False "DepthBit": str(inDepthBitInt) # "32" or "24" or "16" or "15", example "32" }, - "SharedDriveList": inSharedDriveList, # List of the Root sesion hard drives, example ["c"] + "SharedDriveList": inSharedDriveList, # List of the Root sesion hard drives, example ["c"], + "RedirectClipboardBool": inRedirectClipboardBool, # True - share clipboard to RDP; False - else ###### Will updated in program ############ "SessionHex": "77777sdfsdf77777dsfdfsf77777777", # Hex is created when robot runs, example "" "SessionIsWindowExistBool": False, @@ -2087,7 +2131,7 @@ def RDPSessionDublicatesResolve(inGSettings): #for lItemKeyStr in inGSettings["RobotRDPActive"]["RDPList"]: # lItemDict = inGSettings["RobotRDPActive"]["RDPList"][lItemKeyStr] -def RDPSessionConnect(inRDPSessionKeyStr, inRDPTemplateDict=None, inHostStr=None, inPortStr=None, inLoginStr=None, inPasswordStr=None, inGSettings = None): +def RDPSessionConnect(inRDPSessionKeyStr, inRDPTemplateDict=None, inHostStr=None, inPortStr=None, inLoginStr=None, inPasswordStr=None, inGSettings = None, inRedirectClipboardBool=True): """ Create new RDPSession in RobotRDPActive. Attention - activity will be ignored if RDP key is already exists 2 way of the use @@ -2137,7 +2181,7 @@ def RDPSessionConnect(inRDPSessionKeyStr, inRDPTemplateDict=None, inHostStr=None # Var 2 - backward compatibility if lRDPConfigurationItem is None: lRDPConfigurationItem = RDPTemplateCreate(inLoginStr=inLoginStr, inPasswordStr=inPasswordStr, - inHostStr=inHostStr, inPortInt = int(inPortStr)) # ATTENTION - dont connect if RDP session is exist + inHostStr=inHostStr, inPortInt = int(inPortStr), inRedirectClipboardBool=inRedirectClipboardBool) # ATTENTION - dont connect if RDP session is exist # Start the connect if inRDPSessionKeyStr not in inGSettings["RobotRDPActive"]["RDPList"]: inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr] = lRDPConfigurationItem # Add item in RDPList