From 7a6e961aa87e8a9fbc8afcb60237510a69be0f20 Mon Sep 17 00:00:00 2001 From: Ivan Maslov Date: Sat, 5 Dec 2020 21:15:37 +0300 Subject: [PATCH] - Create pyOpenRPA.Agent - just prototype, need test - Create Agent support in Orchestrator (/pyOpenRPA/Agent/O2A and /pyOpenRPA/Agent/A2O) Just writed prototype - start to test --- Agent/AgentDaemonX64.cmd | 4 ++ Agent/AgentSettings.py | 55 +++++++++++++++++++ Sources/pyOpenRPA/Agent/A2O.py | 28 ++++++++++ Sources/pyOpenRPA/Agent/Agent.py | 15 +++++ Sources/pyOpenRPA/Agent/O2A.py | 22 ++++++++ Sources/pyOpenRPA/Agent/__init__.py | 0 .../pyOpenRPA/Orchestrator/ServerSettings.py | 21 +++++++ changelog.md | 2 + 8 files changed, 147 insertions(+) create mode 100644 Agent/AgentDaemonX64.cmd create mode 100644 Agent/AgentSettings.py create mode 100644 Sources/pyOpenRPA/Agent/A2O.py create mode 100644 Sources/pyOpenRPA/Agent/Agent.py create mode 100644 Sources/pyOpenRPA/Agent/O2A.py create mode 100644 Sources/pyOpenRPA/Agent/__init__.py diff --git a/Agent/AgentDaemonX64.cmd b/Agent/AgentDaemonX64.cmd new file mode 100644 index 00000000..b84d5675 --- /dev/null +++ b/Agent/AgentDaemonX64.cmd @@ -0,0 +1,4 @@ +cd %~dp0 +copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\pyOpenRPA_Agent.exe +.\..\Resources\WPy64-3720\python-3.7.2.amd64\pyOpenRPA_Agent.exe "AgentSettings.py" +pause >nul \ No newline at end of file diff --git a/Agent/AgentSettings.py b/Agent/AgentSettings.py new file mode 100644 index 00000000..d21b8106 --- /dev/null +++ b/Agent/AgentSettings.py @@ -0,0 +1,55 @@ +import psutil, datetime, logging, os, sys # stdout from logging + +# Config settings +lPyOpenRPASourceFolderPathStr = r"..\Sources" # Path for test pyOpenRPA package + +# Operations +if lPyOpenRPASourceFolderPathStr != "": sys.path.insert(0,os.path.abspath(os.path.join(lPyOpenRPASourceFolderPathStr))) # Path for test pyOpenRPA package + +from pyOpenRPA.Agent import Agent + +if __name__ == "__main__": # New init way + gSettings = { + "OrchestratorDict":{ + "IsHTTPSBool": False, # True - if server is secured HTTPS, False - if server is not secured HTTP + "HostStr":"127.0.0.1", + "PortInt":80, + "SuperTokenStr":"", # Access token to Orchestrator + }, + "O2ADict":{ + "IsOnlineBool": True, # Parameter can be changed when program executes + "RetryTimeoutSecFloat": 10.0, # Retry interval if some error when connect + + }, + "A2ODict": { + "RetryTimeoutSecFloat": 10.0, # Retry interval if some error when connect + }, + "Logger":logging.getLogger("Agent"), + "AgentDict": { # Will be filled automatically + "HostNameUpperStr":None, # Machine hostname + "UserUpperStr": None, # Username string + } + } + + if not os.path.exists("Reports"): + os.makedirs("Reports") + ########################## + # Подготовка логгера Robot + ######################### + lL = gSettings["Logger"] + lL.setLevel(logging.DEBUG) + # create the logging file handler + mRobotLoggerFH = logging.FileHandler( + "Reports\\" + datetime.datetime.now().strftime("%Y_%m_%d") + ".log") + mRobotLoggerFormatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') + mRobotLoggerFH.setFormatter(mRobotLoggerFormatter) + # add handler to logger object + lL.addHandler(mRobotLoggerFH) + ####################Add console output + handler = logging.StreamHandler(sys.stdout) + handler.setFormatter(mRobotLoggerFormatter) + lL.addHandler(handler) + ############################################ + # Call the orchestrator def + Agent.Agent(inGSettings=gSettings) + diff --git a/Sources/pyOpenRPA/Agent/A2O.py b/Sources/pyOpenRPA/Agent/A2O.py new file mode 100644 index 00000000..99f6a336 --- /dev/null +++ b/Sources/pyOpenRPA/Agent/A2O.py @@ -0,0 +1,28 @@ +import requests, time +# A2O - Data flow Agent to Orchestrator + +# f"{lProtocolStr}://{lHostStr}:{lPortInt}/pyOpenRPA/Agent/A2O" +# Request BODY: +# { "HostNameUpperStr": "", "UserUpperStr": "", "LogList":[]} +# Response BODY: +# {} + +# Send logs to orchestrator +def _A2ODataSend(inGSettings, inDataDict): + lL = inGSettings["Logger"] + while inGSettings["A2O"]["IsOnlineBool"]: + # Send request to the orchestrator server + try: + lProtocolStr= "https" if inGSettings["OrchestratorDict"]["IsHTTPSBool"] else "http" + lHostStr = inGSettings["OrchestratorDict"]["HostStr"] + lPortInt = inGSettings["OrchestratorDict"]["PortInt"] + lURLStr=f"{lProtocolStr}://{lHostStr}:{lPortInt}/pyOpenRPA/Agent/A2O" + lResponse = requests.post(url= lURLStr, cookies = {"AuthToken":inGSettings["OrchestratorDict"]["SuperTokenStr"]}, data=inDataDict) + except Exception as e: + if lL: lL.exception(f"A2O Error handler. Sleep for {inGSettings['A2O']['RetryTimeoutSecFloat']} s.") + time.sleep(inGSettings["A2O"]["RetryTimeoutSecFloat"]) + +# Send some logs to orchestrator +def LogListSend(inGSettings, inLogList): + lDataDict = { "HostNameUpperStr": inGSettings["AgentDict"]["HostNameUpperStr"], "UserUpperStr": inGSettings["AgentDict"]["UserUpperStr"], "LogList": inLogList} + _A2ODataSend(inGSettings=inGSettings, inDataDict=lDataDict) \ No newline at end of file diff --git a/Sources/pyOpenRPA/Agent/Agent.py b/Sources/pyOpenRPA/Agent/Agent.py new file mode 100644 index 00000000..a67980f4 --- /dev/null +++ b/Sources/pyOpenRPA/Agent/Agent.py @@ -0,0 +1,15 @@ +import threading, socket, getpass +from . import O2A, A2O # Data flow Orchestrator To Agent + +# Main def +def Agent(inGSettings): + # Detect Machine host name and username + inGSettings["AgentDict"]["HostNameUpperStr"] = socket.gethostname().upper() + inGSettings["AgentDict"]["UserUpperStr"] = getpass.getuser().upper() + + # Start thread to wait data from Orchestrator (O2A) + lO2AThread = threading.Thread(target=O2A.O2A_Loop, kwargs={"inGSettings":inGSettings}) + lO2AThread.start() + + # Send log that Agent has been started + A2O.LogListSend(inGSettings=inGSettings, inLogList=[f'Host: {inGSettings["AgentDict"]["HostNameUpperStr"]}, User: {inGSettings["AgentDict"]["UserUpperStr"]}, Agent has been started.']) \ No newline at end of file diff --git a/Sources/pyOpenRPA/Agent/O2A.py b/Sources/pyOpenRPA/Agent/O2A.py new file mode 100644 index 00000000..937b05bb --- /dev/null +++ b/Sources/pyOpenRPA/Agent/O2A.py @@ -0,0 +1,22 @@ +import requests, time +# O2A - Data flow Orchestrator to Agent + +# f"{lProtocolStr}://{lHostStr}:{lPortInt}/pyOpenRPA/Agent/O2A" +# Request BODY: +# { "HostNameUpperStr": "", "UserUpperStr": "" } +# Response BODY: +# {} + +def O2A_Loop(inGSettings): + lL = inGSettings["Logger"] + while inGSettings["O2A"]["IsOnlineBool"]: + # Send request to the orchestrator server + try: + lProtocolStr= "https" if inGSettings["OrchestratorDict"]["IsHTTPSBool"] else "http" + lHostStr = inGSettings["OrchestratorDict"]["HostStr"] + lPortInt = inGSettings["OrchestratorDict"]["PortInt"] + lURLStr=f"{lProtocolStr}://{lHostStr}:{lPortInt}/pyOpenRPA/Agent/O2A" + lDataDict = { "HostNameUpperStr": inGSettings["AgentDict"]["HostNameUpperStr"], "UserUpperStr": inGSettings["AgentDict"]["UserUpperStr"]} + lResponse = requests.post(url= lURLStr, cookies = {"AuthToken":inGSettings["OrchestratorDict"]["SuperTokenStr"]}, data=lDataDict) + except Exception as e: + time.sleep(inGSettings["O2A"]["RetryTimeoutSecFloat"]) \ No newline at end of file diff --git a/Sources/pyOpenRPA/Agent/__init__.py b/Sources/pyOpenRPA/Agent/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Sources/pyOpenRPA/Orchestrator/ServerSettings.py b/Sources/pyOpenRPA/Orchestrator/ServerSettings.py index fdb1b037..0db93705 100644 --- a/Sources/pyOpenRPA/Orchestrator/ServerSettings.py +++ b/Sources/pyOpenRPA/Orchestrator/ServerSettings.py @@ -189,6 +189,25 @@ def pyOpenRPA_ActivityListExecute(inRequest, inGSettings): lResultList = Processor.ActivityListExecute(inGSettings = inGSettings, inActivityList = [lInput]) inRequest.OpenRPAResponseDict["Body"] = bytes(json.dumps(lResultList[0]), "utf8") +# See docs in Agent (pyOpenRPA.Agent.O2A) +def pyOpenRPA_Agent_O2A(inRequest, inGSettings): + # Test solution + while True: + time.sleep(3) + +# See docs in Agent (pyOpenRPA.Agent.A2O) +def pyOpenRPA_Agent_A2O(inRequest, inGSettings): + # Recieve the data + lValueStr = None + if inRequest.headers.get('Content-Length') is not None: + lInputByteArrayLength = int(inRequest.headers.get('Content-Length')) + lInputByteArray = inRequest.rfile.read(lInputByteArrayLength) + # Превращение массива байт в объект + lInput = json.loads(lInputByteArray.decode('utf8')) + if "LogList" in lInput: + for lLogItemStr in lInput["LogList"]: + inGSettings["Logger"].info(lLogItemStr) + def SettingsUpdate(inGlobalConfiguration): import os import pyOpenRPA.Orchestrator @@ -224,6 +243,8 @@ def SettingsUpdate(inGlobalConfiguration): {"Method": "GET", "URL": "/pyOpenRPA/Screenshot", "MatchType": "BeginWith", "ResponseDefRequestGlobal": pyOpenRPA_Screenshot, "ResponseContentType": "image/png"}, {"Method": "POST", "URL": "/pyOpenRPA/Processor", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_Processor, "ResponseContentType": "application/json"}, {"Method": "POST", "URL": "/pyOpenRPA/ActivityListExecute", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_ActivityListExecute, "ResponseContentType": "application/json"}, + {"Method": "POST", "URL": "/pyOpenRPA/Agent/O2A", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_Agent_O2A, "ResponseContentType": "application/json"}, + {"Method": "POST", "URL": "/pyOpenRPA/Agent/A2O", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_Agent_A2O, "ResponseContentType": "application/json"}, ] inGlobalConfiguration["Server"]["URLList"]=inGlobalConfiguration["Server"]["URLList"]+lURLList return inGlobalConfiguration \ No newline at end of file diff --git a/changelog.md b/changelog.md index e03f7186..a2febb9b 100644 --- a/changelog.md +++ b/changelog.md @@ -49,6 +49,8 @@ - WEB Remove client freeze when back is - Create new pyOpenRPA UAC Client hierarchy SettingsTemplate.__UACClientAdminCreate__ - need to update functionallity - Orchestrator WEB: Update WEB to the new UACClient +- Create pyOpenRPA.Agent - just prototype, need test +- Create Agent support in Orchestrator (/pyOpenRPA/Agent/O2A and /pyOpenRPA/Agent/A2O) [1.1.0] After 2 month test prefinal with new improovements (+RobotRDPActive in Orchestrator + Easy ControlPanelTemplate) Beta before 1.1.0 (new way of OpenRPA with improvements. Sorry, but no backward compatibility)/ Backward compatibility will start from 1.0.1