diff --git a/Orchestrator/pyOpenRPA.Orchestrator_x64.cmd b/Orchestrator/pyOpenRPA.Orchestrator_x64.cmd index ddb14a81..b6773cdf 100644 --- a/Orchestrator/pyOpenRPA.Orchestrator_x64.cmd +++ b/Orchestrator/pyOpenRPA.Orchestrator_x64.cmd @@ -1,4 +1,3 @@ cd %~dp0 copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\pyOpenRPA_Orchestrator.exe -.\..\Resources\WPy64-3720\python-3.7.2.amd64\pyOpenRPA_Orchestrator.exe "OrchestratorSettings.py" -pause >nul \ No newline at end of file +.\..\Resources\WPy64-3720\python-3.7.2.amd64\pyOpenRPA_Orchestrator.exe "OrchestratorSettings.py" \ No newline at end of file diff --git a/Orchestrator/pyOpenRPA.Orchestrator_x64_administrator.lnk b/Orchestrator/pyOpenRPA.Orchestrator_x64_administrator.lnk deleted file mode 100644 index 403e7635..00000000 Binary files a/Orchestrator/pyOpenRPA.Orchestrator_x64_administrator.lnk and /dev/null differ diff --git a/Orchestrator/pyOpenRPA.Orchestrator_x64_administrator_startup.cmd b/Orchestrator/pyOpenRPA.Orchestrator_x64_administrator_startup.cmd deleted file mode 100644 index 2b98f0ea..00000000 --- a/Orchestrator/pyOpenRPA.Orchestrator_x64_administrator_startup.cmd +++ /dev/null @@ -1,2 +0,0 @@ -cd %~dp0 -start pyOpenRPA.Orchestrator_x64_administrator.lnk \ No newline at end of file diff --git a/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py b/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py index eaff0de7..4194ce44 100644 --- a/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py +++ b/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py @@ -1,4 +1,4 @@ -import subprocess, json, psutil, time, os, win32security, sys, base64, logging #Get input argument +import subprocess, json, psutil, time, os, win32security, sys, base64, logging, ctypes #Get input argument from . import Server from . import Timer from . import Processor @@ -807,175 +807,185 @@ from .. import __version__ # Get version from the package # Main def for orchestrator def Orchestrator(inGSettings): lL = inGSettings["Logger"] - - #mGlobalDict = Settings.Settings(sys.argv[1]) - gSettingsDict = inGSettings # Alias for old name in alg - inGSettings["VersionStr"] = __version__ - #Logger alias - lL = gSettingsDict["Logger"] - if lL: lL.info("Link the gSettings in submodules") #Logging - Processor.gSettingsDict = gSettingsDict - Timer.gSettingsDict = gSettingsDict - Timer.Processor.gSettingsDict = gSettingsDict - Server.gSettingsDict = gSettingsDict - Server.ProcessorOld.gSettingsDict = gSettingsDict # Backward compatibility - - # Check _SessionLast_RDPList.json in working directory. if exist - load into gsettings - # GSettings - #"RobotRDPActive": { - # "RDPList": { - if os.path.exists("_SessionLast_RDPList.json"): - lFile = open("_SessionLast_RDPList.json", "r", encoding="utf-8") - lSessionLastRDPList = json.loads(lFile.read()) - lFile.close() # Close the file - os.remove("_SessionLast_RDPList.json") # remove the temp file - gSettingsDict["RobotRDPActive"]["RDPList"]=lSessionLastRDPList # Set the last session dict - if lL: lL.warning(f"RDP Session List was restored from previous Orchestrator session") - - # Init SettingsUpdate defs from file list (after RDP restore) - lSettingsUpdateFilePathList = gSettingsDict.get("OrchestratorStart", {}).get("DefSettingsUpdatePathList",[]) - lSubmoduleFunctionName = "SettingsUpdate" - lSettingsPath = "\\".join(os.path.join(os.getcwd(), __file__).split("\\")[:-1]) - for lModuleFilePathItem in lSettingsUpdateFilePathList: # Import defs with try catch - try: # Try to init - go next if error and log in logger - lModuleName = lModuleFilePathItem[0:-3] - lFileFullPath = os.path.join(lSettingsPath, lModuleFilePathItem) - 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 - getattr(lTechModuleFromSpec, lSubmoduleFunctionName)(gSettingsDict) - except Exception as e: - if lL: lL.exception(f"Error when init .py file in orchestrator '{lModuleFilePathItem}'. Exception is below:") - - # Turn on backward compatibility - BackwardCompatibility.Update(inGSettings= gSettingsDict) - - # Init the log dump to WEB - #import pdb; pdb.set_trace() - ############################################ - if len(lL.handlers)== 0: - mRobotLoggerFormatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') - else: - mRobotLoggerFormatter = lL.handlers[0].formatter - mHandlerDumpLogList = LoggerHandlerDumpLogList.LoggerHandlerDumpLogList(inDict=inGSettings["Client"], - inKeyStr="DumpLogList", - inHashKeyStr="DumpLogListHashStr", - inRowCountInt=inGSettings["Client"]["DumpLogListCountInt"]) - mHandlerDumpLogList.setFormatter(mRobotLoggerFormatter) - lL.addHandler(mHandlerDumpLogList) - - # Append Orchestrator def to ProcessorDictAlias - lModule = sys.modules[__name__] - lModuleDefList = dir(lModule) - for lItemDefNameStr in lModuleDefList: - # Dont append alias for defs Orchestrator and ___deprecated_orchestrator_start__ - if lItemDefNameStr not in ["Orchestrator", "___deprecated_orchestrator_start__"]: - lItemDef = getattr(lModule,lItemDefNameStr) - if callable(lItemDef): inGSettings["ProcessorDict"]["AliasDefDict"][lItemDefNameStr]=lItemDef - - #Инициализация настроечных параметров - lDaemonLoopSeconds=gSettingsDict["SchedulerDict"]["CheckIntervalSecFloat"] - lDaemonActivityLogDict={} #Словарь отработанных активностей, ключ - кортеж (, , , ) - lDaemonLastDateTime=datetime.datetime.now() - gSettingsDict["ServerDict"]["WorkingDirectoryPathStr"] = os.getcwd() # Set working directory in g settings - - #Инициализация сервера - lThreadServer = Server.RobotDaemonServer("ServerThread", gSettingsDict) - lThreadServer.start() - if lL: lL.info("Web server has been started") #Logging - - # Init the RobotScreenActive in another thread - lRobotScreenActiveThread = threading.Thread(target= Monitor.CheckScreen) - lRobotScreenActiveThread.daemon = True # Run the thread in daemon mode. - lRobotScreenActiveThread.start() # Start the thread execution. - if lL: lL.info("Robot Screen active has been started") #Logging - - # Init the RobotRDPActive in another thread - lRobotRDPActiveThread = threading.Thread(target= RobotRDPActive.RobotRDPActive, kwargs={"inGSettings":gSettingsDict}) - lRobotRDPActiveThread.daemon = True # Run the thread in daemon mode. - lRobotRDPActiveThread.start() # Start the thread execution. - if lL: lL.info("Robot RDP active has been started") #Logging - - # Init autocleaner in another thread - lAutocleanerThread = threading.Thread(target= GSettingsAutocleaner, kwargs={"inGSettings":gSettingsDict}) - lAutocleanerThread.daemon = True # Run the thread in daemon mode. - lAutocleanerThread.start() # Start the thread execution. - if lL: lL.info("Autocleaner thread has been started") #Logging - - # Orchestrator start activity - if lL: lL.info("Orchestrator start activity run") #Logging - for lActivityItem in gSettingsDict["OrchestratorStart"]["ActivityList"]: - Processor.ActivityListOrDict(lActivityItem) - - # Processor thread - lProcessorThread = threading.Thread(target= Processor.ProcessorRunSync, kwargs={"inGSettings":gSettingsDict}) - lProcessorThread.daemon = True # Run the thread in daemon mode. - lProcessorThread.start() # Start the thread execution. - if lL: lL.info("Processor has been started (ProcessorDict)") #Logging - - - if lL: lL.info("Scheduler loop start") #Logging - gDaemonActivityLogDictRefreshSecInt = 10 # The second period for clear lDaemonActivityLogDict from old items - gDaemonActivityLogDictLastTime = time.time() # The second perioad for clean lDaemonActivityLogDict from old items - while True: + # https://stackoverflow.com/questions/130763/request-uac-elevation-from-within-a-python-script + def is_admin(): try: - lCurrentDateTime = datetime.datetime.now() - #Циклический обход правил - lFlagSearchActivityType=True - # Periodically clear the lDaemonActivityLogDict - if time.time()-gDaemonActivityLogDictLastTime>=gDaemonActivityLogDictRefreshSecInt: - gDaemonActivityLogDictLastTime = time.time() # Update the time - for lIndex, lItem in enumerate(lDaemonActivityLogDict): - if lItem["ActivityEndDateTime"] and lCurrentDateTime<=lItem["ActivityEndDateTime"]: - pass - # Activity is actual - do not delete now - else: - # remove the activity - not actual - lDaemonActivityLogDict.pop(lIndex,None) - lIterationLastDateTime = lDaemonLastDateTime # Get current datetime before iterator (need for iterate all activities in loop) - # Iterate throught the activity list - for lIndex, lItem in enumerate(gSettingsDict["SchedulerDict"]["ActivityTimeList"]): - try: - # Prepare GUID of the activity - lGUID = None - if "GUID" in lItem and lItem["GUID"]: - lGUID = lItem["GUID"] - else: - lGUID = str(uuid.uuid4()) - lItem["GUID"]=lGUID - - #Проверка дней недели, в рамках которых можно запускать активность - lItemWeekdayList=lItem.get("WeekdayList", [0, 1, 2, 3, 4, 5, 6]) - if lCurrentDateTime.weekday() in lItemWeekdayList: - if lFlagSearchActivityType: - ####################################################################### - #Branch 1 - if has TimeHH:MM - ####################################################################### - if "TimeHH:MM" in lItem: - #Вид активности - запуск процесса - #Сформировать временной штамп, относительно которого надо будет проверять время - #часовой пояс пока не учитываем - lActivityDateTime=datetime.datetime.strptime(lItem["TimeHH:MM"],"%H:%M") - lActivityDateTime=lActivityDateTime.replace(year=lCurrentDateTime.year,month=lCurrentDateTime.month,day=lCurrentDateTime.day) - #Убедиться в том, что время наступило - if ( - lActivityDateTime>=lDaemonLastDateTime and - lCurrentDateTime>=lActivityDateTime): - # Log info about activity - if lL: lL.info(f"Scheduler:: Activity list is started in new thread. Scheduler item: {lItem}") #Logging - # Do the activity - lThread = threading.Thread(target=Processor.ActivityListExecute, kwargs={"inGSettings": inGSettings, "inActivityList":lItem["ActivityList"]}) - lThread.start() - lIterationLastDateTime = datetime.datetime.now() # Set the new datetime for the new processor activity - except Exception as e: - if lL: lL.exception(f"Scheduler: Exception has been catched in Scheduler module when activity time item was initialising. ActivityTimeItem is {lItem}") - lDaemonLastDateTime = lIterationLastDateTime # Set the new datetime for the new processor activity - #Уснуть до следующего прогона - time.sleep(lDaemonLoopSeconds) - except Exception as e: - if lL: lL.exception(f"Scheduler: Exception has been catched in Scheduler module. Global error") + return ctypes.windll.shell32.IsUserAnAdmin() + except: + return False + if not is_admin(): + # Re-run the program with admin rights + ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1) + else: + # Code of your program here + #mGlobalDict = Settings.Settings(sys.argv[1]) + gSettingsDict = inGSettings # Alias for old name in alg + inGSettings["VersionStr"] = __version__ + #Logger alias + lL = gSettingsDict["Logger"] + if lL: lL.info("Link the gSettings in submodules") #Logging + Processor.gSettingsDict = gSettingsDict + Timer.gSettingsDict = gSettingsDict + Timer.Processor.gSettingsDict = gSettingsDict + Server.gSettingsDict = gSettingsDict + Server.ProcessorOld.gSettingsDict = gSettingsDict # Backward compatibility + + # Check _SessionLast_RDPList.json in working directory. if exist - load into gsettings + # GSettings + #"RobotRDPActive": { + # "RDPList": { + if os.path.exists("_SessionLast_RDPList.json"): + lFile = open("_SessionLast_RDPList.json", "r", encoding="utf-8") + lSessionLastRDPList = json.loads(lFile.read()) + lFile.close() # Close the file + os.remove("_SessionLast_RDPList.json") # remove the temp file + gSettingsDict["RobotRDPActive"]["RDPList"]=lSessionLastRDPList # Set the last session dict + if lL: lL.warning(f"RDP Session List was restored from previous Orchestrator session") + + # Init SettingsUpdate defs from file list (after RDP restore) + lSettingsUpdateFilePathList = gSettingsDict.get("OrchestratorStart", {}).get("DefSettingsUpdatePathList",[]) + lSubmoduleFunctionName = "SettingsUpdate" + lSettingsPath = "\\".join(os.path.join(os.getcwd(), __file__).split("\\")[:-1]) + for lModuleFilePathItem in lSettingsUpdateFilePathList: # Import defs with try catch + try: # Try to init - go next if error and log in logger + lModuleName = lModuleFilePathItem[0:-3] + lFileFullPath = os.path.join(lSettingsPath, lModuleFilePathItem) + 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 + getattr(lTechModuleFromSpec, lSubmoduleFunctionName)(gSettingsDict) + except Exception as e: + if lL: lL.exception(f"Error when init .py file in orchestrator '{lModuleFilePathItem}'. Exception is below:") + + # Turn on backward compatibility + BackwardCompatibility.Update(inGSettings= gSettingsDict) + + # Init the log dump to WEB + #import pdb; pdb.set_trace() + ############################################ + if len(lL.handlers)== 0: + mRobotLoggerFormatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') + else: + mRobotLoggerFormatter = lL.handlers[0].formatter + mHandlerDumpLogList = LoggerHandlerDumpLogList.LoggerHandlerDumpLogList(inDict=inGSettings["Client"], + inKeyStr="DumpLogList", + inHashKeyStr="DumpLogListHashStr", + inRowCountInt=inGSettings["Client"]["DumpLogListCountInt"]) + mHandlerDumpLogList.setFormatter(mRobotLoggerFormatter) + lL.addHandler(mHandlerDumpLogList) + + # Append Orchestrator def to ProcessorDictAlias + lModule = sys.modules[__name__] + lModuleDefList = dir(lModule) + for lItemDefNameStr in lModuleDefList: + # Dont append alias for defs Orchestrator and ___deprecated_orchestrator_start__ + if lItemDefNameStr not in ["Orchestrator", "___deprecated_orchestrator_start__"]: + lItemDef = getattr(lModule,lItemDefNameStr) + if callable(lItemDef): inGSettings["ProcessorDict"]["AliasDefDict"][lItemDefNameStr]=lItemDef + + #Инициализация настроечных параметров + lDaemonLoopSeconds=gSettingsDict["SchedulerDict"]["CheckIntervalSecFloat"] + lDaemonActivityLogDict={} #Словарь отработанных активностей, ключ - кортеж (, , , ) + lDaemonLastDateTime=datetime.datetime.now() + gSettingsDict["ServerDict"]["WorkingDirectoryPathStr"] = os.getcwd() # Set working directory in g settings + + #Инициализация сервера + lThreadServer = Server.RobotDaemonServer("ServerThread", gSettingsDict) + lThreadServer.start() + if lL: lL.info("Web server has been started") #Logging + + # Init the RobotScreenActive in another thread + lRobotScreenActiveThread = threading.Thread(target= Monitor.CheckScreen) + lRobotScreenActiveThread.daemon = True # Run the thread in daemon mode. + lRobotScreenActiveThread.start() # Start the thread execution. + if lL: lL.info("Robot Screen active has been started") #Logging + + # Init the RobotRDPActive in another thread + lRobotRDPActiveThread = threading.Thread(target= RobotRDPActive.RobotRDPActive, kwargs={"inGSettings":gSettingsDict}) + lRobotRDPActiveThread.daemon = True # Run the thread in daemon mode. + lRobotRDPActiveThread.start() # Start the thread execution. + if lL: lL.info("Robot RDP active has been started") #Logging + + # Init autocleaner in another thread + lAutocleanerThread = threading.Thread(target= GSettingsAutocleaner, kwargs={"inGSettings":gSettingsDict}) + lAutocleanerThread.daemon = True # Run the thread in daemon mode. + lAutocleanerThread.start() # Start the thread execution. + if lL: lL.info("Autocleaner thread has been started") #Logging + + # Orchestrator start activity + if lL: lL.info("Orchestrator start activity run") #Logging + for lActivityItem in gSettingsDict["OrchestratorStart"]["ActivityList"]: + Processor.ActivityListOrDict(lActivityItem) + + # Processor thread + lProcessorThread = threading.Thread(target= Processor.ProcessorRunSync, kwargs={"inGSettings":gSettingsDict}) + lProcessorThread.daemon = True # Run the thread in daemon mode. + lProcessorThread.start() # Start the thread execution. + if lL: lL.info("Processor has been started (ProcessorDict)") #Logging + + + if lL: lL.info("Scheduler loop start") #Logging + gDaemonActivityLogDictRefreshSecInt = 10 # The second period for clear lDaemonActivityLogDict from old items + gDaemonActivityLogDictLastTime = time.time() # The second perioad for clean lDaemonActivityLogDict from old items + while True: + try: + lCurrentDateTime = datetime.datetime.now() + #Циклический обход правил + lFlagSearchActivityType=True + # Periodically clear the lDaemonActivityLogDict + if time.time()-gDaemonActivityLogDictLastTime>=gDaemonActivityLogDictRefreshSecInt: + gDaemonActivityLogDictLastTime = time.time() # Update the time + for lIndex, lItem in enumerate(lDaemonActivityLogDict): + if lItem["ActivityEndDateTime"] and lCurrentDateTime<=lItem["ActivityEndDateTime"]: + pass + # Activity is actual - do not delete now + else: + # remove the activity - not actual + lDaemonActivityLogDict.pop(lIndex,None) + lIterationLastDateTime = lDaemonLastDateTime # Get current datetime before iterator (need for iterate all activities in loop) + # Iterate throught the activity list + for lIndex, lItem in enumerate(gSettingsDict["SchedulerDict"]["ActivityTimeList"]): + try: + # Prepare GUID of the activity + lGUID = None + if "GUID" in lItem and lItem["GUID"]: + lGUID = lItem["GUID"] + else: + lGUID = str(uuid.uuid4()) + lItem["GUID"]=lGUID + + #Проверка дней недели, в рамках которых можно запускать активность + lItemWeekdayList=lItem.get("WeekdayList", [0, 1, 2, 3, 4, 5, 6]) + if lCurrentDateTime.weekday() in lItemWeekdayList: + if lFlagSearchActivityType: + ####################################################################### + #Branch 1 - if has TimeHH:MM + ####################################################################### + if "TimeHH:MM" in lItem: + #Вид активности - запуск процесса + #Сформировать временной штамп, относительно которого надо будет проверять время + #часовой пояс пока не учитываем + lActivityDateTime=datetime.datetime.strptime(lItem["TimeHH:MM"],"%H:%M") + lActivityDateTime=lActivityDateTime.replace(year=lCurrentDateTime.year,month=lCurrentDateTime.month,day=lCurrentDateTime.day) + #Убедиться в том, что время наступило + if ( + lActivityDateTime>=lDaemonLastDateTime and + lCurrentDateTime>=lActivityDateTime): + # Log info about activity + if lL: lL.info(f"Scheduler:: Activity list is started in new thread. Scheduler item: {lItem}") #Logging + # Do the activity + lThread = threading.Thread(target=Processor.ActivityListExecute, kwargs={"inGSettings": inGSettings, "inActivityList":lItem["ActivityList"]}) + lThread.start() + lIterationLastDateTime = datetime.datetime.now() # Set the new datetime for the new processor activity + except Exception as e: + if lL: lL.exception(f"Scheduler: Exception has been catched in Scheduler module when activity time item was initialising. ActivityTimeItem is {lItem}") + lDaemonLastDateTime = lIterationLastDateTime # Set the new datetime for the new processor activity + #Уснуть до следующего прогона + time.sleep(lDaemonLoopSeconds) + except Exception as e: + if lL: lL.exception(f"Scheduler: Exception has been catched in Scheduler module. Global error") # Backward compatibility below to 1.2.0 def __deprecated_orchestrator_start__():