|
|
import subprocess, json, psutil, time, os, win32security, sys, base64, logging, ctypes #Get input argument
|
|
|
from . import Server
|
|
|
from . import Timer
|
|
|
from . import Processor
|
|
|
from . import BackwardCompatibility # Backward compatibility from v1.1.13
|
|
|
from . import Core
|
|
|
from .Utils import LoggerHandlerDumpLogList
|
|
|
|
|
|
# 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 .RobotRDPActive import CMDStr # Create CMD Strings
|
|
|
from .RobotRDPActive import Connector # RDP API
|
|
|
|
|
|
#from .Settings import Settings
|
|
|
import importlib
|
|
|
from importlib import util
|
|
|
import threading # Multi-threading for RobotRDPActive
|
|
|
from .RobotRDPActive import RobotRDPActive #Start robot rdp active
|
|
|
from .RobotScreenActive import Monitor #Start robot screen active
|
|
|
from . import SettingsTemplate # Settings template
|
|
|
import uuid # Generate uuid
|
|
|
import datetime # datetime
|
|
|
|
|
|
#Единый глобальный словарь (За основу взять из Settings.py)
|
|
|
global gSettingsDict
|
|
|
|
|
|
# AGENT DEFS
|
|
|
|
|
|
def AgentActivityItemAdd(inGSettings, inHostNameStr, inUserStr, inActivityItemDict):
|
|
|
"""
|
|
|
Add activity in AgentDict. Check if item is created
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inHostNameStr: Agent host name
|
|
|
:param inUserStr: User login, where agent is based
|
|
|
:param inActivityItemDict: ActivityItem
|
|
|
:return: None
|
|
|
"""
|
|
|
lAgentDictItemKeyTurple = (inHostNameStr.upper(),inUserStr.upper())
|
|
|
if lAgentDictItemKeyTurple not in inGSettings["AgentDict"]:
|
|
|
inGSettings["AgentDict"][lAgentDictItemKeyTurple] = SettingsTemplate.__AgentDictItemCreate__()
|
|
|
lThisAgentDict = inGSettings["AgentDict"][lAgentDictItemKeyTurple]
|
|
|
lThisAgentDict["ActivityList"].append(inActivityItemDict)
|
|
|
|
|
|
|
|
|
def AgentOSCMD(inGSettings, inHostNameStr, inUserStr, inCMDStr, inRunAsyncBool=True):
|
|
|
"""
|
|
|
Send CMD to OS thought the pyOpenRPA.Agent daemon. Result return to log + Orchestrator by the A2O connection
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inHostNameStr:
|
|
|
:param inUserStr:
|
|
|
:param inCMDStr:
|
|
|
:param inRunAsyncBool:
|
|
|
"""
|
|
|
lActivityItemDict = {
|
|
|
"Def":"OSCMD", # def alias (look pyOpeRPA.Agent gSettings["ProcessorDict"]["AliasDefDict"])
|
|
|
"ArgList":[], # Args list
|
|
|
"ArgDict":{"inCMDStr":inCMDStr,"inRunAsyncBool":inRunAsyncBool}, # 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
|
|
|
AgentActivityItemAdd(inGSettings=inGSettings, inHostNameStr=inHostNameStr, inUserStr=inUserStr, inActivityItemDict=lActivityItemDict)
|
|
|
|
|
|
def AgentOSFileBinaryDataBytesCreate(inGSettings, inHostNameStr, inUserStr, inFilePathStr, inFileDataBytes):
|
|
|
"""
|
|
|
Create binary file by the base64 string by the pyOpenRPA.Agent daemon process (safe for JSON transmition)
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inHostNameStr:
|
|
|
:param inUserStr:
|
|
|
:param inFilePathStr:
|
|
|
:param inFileDataBytes:
|
|
|
"""
|
|
|
|
|
|
lFileDataBase64Str = base64.b64encode(inFileDataBytes).decode("utf-8")
|
|
|
lActivityItemDict = {
|
|
|
"Def":"OSFileBinaryDataBase64StrCreate", # def alias (look pyOpeRPA.Agent gSettings["ProcessorDict"]["AliasDefDict"])
|
|
|
"ArgList":[], # Args list
|
|
|
"ArgDict":{"inFilePathStr":inFilePathStr,"inFileDataBase64Str":lFileDataBase64Str}, # 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
|
|
|
AgentActivityItemAdd(inGSettings=inGSettings, inHostNameStr=inHostNameStr, inUserStr=inUserStr, inActivityItemDict=lActivityItemDict)
|
|
|
|
|
|
|
|
|
def AgentOSFileBinaryDataBase64StrCreate(inGSettings, inHostNameStr, inUserStr, inFilePathStr, inFileDataBase64Str):
|
|
|
"""
|
|
|
Create binary file by the base64 string by the pyOpenRPA.Agent daemon process (safe for JSON transmission)
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inHostNameStr:
|
|
|
:param inUserStr:
|
|
|
:param inFilePathStr:
|
|
|
:param inFileDataBase64Str:
|
|
|
"""
|
|
|
|
|
|
lActivityItemDict = {
|
|
|
"Def":"OSFileBinaryDataBase64StrCreate", # def alias (look pyOpeRPA.Agent gSettings["ProcessorDict"]["AliasDefDict"])
|
|
|
"ArgList":[], # Args list
|
|
|
"ArgDict":{"inFilePathStr":inFilePathStr,"inFileDataBase64Str":inFileDataBase64Str}, # 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
|
|
|
AgentActivityItemAdd(inGSettings=inGSettings, inHostNameStr=inHostNameStr, inUserStr=inUserStr, inActivityItemDict=lActivityItemDict)
|
|
|
|
|
|
# Send text file to Agent (string)
|
|
|
def AgentOSFileTextDataStrCreate(inGSettings, inHostNameStr, inUserStr, inFilePathStr, inFileDataStr, inEncodingStr = "utf-8"):
|
|
|
"""
|
|
|
Create text file by the string by the pyOpenRPA.Agent daemon process
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inHostNameStr:
|
|
|
:param inUserStr:
|
|
|
:param inFilePathStr:
|
|
|
:param inFileDataStr:
|
|
|
:param inEncodingStr:
|
|
|
"""
|
|
|
|
|
|
lActivityItemDict = {
|
|
|
"Def":"OSFileTextDataStrCreate", # def alias (look pyOpeRPA.Agent gSettings["ProcessorDict"]["AliasDefDict"])
|
|
|
"ArgList":[], # Args list
|
|
|
"ArgDict":{"inFilePathStr":inFilePathStr,"inFileDataStr":inFileDataStr, "inEncodingStr": inEncodingStr}, # 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
|
|
|
AgentActivityItemAdd(inGSettings=inGSettings, inHostNameStr=inHostNameStr, inUserStr=inUserStr, inActivityItemDict=lActivityItemDict)
|
|
|
|
|
|
# OS DEFS
|
|
|
def OSCredentialsVerify(inUserStr, inPasswordStr, inDomainStr=""): ##
|
|
|
"""
|
|
|
Verify user credentials in windows. Return bool
|
|
|
|
|
|
:param inUserStr:
|
|
|
:param inPasswordStr:
|
|
|
:param inDomainStr:
|
|
|
:return: True - Credentials are actual; False - Credentials are not actual
|
|
|
"""
|
|
|
try:
|
|
|
hUser = win32security.LogonUser(
|
|
|
inUserStr,inDomainStr, inPasswordStr,
|
|
|
win32security.LOGON32_LOGON_NETWORK, win32security.LOGON32_PROVIDER_DEFAULT
|
|
|
)
|
|
|
except win32security.error:
|
|
|
return False
|
|
|
else:
|
|
|
return True
|
|
|
|
|
|
def OSCMD(inCMDStr, inRunAsyncBool=True, inLogger = None):
|
|
|
"""
|
|
|
OS send command in shell locally
|
|
|
|
|
|
:param inCMDStr:
|
|
|
:param inRunAsyncBool:
|
|
|
:param inLogger:
|
|
|
:return: CMD result string
|
|
|
"""
|
|
|
lResultStr = ""
|
|
|
# Subdef to listen OS result
|
|
|
def _CMDRunAndListenLogs(inCMDStr, inLogger):
|
|
|
lResultStr = ""
|
|
|
lOSCMDKeyStr = str(uuid.uuid4())[0:4].upper()
|
|
|
lCMDProcess = subprocess.Popen(f'cmd /c {inCMDStr}', stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
|
if inLogger:
|
|
|
lListenBool = True
|
|
|
inLogger.info(f"{lOSCMDKeyStr}: # # # # CMD Process has been STARTED # # # # ")
|
|
|
inLogger.info(f"{lOSCMDKeyStr}: {inCMDStr}")
|
|
|
while lListenBool:
|
|
|
lOutputLineBytes = lCMDProcess.stdout.readline()
|
|
|
if lOutputLineBytes == b"":
|
|
|
lListenBool = False
|
|
|
lStr = lOutputLineBytes.decode('cp866')
|
|
|
if lStr.endswith("\n"): lStr = lStr[:-1]
|
|
|
inLogger.info(f"{lOSCMDKeyStr}: {lStr}")
|
|
|
lResultStr+=lStr
|
|
|
inLogger.info(f"{lOSCMDKeyStr}: # # # # CMD Process has been FINISHED # # # # ")
|
|
|
return lResultStr
|
|
|
# New call
|
|
|
if inRunAsyncBool:
|
|
|
lThread = threading.Thread(target=_CMDRunAndListenLogs, kwargs={"inCMDStr":inCMDStr, "inLogger":inLogger})
|
|
|
lThread.start()
|
|
|
lResultStr="ActivityList has been started in async mode - no output is available here."
|
|
|
else:
|
|
|
lResultStr = _CMDRunAndListenLogs(inCMDStr=inCMDStr, inLogger=inLogger)
|
|
|
#lCMDCode = "cmd /c " + inCMDStr
|
|
|
#subprocess.Popen(lCMDCode)
|
|
|
#lResultCMDRun = 1 # os.system(lCMDCode)
|
|
|
return lResultStr
|
|
|
|
|
|
def OrchestratorRestart(inGSettings=None):
|
|
|
"""
|
|
|
Orchestrator restart
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
"""
|
|
|
OrchestratorSessionSave(inGSettings=inGSettings) # Dump RDP List in file json
|
|
|
if inGSettings is not None:
|
|
|
lL = inGSettings["Logger"]
|
|
|
if lL: lL.info(f"Do restart")
|
|
|
# Restart session
|
|
|
os.execl(sys.executable, os.path.abspath(__file__), *sys.argv)
|
|
|
sys.exit(0)
|
|
|
|
|
|
def OrchestratorSessionSave(inGSettings=None):
|
|
|
"""
|
|
|
Orchestrator session save in file _SessionLast_RDPList.json (encoding = "utf-8")
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:return: True every time
|
|
|
"""
|
|
|
# Dump RDP List in file json
|
|
|
lFile = open("_SessionLast_RDPList.json", "w", encoding="utf-8")
|
|
|
lFile.write(json.dumps(inGSettings["RobotRDPActive"]["RDPList"])) # dump json to file
|
|
|
lFile.close() # Close the file
|
|
|
if inGSettings is not None:
|
|
|
lL = inGSettings["Logger"]
|
|
|
if lL: lL.info(
|
|
|
f"Orchestrator has dump the RDP list before the restart.")
|
|
|
return True
|
|
|
|
|
|
def UACKeyListCheck(inRequest, inRoleKeyList) -> bool:
|
|
|
"""
|
|
|
Check is client is has access for the key list
|
|
|
|
|
|
:param inRequest:
|
|
|
:param inRoleKeyList:
|
|
|
:return: bool
|
|
|
"""
|
|
|
return inRequest.UACClientCheck(inRoleKeyList=inRoleKeyList)
|
|
|
|
|
|
def UACUpdate(inGSettings, inADLoginStr, inADStr="", inADIsDefaultBool=True, inURLList=None, inRoleHierarchyAllowedDict=None):
|
|
|
"""
|
|
|
Update user access (UAC)
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inADLoginStr:
|
|
|
:param inADStr:
|
|
|
:param inADIsDefaultBool:
|
|
|
:param inURLList:
|
|
|
:param inRoleHierarchyAllowedDict:
|
|
|
"""
|
|
|
lUserTurple = (inADStr.upper(),inADLoginStr.upper()) # Create turple key for inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"]
|
|
|
if inURLList is None: inURLList = [] # Check if None
|
|
|
if inRoleHierarchyAllowedDict is None: inRoleHierarchyAllowedDict = {} # Check if None
|
|
|
# Get the old URLList
|
|
|
try:
|
|
|
inURLList += inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"][lUserTurple]["MethodMatchURLBeforeList"]
|
|
|
except:
|
|
|
pass
|
|
|
# Check RoleHierarchyAllowedDict in gSettings for the old role hierarchy - include in result.
|
|
|
if lUserTurple in inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"] and "RoleHierarchyAllowedDict" in inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"][lUserTurple]:
|
|
|
lRoleHierarchyAllowedOLDDict = inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"][lUserTurple]["RoleHierarchyAllowedDict"]
|
|
|
Server.__ComplexDictMerge2to1__(in1Dict=inRoleHierarchyAllowedDict, in2Dict=lRoleHierarchyAllowedOLDDict) # Merge dict 2 into dict 1
|
|
|
|
|
|
# Create Access item
|
|
|
lRuleDomainUserDict = {
|
|
|
"MethodMatchURLBeforeList": inURLList,
|
|
|
"RoleHierarchyAllowedDict": inRoleHierarchyAllowedDict
|
|
|
}
|
|
|
# Case add domain + user
|
|
|
inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"].update({(inADStr.upper(),inADLoginStr.upper()):lRuleDomainUserDict})
|
|
|
if inADIsDefaultBool:
|
|
|
# Case add default domain + user
|
|
|
inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"].update({("",inADLoginStr.upper()):lRuleDomainUserDict})
|
|
|
|
|
|
def UACSuperTokenUpdate(inGSettings, inSuperTokenStr):
|
|
|
"""
|
|
|
Add supertoken for the all access (it is need for the robot communication without human)
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inSuperTokenStr:
|
|
|
"""
|
|
|
lLoginStr = "SUPERTOKEN"
|
|
|
UACUpdate(inGSettings=inGSettings, inADLoginStr=lLoginStr)
|
|
|
inGSettings["ServerDict"]["AccessUsers"]["AuthTokensDict"].update(
|
|
|
{inSuperTokenStr:{"User":lLoginStr, "Domain":"", "TokenDatetime": datetime.datetime.now(), "FlagDoNotExpire":True}}
|
|
|
)
|
|
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # #
|
|
|
# OrchestratorWeb defs
|
|
|
# # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
|
|
|
|
|
def WebURLConnectDef(inGSettings, inMethodStr, inURLStr, inMatchTypeStr, inDef, inContentTypeStr="application/octet-stream"):
|
|
|
"""
|
|
|
Connect URL to DEF
|
|
|
"inMethodStr":"GET|POST",
|
|
|
"inURLStr": "/index", #URL of the request
|
|
|
"inMatchTypeStr": "", #"BeginWith|Contains|Equal|EqualCase",
|
|
|
"inContentTypeStr": "", #HTTP Content-type
|
|
|
"inDef": None #Function with str result
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inMethodStr:
|
|
|
:param inURLStr:
|
|
|
:param inMatchTypeStr:
|
|
|
:param inDef:
|
|
|
:param inContentTypeStr:
|
|
|
"""
|
|
|
lURLItemDict = {
|
|
|
"Method": inMethodStr.upper(),
|
|
|
"URL": inURLStr, # URL of the request
|
|
|
"MatchType": inMatchTypeStr, # "BeginWith|Contains|Equal|EqualCase",
|
|
|
# "ResponseFilePath": "", #Absolute or relative path
|
|
|
#"ResponseFolderPath": "C:\Abs\Archive\scopeSrcUL\OpenRPA\Orchestrator\Settings",
|
|
|
# Absolute or relative path
|
|
|
"ResponseContentType": inContentTypeStr, #HTTP Content-type
|
|
|
"ResponseDefRequestGlobal": inDef #Function with str result
|
|
|
}
|
|
|
inGSettings["ServerDict"]["URLList"].append(lURLItemDict)
|
|
|
|
|
|
|
|
|
def WebURLConnectFolder(inGSettings, inMethodStr, inURLStr, inMatchTypeStr, inFolderPathStr):
|
|
|
"""
|
|
|
Connect URL to Folder
|
|
|
"inMethodStr":"GET|POST",
|
|
|
"inURLStr": "/Folder/", #URL of the request
|
|
|
"inMatchTypeStr": "", #"BeginWith|Contains|Equal|EqualCase",
|
|
|
"inFolderPathStr": "", #Absolute or relative path
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inMethodStr:
|
|
|
:param inURLStr:
|
|
|
:param inMatchTypeStr:
|
|
|
:param inFolderPathStr:
|
|
|
"""
|
|
|
# Check if last symbol is "/" - append if not exist
|
|
|
lFolderPathStr = os.path.abspath(inFolderPathStr)
|
|
|
if lFolderPathStr[-1]!="/":lFolderPathStr+="/"
|
|
|
# Prepare URLItem
|
|
|
lURLItemDict = {
|
|
|
"Method": inMethodStr.upper(),
|
|
|
"URL": inURLStr, # URL of the request
|
|
|
"MatchType": inMatchTypeStr, # "BeginWith|Contains|Equal|EqualCase",
|
|
|
# "ResponseFilePath": "", #Absolute or relative path
|
|
|
"ResponseFolderPath": lFolderPathStr, # Absolute or relative path
|
|
|
"ResponseContentType": "application/octet-stream", #HTTP Content-type
|
|
|
#"ResponseDefRequestGlobal": inDef #Function with str result
|
|
|
}
|
|
|
inGSettings["ServerDict"]["URLList"].append(lURLItemDict)
|
|
|
|
|
|
|
|
|
def WebURLConnectFile(inGSettings, inMethodStr, inURLStr, inMatchTypeStr, inFilePathStr, inContentTypeStr="application/octet-stream"):
|
|
|
"""
|
|
|
Connect URL to File
|
|
|
"inMethodStr":"GET|POST",
|
|
|
"inURLStr": "/index", #URL of the request
|
|
|
"inMatchTypeStr": "", #"BeginWith|Contains|Equal|EqualCase",
|
|
|
"inFolderPathStr": "", #Absolute or relative path
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inMethodStr:
|
|
|
:param inURLStr:
|
|
|
:param inMatchTypeStr:
|
|
|
:param inFilePathStr:
|
|
|
:param inContentTypeStr:
|
|
|
"""
|
|
|
lURLItemDict = {
|
|
|
"Method": inMethodStr.upper(),
|
|
|
"URL": inURLStr, # URL of the request
|
|
|
"MatchType": inMatchTypeStr, # "BeginWith|Contains|Equal|EqualCase",
|
|
|
"ResponseFilePath": os.path.abspath(inFilePathStr), #Absolute or relative path
|
|
|
#"ResponseFolderPath": os.path.abspath(inFilePathStr), # Absolute or relative path
|
|
|
"ResponseContentType": inContentTypeStr, #HTTP Content-type
|
|
|
#"ResponseDefRequestGlobal": inDef #Function with str result
|
|
|
}
|
|
|
inGSettings["ServerDict"]["URLList"].append(lURLItemDict)
|
|
|
|
|
|
def WebCPUpdate(inGSettings, inCPKeyStr, inHTMLRenderDef=None, inJSONGeneratorDef=None, inJSInitGeneratorDef=None):
|
|
|
"""
|
|
|
Add control panel HTML, JSON generator or JS when page init
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inCPKeyStr:
|
|
|
:param inHTMLRenderDef:
|
|
|
:param inJSONGeneratorDef:
|
|
|
:param inJSInitGeneratorDef:
|
|
|
"""
|
|
|
# Create Struct if the re is current key
|
|
|
if inCPKeyStr not in inGSettings["CPDict"]:
|
|
|
inGSettings["CPDict"][inCPKeyStr] = {"HTMLRenderDef": None,"JSONGeneratorDef": None, "JSInitGeneratorDef": None}
|
|
|
# CASE HTMLRender
|
|
|
if inHTMLRenderDef is not None:
|
|
|
inGSettings["CPDict"][inCPKeyStr]["HTMLRenderDef"]=inHTMLRenderDef
|
|
|
# CASE JSONGenerator
|
|
|
if inJSONGeneratorDef is not None:
|
|
|
inGSettings["CPDict"][inCPKeyStr]["JSONGeneratorDef"] = inJSONGeneratorDef
|
|
|
# CASE JSInitGeneratorDef
|
|
|
if inJSInitGeneratorDef is not None:
|
|
|
inGSettings["CPDict"][inCPKeyStr]["JSInitGeneratorDef"] = inJSInitGeneratorDef
|
|
|
|
|
|
def WebUserInfoGet(inRequest):
|
|
|
"""
|
|
|
Return User info about request
|
|
|
|
|
|
:param inRequest:
|
|
|
:return: {"DomainUpperStr": "", "UserNameUpperStr": ""}
|
|
|
"""
|
|
|
lDomainUpperStr = inRequest.OpenRPA["Domain"].upper()
|
|
|
lUserUpperStr = inRequest.OpenRPA["User"].upper()
|
|
|
return {"DomainUpperStr": lDomainUpperStr, "UserNameUpperStr": lUserUpperStr}
|
|
|
|
|
|
def WebUserIsSuperToken(inRequest, inGSettings):
|
|
|
"""
|
|
|
Return bool if request is authentificated with supetoken (token which is never expires)
|
|
|
|
|
|
:param inRequest:
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:return: bool True - is supertoken; False - is not supertoken
|
|
|
"""
|
|
|
lIsSuperTokenBool = False
|
|
|
# Get Flag is supertoken (True|False)
|
|
|
lIsSuperTokenBool = inGSettings.get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}).get(inRequest.OpenRPA["AuthToken"], {}).get("FlagDoNotExpire", False)
|
|
|
return lIsSuperTokenBool
|
|
|
|
|
|
def WebUserUACHierarchyGet(inRequest):
|
|
|
"""
|
|
|
Return User UAC Hierarchy DICT Return {...}
|
|
|
|
|
|
:param inRequest:
|
|
|
:return: UAC Dict {}
|
|
|
"""
|
|
|
return inRequest.UserRoleHierarchyGet()
|
|
|
|
|
|
## GSettings defs
|
|
|
def GSettingsKeyListValueSet(inGSettings, inValue, inKeyList=None):
|
|
|
"""
|
|
|
Set value in GSettings by the key list
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inValue:
|
|
|
:param inKeyList:
|
|
|
:return: bool
|
|
|
"""
|
|
|
if inKeyList is None: inKeyList = []
|
|
|
lDict = inGSettings
|
|
|
for lItem2 in inKeyList[:-1]:
|
|
|
#Check if key - value exists
|
|
|
if lItem2 in lDict:
|
|
|
pass
|
|
|
else:
|
|
|
lDict[lItem2]={}
|
|
|
lDict=lDict[lItem2]
|
|
|
lDict[inKeyList[-1]] = inValue #Set value
|
|
|
return True
|
|
|
|
|
|
def GSettingsKeyListValueGet(inGSettings, inKeyList=None):
|
|
|
"""
|
|
|
Get the value from the GSettings by the key list
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inKeyList:
|
|
|
:return: value any type
|
|
|
"""
|
|
|
if inKeyList is None: inKeyList = []
|
|
|
lDict = inGSettings
|
|
|
for lItem2 in inKeyList[:-1]:
|
|
|
#Check if key - value exists
|
|
|
if lItem2 in lDict:
|
|
|
pass
|
|
|
else:
|
|
|
lDict[lItem2]={}
|
|
|
lDict=lDict[lItem2]
|
|
|
return lDict.get(inKeyList[-1],None)
|
|
|
|
|
|
def GSettingsKeyListValueAppend(inGSettings, inValue, inKeyList=None):
|
|
|
"""
|
|
|
Append value in GSettings by the key list
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
Orchestrator.GSettingsKeyListValueAppend(
|
|
|
inGSettings = gSettings,
|
|
|
inValue = "NewValue",
|
|
|
inKeyList=["NewKeyDict","NewKeyList"]):
|
|
|
# result inGSettings: {
|
|
|
# ... another keys in gSettings ...,
|
|
|
# "NewKeyDict":{
|
|
|
# "NewKeyList":[
|
|
|
# "NewValue"
|
|
|
# ]
|
|
|
# }
|
|
|
#}
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inValue: Any value to be appended in gSettings Dict by the key list
|
|
|
:param inKeyList: List of the nested keys (see example)
|
|
|
:return: True every time
|
|
|
"""
|
|
|
if inKeyList is None: inKeyList = []
|
|
|
lDict = inGSettings
|
|
|
for lItem2 in inKeyList[:-1]:
|
|
|
#Check if key - value exists
|
|
|
if lItem2 in lDict:
|
|
|
pass
|
|
|
else:
|
|
|
lDict[lItem2]={}
|
|
|
lDict=lDict[lItem2]
|
|
|
lDict[inKeyList[-1]].append(inValue) #Set value
|
|
|
return True
|
|
|
|
|
|
def GSettingsKeyListValueOperatorPlus(inGSettings, inValue, inKeyList=None):
|
|
|
"""
|
|
|
Execute plus operation between 2 lists (1:inValue and 2:gSettings by the inKeyList)
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
Orchestrator.GSettingsKeyListValueOperatorPlus(
|
|
|
inGSettings = gSettings,
|
|
|
inValue = [1,2,3],
|
|
|
inKeyList=["NewKeyDict","NewKeyList"]):
|
|
|
# result inGSettings: {
|
|
|
# ... another keys in gSettings ...,
|
|
|
# "NewKeyDict":{
|
|
|
# "NewKeyList":[
|
|
|
# "NewValue",
|
|
|
# 1,
|
|
|
# 2,
|
|
|
# 3
|
|
|
# ]
|
|
|
# }
|
|
|
#}
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inValue: List with values to be merged with list in gSettings
|
|
|
:param inKeyList: List of the nested keys (see example)
|
|
|
:return: True every time
|
|
|
"""
|
|
|
if inKeyList is None: inKeyList = []
|
|
|
lDict = inGSettings
|
|
|
for lItem2 in inKeyList[:-1]:
|
|
|
#Check if key - value exists
|
|
|
if lItem2 in lDict:
|
|
|
pass
|
|
|
else:
|
|
|
lDict[lItem2]={}
|
|
|
lDict=lDict[lItem2]
|
|
|
lDict[inKeyList[-1]] += inValue #Set value
|
|
|
return True
|
|
|
|
|
|
def ProcessorAliasDefCreate(inGSettings, inDef, inAliasStr=None):
|
|
|
"""
|
|
|
Create alias for def (can be used in ActivityItem in field Def)
|
|
|
!WHEN DEF ALIAS IS REQUIRED! - Def alias is required when you try to call Python def from the Orchestrator WEB side (because you can't transmit Python def object out of the Python environment)
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
def TestDef():
|
|
|
pass
|
|
|
lAliasStr = Orchestrator.ProcessorAliasDefCreate(
|
|
|
inGSettings = gSettings,
|
|
|
inDef = TestDef,
|
|
|
inAliasStr="TestDefAlias")
|
|
|
# Now you can call TestDef by the alias from var lAliasStr with help of ActivityItem (key Def = lAliasStr)
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inDef: Def
|
|
|
:param inAliasStr: String alias for associated def
|
|
|
:return: str Alias string (Alias can be regenerated if previous alias was occupied)
|
|
|
"""
|
|
|
#TODO Pay attention - New alias can be used too - need to create more complex algorythm to create new alias!
|
|
|
lL = inGSettings["Logger"]
|
|
|
if inAliasStr is None: inAliasStr = str(inDef)
|
|
|
# Check if key is not exists
|
|
|
if inAliasStr in inGSettings["ProcessorDict"]["AliasDefDict"]:
|
|
|
inAliasStr = str(inDef)
|
|
|
if lL: lL.warning(f"Orchestrator.ProcessorAliasDefCreate: Alias {inAliasStr} already exists in alias dictionary. Another alias will be generated and returned")
|
|
|
inGSettings["ProcessorDict"]["AliasDefDict"][inAliasStr] = inDef
|
|
|
return inAliasStr
|
|
|
|
|
|
def ProcessorAliasDefUpdate(inGSettings, inDef, inAliasStr):
|
|
|
"""
|
|
|
Update alias for def (can be used in ActivityItem in field Def).
|
|
|
!WHEN DEF ALIAS IS REQUIRED! - Def alias is required when you try to call Python def from the Orchestrator WEB side (because you can't transmit Python def object out of the Python environment)
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
def TestDef():
|
|
|
pass
|
|
|
Orchestrator.ProcessorAliasDefUpdate(
|
|
|
inGSettings = gSettings,
|
|
|
inDef = TestDef,
|
|
|
inAliasStr="TestDefAlias")
|
|
|
# Now you can call TestDef by the alias "TestDefAlias" with help of ActivityItem (key Def = "TestDefAlias")
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inDef: Def
|
|
|
:param inAliasStr: String alias for associated def
|
|
|
:return: str Alias string
|
|
|
"""
|
|
|
if callable(inDef): inGSettings["ProcessorDict"]["AliasDefDict"][inAliasStr] = inDef
|
|
|
else: raise Exception(f"pyOpenRPA Exception: You can't use Orchestrator.ProcessorAliasDefUpdate with arg 'inDef' string value. inDef is '{inDef}', inAliasStr is '{inAliasStr}'")
|
|
|
return inAliasStr
|
|
|
|
|
|
def ProcessorActivityItemCreate(inDef, inArgList=None, inArgDict=None, inArgGSettingsStr=None, inArgLoggerStr=None):
|
|
|
"""
|
|
|
Create activity item. Activity item can be used as list item in ProcessorActivityItemAppend or in Processor.ActivityListExecute.
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
# EXAMPLE 1
|
|
|
def TestDef(inArg1Str, inGSettings, inLogger):
|
|
|
pass
|
|
|
lActivityItem = Orchestrator.ProcessorActivityItemCreate(
|
|
|
inDef = TestDef,
|
|
|
inArgList=[],
|
|
|
inArgDict={"inArg1Str": "ArgValueStr"},
|
|
|
inArgGSettingsStr = "inGSettings",
|
|
|
inArgLoggerStr = "inLogger")
|
|
|
# lActivityItem:
|
|
|
# {
|
|
|
# "Def":TestDef,
|
|
|
# "ArgList":inArgList,
|
|
|
# "ArgDict":inArgDict,
|
|
|
# "ArgGSettings": "inArgGSettings",
|
|
|
# "ArgLogger": "inLogger"
|
|
|
# }
|
|
|
|
|
|
# EXAMPLE 2
|
|
|
def TestDef(inArg1Str):
|
|
|
pass
|
|
|
Orchestrator.ProcessorAliasDefUpdate(
|
|
|
inGSettings = gSettings,
|
|
|
inDef = TestDef,
|
|
|
inAliasStr="TestDefAlias")
|
|
|
lActivityItem = Orchestrator.ProcessorActivityItemCreate(
|
|
|
inDef = "TestDefAlias",
|
|
|
inArgList=[],
|
|
|
inArgDict={"inArg1Str": "ArgValueStr"},
|
|
|
inArgGSettingsStr = None,
|
|
|
inArgLoggerStr = None)
|
|
|
# lActivityItem:
|
|
|
# {
|
|
|
# "Def":"TestDefAlias",
|
|
|
# "ArgList":inArgList,
|
|
|
# "ArgDict":inArgDict,
|
|
|
# "ArgGSettings": None,
|
|
|
# "ArgLogger": None
|
|
|
# }
|
|
|
|
|
|
:param inDef: def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
:param inArgList: Args list for the Def
|
|
|
:param inArgDict: Args dict for the def
|
|
|
:param inArgGSettingsStr: Name of def argument of the GSettings dict
|
|
|
:param inArgLoggerStr: Name of def argument of the logging object
|
|
|
:return: {}
|
|
|
"""
|
|
|
if inArgList is None: inArgList=[]
|
|
|
if inArgDict is None: inArgDict={}
|
|
|
lActivityItemDict= {
|
|
|
"Def":inDef, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList":inArgList, # Args list
|
|
|
"ArgDict":inArgDict, # Args dictionary
|
|
|
"ArgGSettings": inArgGSettingsStr, # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
"ArgLogger": inArgLoggerStr # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
}
|
|
|
return lActivityItemDict
|
|
|
|
|
|
def ProcessorActivityItemAppend(inGSettings, inDef=None, inArgList=None, inArgDict=None, inArgGSettingsStr=None, inArgLoggerStr=None, inActivityItemDict=None):
|
|
|
"""
|
|
|
Create and add activity item in processor queue.
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
# EXAMPLE 1
|
|
|
def TestDef(inArg1Str, inGSettings, inLogger):
|
|
|
pass
|
|
|
lActivityItem = Orchestrator.ProcessorActivityItemAppend(
|
|
|
inGSettings = gSettingsDict,
|
|
|
inDef = TestDef,
|
|
|
inArgList=[],
|
|
|
inArgDict={"inArg1Str": "ArgValueStr"},
|
|
|
inArgGSettingsStr = "inGSettings",
|
|
|
inArgLoggerStr = "inLogger")
|
|
|
# Activity have been already append in the processor queue
|
|
|
|
|
|
# EXAMPLE 2
|
|
|
def TestDef(inArg1Str):
|
|
|
pass
|
|
|
Orchestrator.ProcessorAliasDefUpdate(
|
|
|
inGSettings = gSettings,
|
|
|
inDef = TestDef,
|
|
|
inAliasStr="TestDefAlias")
|
|
|
lActivityItem = Orchestrator.ProcessorActivityItemCreate(
|
|
|
inDef = "TestDefAlias",
|
|
|
inArgList=[],
|
|
|
inArgDict={"inArg1Str": "ArgValueStr"},
|
|
|
inArgGSettingsStr = None,
|
|
|
inArgLoggerStr = None)
|
|
|
Orchestrator.ProcessorActivityItemAppend(
|
|
|
inGSettings = gSettingsDict,
|
|
|
inActivityItemDict = lActivityItem)
|
|
|
# Activity have been already append in the processor queue
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inDef: def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
:param inArgList: Args list for the Def
|
|
|
:param inArgDict: Args dict for the Def
|
|
|
:param inArgGSettingsStr: Name of def argument of the GSettings dict
|
|
|
:param inArgLoggerStr: Name of def argument of the logging object
|
|
|
:param inActivityItemDict: Fill if you already have ActivityItemDict (don't fill inDef, inArgList, inArgDict, inArgGSettingsStr, inArgLoggerStr)
|
|
|
"""
|
|
|
if inActivityItemDict is None:
|
|
|
if inArgList is None: inArgList=[]
|
|
|
if inArgDict is None: inArgDict={}
|
|
|
if inDef is None: raise Exception(f"pyOpenRPA Exception: ProcessorActivityItemAppend need inDef arg if you dont use inActivityItemDict")
|
|
|
lActivityList=[
|
|
|
{
|
|
|
"Def":inDef, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList":inArgList, # Args list
|
|
|
"ArgDict":inArgDict, # Args dictionary
|
|
|
"ArgGSettings": inArgGSettingsStr, # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
"ArgLogger": inArgLoggerStr # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
}
|
|
|
]
|
|
|
else:
|
|
|
lActivityList = [inActivityItemDict]
|
|
|
inGSettings["ProcessorDict"]["ActivityList"]+=lActivityList
|
|
|
|
|
|
## Process defs
|
|
|
def ProcessIsStarted(inProcessNameWOExeStr): # Check if process is started
|
|
|
"""
|
|
|
Check if there is any running process that contains the given name processName.
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
lProcessIsStartedBool = Orchestrator.ProcessIsStarted(inProcessNameWOExeStr = "notepad")
|
|
|
# lProcessIsStartedBool is True - notepad.exe is running on the Orchestrator machine
|
|
|
|
|
|
:param inProcessNameWOExeStr: Process name WithOut (WO) '.exe' postfix. Example: "notepad" (not "notepad.exe")
|
|
|
:return: True - process is running on the orchestrator machine; False - process is not running on the orchestrator machine
|
|
|
"""
|
|
|
#Iterate over the all the running process
|
|
|
for proc in psutil.process_iter():
|
|
|
try:
|
|
|
# Check if process name contains the given name string.
|
|
|
if inProcessNameWOExeStr.lower() in proc.name().lower():
|
|
|
return True
|
|
|
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
|
pass
|
|
|
return False
|
|
|
|
|
|
def ProcessStart(inPathStr, inArgList, inStopProcessNameWOExeStr=None):
|
|
|
"""
|
|
|
Start process locally. Extra feature: Use inStopProcessNameWOExeStr to stop the execution if current process is running.
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
Orchestrator.ProcessStart(
|
|
|
inPathStr = "notepad"
|
|
|
inArgList = []
|
|
|
inStopProcessNameWOExeStr = "notepad")
|
|
|
# notepad.exe will be started if no notepad.exe is active on the machine
|
|
|
|
|
|
:param inPathStr: Command to send in CMD
|
|
|
:param inArgList: List of the arguments for the CMD command. Example: ["test.txt"]
|
|
|
:param inStopProcessNameWOExeStr: Trigger: stop execution if process is running. Process name WithOut (WO) '.exe' postfix. Example: "notepad" (not "notepad.exe")
|
|
|
:return: None - nothing is returned. If process will not start -exception will be raised
|
|
|
"""
|
|
|
lStartProcessBool = True
|
|
|
if inStopProcessNameWOExeStr is not None: #Check if process running
|
|
|
lCheckTaskName = inStopProcessNameWOExeStr
|
|
|
if len(lCheckTaskName)>4:
|
|
|
if lCheckTaskName[-4:].upper() != ".EXE":
|
|
|
lCheckTaskName = lCheckTaskName+".exe"
|
|
|
else:
|
|
|
lCheckTaskName = lCheckTaskName+".exe"
|
|
|
#Check if process exist
|
|
|
if not ProcessIsStarted(inProcessNameWOExeStr=lCheckTaskName): lStartProcessBool=True
|
|
|
|
|
|
if lStartProcessBool == True: # Start if flag is true
|
|
|
lItemArgs=[inPathStr]
|
|
|
if inArgList is None: inArgList = [] # 2021 02 22 Minor fix default value
|
|
|
lItemArgs.extend(inArgList)
|
|
|
subprocess.Popen(lItemArgs,shell=True)
|
|
|
|
|
|
def ProcessStop(inProcessNameWOExeStr, inCloseForceBool, inUserNameStr = "%username%"):
|
|
|
"""
|
|
|
Stop process on the orchestrator machine. You can set user session on the machine and set flag about to force close process.
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
Orchestrator.ProcessStop(
|
|
|
inProcessNameWOExeStr = "notepad"
|
|
|
inCloseForceBool = True
|
|
|
inUserNameStr = "USER_99")
|
|
|
# Will close process "notepad.exe" on the user session "USER_99" (!ATTENTION! if process not exists no exceptions will be raised)
|
|
|
|
|
|
:param inProcessNameWOExeStr: Process name WithOut (WO) '.exe' postfix. Example: "notepad" (not "notepad.exe")
|
|
|
:param inCloseForceBool: True - do force close. False - send signal to safe close (!ATTENTION! - Safe close works only in orchestrator session. Win OS doens't allow to send safe close signal between GUI sessions)
|
|
|
:param inUserNameStr: User name which is has current process to close. Default value is close process on the Orchestrator session
|
|
|
:return: None
|
|
|
"""
|
|
|
# Support input arg if with .exe
|
|
|
lProcessNameWExeStr = inProcessNameWOExeStr
|
|
|
if len(lProcessNameWExeStr) > 4:
|
|
|
if lProcessNameWExeStr[-4:].upper() != ".EXE":
|
|
|
lProcessNameWExeStr = lProcessNameWExeStr + ".exe"
|
|
|
else:
|
|
|
lProcessNameWExeStr = lProcessNameWExeStr + ".exe"
|
|
|
# Flag Force
|
|
|
lActivityCloseCommand = 'taskkill /im ' + lProcessNameWExeStr
|
|
|
if inCloseForceBool == True:
|
|
|
lActivityCloseCommand += " /F"
|
|
|
# None - all users, %username% - current user, another str - another user
|
|
|
if inUserNameStr is not None:
|
|
|
lActivityCloseCommand += f' /fi "username eq {inUserNameStr}"'
|
|
|
# Kill process
|
|
|
os.system(lActivityCloseCommand)
|
|
|
|
|
|
def ProcessListGet(inProcessNameWOExeList=None):
|
|
|
"""
|
|
|
Return process list on the orchestrator machine sorted by Memory Usage. You can determine the list of the processes you are interested - def will return the list about it.
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
lProcessList = Orchestrator.ProcessListGet()
|
|
|
# Return the list of the process on the machine.
|
|
|
# !ATTENTION! RUn orchestrator as administrator to get all process list on the machine.
|
|
|
|
|
|
:param inProcessNameWOExeList:
|
|
|
:return: {
|
|
|
"ProcessWOExeList": ["notepad","..."],
|
|
|
"ProcessWOExeUpperList": ["NOTEPAD","..."],
|
|
|
"ProcessDetailList": [
|
|
|
{
|
|
|
'pid': 412,
|
|
|
'username': "DESKTOP\\USER",
|
|
|
'name': 'notepad.exe',
|
|
|
'vms': 13.77767775,
|
|
|
'NameWOExeUpperStr': 'NOTEPAD',
|
|
|
'NameWOExeStr': "'notepad'"},
|
|
|
{...}]
|
|
|
|
|
|
"""
|
|
|
if inProcessNameWOExeList is None: inProcessNameWOExeList = []
|
|
|
lMapUPPERInput = {} # Mapping for processes WO exe
|
|
|
lResult = {"ProcessWOExeList":[], "ProcessWOExeUpperList":[],"ProcessDetailList":[]}
|
|
|
# Create updated list for quick check
|
|
|
lProcessNameWOExeList = []
|
|
|
for lItem in inProcessNameWOExeList:
|
|
|
if lItem is not None:
|
|
|
lProcessNameWOExeList.append(f"{lItem.upper()}.EXE")
|
|
|
lMapUPPERInput[f"{lItem.upper()}.EXE"]= lItem
|
|
|
# Iterate over the list
|
|
|
for proc in psutil.process_iter():
|
|
|
try:
|
|
|
# Fetch process details as dict
|
|
|
pinfo = proc.as_dict(attrs=['pid', 'name', 'username'])
|
|
|
pinfo['vms'] = proc.memory_info().vms / (1024 * 1024)
|
|
|
pinfo['NameWOExeUpperStr'] = pinfo['name'][:-4].upper()
|
|
|
# Add if empty inProcessNameWOExeList or if process in inProcessNameWOExeList
|
|
|
if len(lProcessNameWOExeList)==0 or pinfo['name'].upper() in lProcessNameWOExeList:
|
|
|
try: # 2021 02 22 Minor fix if not admin rights
|
|
|
pinfo['NameWOExeStr'] = lMapUPPERInput[pinfo['name'].upper()]
|
|
|
except Exception as e:
|
|
|
pinfo['NameWOExeStr'] = pinfo['name'][:-4]
|
|
|
lResult["ProcessDetailList"].append(pinfo) # Append dict to list
|
|
|
lResult["ProcessWOExeList"].append(pinfo['NameWOExeStr'])
|
|
|
lResult["ProcessWOExeUpperList"].append(pinfo['NameWOExeUpperStr'])
|
|
|
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
|
pass
|
|
|
return lResult
|
|
|
|
|
|
# Python def - start module function
|
|
|
def PythonStart(inModulePathStr, inDefNameStr, inArgList=None, inArgDict=None, inLogger = None):
|
|
|
"""
|
|
|
Import module and run def in the Orchestrator process.
|
|
|
|
|
|
.. note::
|
|
|
|
|
|
Import module will be each time when PythonStart def will be called.
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
Orchestrator.PythonStart(
|
|
|
inModulePathStr="ModuleToCall.py", # inModulePathStr: Working Directory\\ModuleToCall.py
|
|
|
inDefNameStr="TestDef")
|
|
|
# Import module in Orchestrator process and call def "TestDef" from module "ModuleToCall.py"
|
|
|
|
|
|
:param inModulePathStr: Absolute or relative (working directory of the orchestrator process) path to the importing module .py
|
|
|
:param inDefNameStr: Def name in module
|
|
|
:param inArgList: List of the arguments for callable def
|
|
|
:param inArgDict: Dict of the named arguments for callable def
|
|
|
:param inLogger: Logger instance to log some information when PythonStart def is running
|
|
|
:return: None
|
|
|
"""
|
|
|
if inArgList is None: inArgList=[]
|
|
|
if inArgDict is None: inArgDict={}
|
|
|
try:
|
|
|
lModule=importlib.import_module(inModulePathStr) #Подключить модуль для вызова
|
|
|
lFunction=getattr(lModule,inDefNameStr) #Найти функцию
|
|
|
return lFunction(*inArgList,**inArgDict)
|
|
|
except Exception as e:
|
|
|
if inLogger: inLogger.exception("Loop activity error: module/function not founded")
|
|
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # #
|
|
|
# Scheduler
|
|
|
# # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
|
|
def SchedulerActivityTimeAddWeekly(inGSettings, inTimeHHMMStr="23:55:", inWeekdayList=None, inActivityList=None):
|
|
|
"""
|
|
|
Add activity item list in scheduler. You can set weekday list and set time when launch. Activity list will be executed at planned time/day.
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
# EXAMPLE 1
|
|
|
def TestDef(inArg1Str):
|
|
|
pass
|
|
|
lActivityItem = Orchestrator.ProcessorActivityItemCreate(
|
|
|
inDef = TestDef,
|
|
|
inArgList=[],
|
|
|
inArgDict={"inArg1Str": "ArgValueStr"},
|
|
|
inArgGSettingsStr = None,
|
|
|
inArgLoggerStr = None)
|
|
|
Orchestrator.SchedulerActivityTimeAddWeekly(
|
|
|
inGSettings = gSettingsDict,
|
|
|
inTimeHHMMStr = "04:34",
|
|
|
inWeekdayList=[2,3,4],
|
|
|
inActivityList = [lActivityItem])
|
|
|
# Activity will be executed at 04:34 Wednesday (2), thursday (3), friday (4)
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inTimeHHMMStr: Activation time from "00:00" to "23:59". Example: "05:29"
|
|
|
:param inWeekdayList: Week day list to initiate activity list. Use int from 0 (monday) to 6 (sunday) as list items. Example: [0,1,2,3,4]. Default value is everyday ([0,1,2,3,4,5,6])
|
|
|
:param inActivityList: Activity list structure
|
|
|
:return: None
|
|
|
"""
|
|
|
if inWeekdayList is None: inWeekdayList=[0,1,2,3,4,5,6]
|
|
|
if inActivityList is None: inActivityList=[]
|
|
|
Processor.__ActivityListVerify__(inActivityList=inActivityList) # DO VERIFICATION FOR THE inActivityList
|
|
|
lActivityTimeItemDict = {
|
|
|
"TimeHH:MMStr": inTimeHHMMStr, # Time [HH:MM] to trigger activity
|
|
|
"WeekdayList": inWeekdayList, # List of the weekday index when activity is applicable, Default [1,2,3,4,5,6,7]
|
|
|
"ActivityList": inActivityList,
|
|
|
"GUID": None # # Will be filled in Orchestrator automatically - is needed for detect activity completion
|
|
|
}
|
|
|
inGSettings["SchedulerDict"]["ActivityTimeList"].append(lActivityTimeItemDict)
|
|
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # #
|
|
|
# RDPSession
|
|
|
# # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
|
|
def RDPTemplateCreate(inLoginStr, inPasswordStr, inHostStr="127.0.0.1", inPortInt = 3389, inWidthPXInt = 1680, inHeightPXInt = 1050,
|
|
|
inUseBothMonitorBool = False, inDepthBitInt = 32, inSharedDriveList=None):
|
|
|
"""
|
|
|
Create RDP connect dict item/ Use it connect/reconnect (Orchestrator.RDPSessionConnect)
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
lRDPItemDict = Orchestrator.RDPTemplateCreate(
|
|
|
inLoginStr = "USER_99",
|
|
|
inPasswordStr = "USER_PASS_HERE",
|
|
|
inHostStr="127.0.0.1",
|
|
|
inPortInt = 3389,
|
|
|
inWidthPXInt = 1680,
|
|
|
inHeightPXInt = 1050,
|
|
|
inUseBothMonitorBool = False,
|
|
|
inDepthBitInt = 32,
|
|
|
inSharedDriveList=None)
|
|
|
# lRDPTemplateDict= { # Init the configuration item
|
|
|
# "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"],
|
|
|
# ###### Will updated in program ############
|
|
|
# "SessionHex": "77777sdfsdf77777dsfdfsf77777777", # Hex is created when robot runs, example ""
|
|
|
# "SessionIsWindowExistBool": False, "SessionIsWindowResponsibleBool": False, "SessionIsIgnoredBool": False
|
|
|
# }
|
|
|
|
|
|
:param inLoginStr: User/Robot Login, example "USER_99"
|
|
|
:param inPasswordStr: Password, example "USER_PASS_HERE"
|
|
|
:param inHostStr: Host address, example "77.77.22.22"
|
|
|
:param inPortInt: RDP Port, example "3389" (default)
|
|
|
:param inWidthPXInt: Width of the remote desktop in pixels, example 1680
|
|
|
:param inHeightPXInt: Height of the remote desktop in pixels, example 1050
|
|
|
: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"]
|
|
|
:return:
|
|
|
{
|
|
|
"Host": inHostStr, # Host address, example "77.77.22.22"
|
|
|
"Port": str(inPortInt), # RDP Port, example "3389"
|
|
|
"Login": inLoginStr, # Login, example "test"
|
|
|
"Password": inPasswordStr, # Password, example "test"
|
|
|
"Screen": {
|
|
|
"Width": inWidthPXInt, # Width of the remote desktop in pixels, example 1680
|
|
|
"Height": inHeightPXInt, # Height of the remote desktop in pixels, example 1050
|
|
|
# "640x480" or "1680x1050" or "FullScreen". If Resolution not exists set full screen, example
|
|
|
"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"]
|
|
|
###### 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
|
|
|
"SessionIsWindowResponsibleBool": False,
|
|
|
# Flag if RDP window is responsible (recieve commands). Check every nn seconds. If window is Responsible - window is Exist too , example False
|
|
|
"SessionIsIgnoredBool": False # Flag to ignore RDP window False - dont ignore, True - ignore, example False
|
|
|
}
|
|
|
|
|
|
"""
|
|
|
if inSharedDriveList is None: inSharedDriveList = ["c"]
|
|
|
lRDPTemplateDict= { # Init the configuration item
|
|
|
"Host": inHostStr, # Host address, example "77.77.22.22"
|
|
|
"Port": str(inPortInt), # RDP Port, example "3389"
|
|
|
"Login": inLoginStr, # Login, example "test"
|
|
|
"Password": inPasswordStr, # Password, example "test"
|
|
|
"Screen": {
|
|
|
"Width": inWidthPXInt, # Width of the remote desktop in pixels, example 1680
|
|
|
"Height": inHeightPXInt, # Height of the remote desktop in pixels, example 1050
|
|
|
# "640x480" or "1680x1050" or "FullScreen". If Resolution not exists set full screen, example
|
|
|
"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"]
|
|
|
###### 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
|
|
|
"SessionIsWindowResponsibleBool": False,
|
|
|
# Flag if RDP window is responsible (recieve commands). Check every nn seconds. If window is Responsible - window is Exist too , example False
|
|
|
"SessionIsIgnoredBool": False # Flag to ignore RDP window False - dont ignore, True - ignore, example False
|
|
|
}
|
|
|
return lRDPTemplateDict
|
|
|
|
|
|
# TODO Search dublicates in GSettings RDPlist !
|
|
|
# Return list if dublicates
|
|
|
def RDPSessionDublicatesResolve(inGSettings):
|
|
|
"""
|
|
|
DEVELOPING Search duplicates in GSettings RDPlist
|
|
|
!def is developing!
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:return:
|
|
|
"""
|
|
|
pass
|
|
|
#for lItemKeyStr in inGSettings["RobotRDPActive"]["RDPList"]:
|
|
|
# lItemDict = inGSettings["RobotRDPActive"]["RDPList"][lItemKeyStr]
|
|
|
|
|
|
def RDPSessionConnect(inGSettings, inRDPSessionKeyStr, inRDPTemplateDict=None, inHostStr=None, inPortStr=None, inLoginStr=None, inPasswordStr=None):
|
|
|
"""
|
|
|
Create new RDPSession in RobotRDPActive. Attention - activity will be ignored if RDP key is already exists
|
|
|
2 way of the use
|
|
|
Var 1 (Main stream): inGSettings, inRDPSessionKeyStr, inRDPTemplateDict
|
|
|
Var 2 (Backward compatibility): inGSettings, inRDPSessionKeyStr, inHostStr, inPortStr, inLoginStr, inPasswordStr
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
lRDPItemDict = Orchestrator.RDPTemplateCreate(
|
|
|
inLoginStr = "USER_99",
|
|
|
inPasswordStr = "USER_PASS_HERE", inHostStr="127.0.0.1", inPortInt = 3389, inWidthPXInt = 1680,
|
|
|
inHeightPXInt = 1050, inUseBothMonitorBool = False, inDepthBitInt = 32, inSharedDriveList=None)
|
|
|
Orchestrator.RDPSessionConnect(
|
|
|
inGSettings = gSettings,
|
|
|
inRDPSessionKeyStr = "RDPKey",
|
|
|
inRDPTemplateDict = lRDPItemDict)
|
|
|
# Orchestrator will create RDP session by the lRDPItemDict configuration
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inRDPSessionKeyStr: RDP Session string key - need for the further identification
|
|
|
:param inRDPTemplateDict: RDP configuration dict with settings (see def Orchestrator.RDPTemplateCreate)
|
|
|
:param inHostStr: Backward compatibility from Orchestrator v 1.1.20. Use inRDPTemplateDict
|
|
|
:param inPortStr: Backward compatibility from Orchestrator v 1.1.20. Use inRDPTemplateDict
|
|
|
:param inLoginStr: Backward compatibility from Orchestrator v 1.1.20. Use inRDPTemplateDict
|
|
|
:param inPasswordStr: Backward compatibility from Orchestrator v 1.1.20. Use inRDPTemplateDict
|
|
|
:return: True every time :)
|
|
|
"""
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionConnect, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr, "inRDPTemplateDict":inRDPTemplateDict, "inHostStr": inHostStr, "inPortStr": inPortStr,
|
|
|
"inLoginStr": inLoginStr, "inPasswordStr": inPasswordStr}, # 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)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else: # In processor - do execution
|
|
|
# Var 1 - if RDPTemplateDict is input
|
|
|
lRDPConfigurationItem=inRDPTemplateDict
|
|
|
# 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
|
|
|
# Start the connect
|
|
|
if inRDPSessionKeyStr not in inGSettings["RobotRDPActive"]["RDPList"]:
|
|
|
inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr] = lRDPConfigurationItem # Add item in RDPList
|
|
|
Connector.Session(lRDPConfigurationItem) # Create the RDP session
|
|
|
Connector.SystemRDPWarningClickOk() # Click all warning messages
|
|
|
else:
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP session was not created because it is alredy exists in the RDPList. Use RDPSessionReconnect if you want to update RDP configuration.")
|
|
|
return True
|
|
|
|
|
|
def RDPSessionDisconnect(inGSettings, inRDPSessionKeyStr, inBreakTriggerProcessWOExeList = None):
|
|
|
"""
|
|
|
Disconnect the RDP session and stop monitoring it.
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
Orchestrator.RDPSessionDisconnect(
|
|
|
inGSettings = gSettings,
|
|
|
inRDPSessionKeyStr = "RDPKey")
|
|
|
# Orchestrator will disconnect RDP session and will stop to monitoring current RDP
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inRDPSessionKeyStr: RDP Session string key - need for the further identification
|
|
|
:param inBreakTriggerProcessWOExeList: List of the processes, which will stop the execution. Example ["notepad"]
|
|
|
|
|
|
.. note::
|
|
|
|
|
|
Orchestrator look processes on the current machine
|
|
|
:return: True every time
|
|
|
"""
|
|
|
if inBreakTriggerProcessWOExeList is None: inBreakTriggerProcessWOExeList = []
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionDisconnect, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr, "inBreakTriggerProcessWOExeList": inBreakTriggerProcessWOExeList }, # 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)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else: # In processor - do execution
|
|
|
lSessionHex = inGSettings["RobotRDPActive"]["RDPList"].get(inRDPSessionKeyStr,{}).get("SessionHex", None)
|
|
|
if lSessionHex:
|
|
|
lProcessListResult = {"ProcessWOExeList":[],"ProcessDetailList":[]}
|
|
|
if len(inBreakTriggerProcessWOExeList) > 0:
|
|
|
lProcessListResult = ProcessListGet(inProcessNameWOExeList=inBreakTriggerProcessWOExeList) # Run the task manager monitor
|
|
|
if len(lProcessListResult["ProcessWOExeList"]) == 0: # Start disconnect if no process exist
|
|
|
inGSettings["RobotRDPActive"]["RDPList"].pop(inRDPSessionKeyStr,None)
|
|
|
Connector.SessionClose(inSessionHexStr=lSessionHex)
|
|
|
Connector.SystemRDPWarningClickOk() # Click all warning messages
|
|
|
return True
|
|
|
|
|
|
def RDPSessionReconnect(inGSettings, inRDPSessionKeyStr, inRDPTemplateDict=None):
|
|
|
"""
|
|
|
Reconnect the RDP session
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
lRDPItemDict = Orchestrator.RDPTemplateCreate(
|
|
|
inLoginStr = "USER_99",
|
|
|
inPasswordStr = "USER_PASS_HERE", inHostStr="127.0.0.1", inPortInt = 3389, inWidthPXInt = 1680,
|
|
|
inHeightPXInt = 1050, inUseBothMonitorBool = False, inDepthBitInt = 32, inSharedDriveList=None)
|
|
|
Orchestrator.RDPSessionReconnect(
|
|
|
inGSettings = gSettings,
|
|
|
inRDPSessionKeyStr = "RDPKey",
|
|
|
inRDPTemplateDict = inRDPTemplateDict)
|
|
|
# Orchestrator will reconnect RDP session and will continue to monitoring current RDP
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inRDPSessionKeyStr: RDP Session string key - need for the further identification
|
|
|
:param inRDPTemplateDict: RDP configuration dict with settings (see def Orchestrator.RDPTemplateCreate)
|
|
|
:return:
|
|
|
"""
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionReconnect, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr, "inRDPTemplateDict":inRDPTemplateDict }, # 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)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else:
|
|
|
lRDPConfigurationItem = inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr]
|
|
|
RDPSessionDisconnect(inGSettings = inGSettings, inRDPSessionKeyStr=inRDPSessionKeyStr) # Disconnect the RDP 2021 02 22 minor fix by Ivan Maslov
|
|
|
# Replace Configuration item if inRDPTemplateDict exists
|
|
|
if inRDPTemplateDict is not None: lRDPConfigurationItem=inRDPTemplateDict
|
|
|
# Add item in RDPList
|
|
|
inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr] = lRDPConfigurationItem
|
|
|
# Create the RDP session
|
|
|
Connector.Session(lRDPConfigurationItem)
|
|
|
return True
|
|
|
|
|
|
def RDPSessionMonitorStop(inGSettings, inRDPSessionKeyStr):
|
|
|
"""
|
|
|
Stop monitoring the RDP session by the Orchestrator process. Current def don't kill RDP session - only stop to track it (it can give )
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
Orchestrator.RDPSessionMonitorStop(
|
|
|
inGSettings = gSettings,
|
|
|
inRDPSessionKeyStr = "RDPKey")
|
|
|
# Orchestrator will stop the RDP monitoring
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inRDPSessionKeyStr: RDP Session string key - need for the further identification
|
|
|
:return: True every time :>
|
|
|
"""
|
|
|
lResult = True
|
|
|
inGSettings["RobotRDPActive"]["RDPList"].pop(inRDPSessionKeyStr,None) # Remove item from RDPList
|
|
|
return lResult
|
|
|
|
|
|
def RDPSessionLogoff(inGSettings, inRDPSessionKeyStr, inBreakTriggerProcessWOExeList = None):
|
|
|
"""
|
|
|
Logoff the RDP session from the Orchestrator process (close all apps in session when logoff)
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
Orchestrator.RDPSessionLogoff(
|
|
|
inGSettings = gSettings,
|
|
|
inRDPSessionKeyStr = "RDPKey",
|
|
|
inBreakTriggerProcessWOExeList = ['Notepad'])
|
|
|
# Orchestrator will logoff the RDP session
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inRDPSessionKeyStr: RDP Session string key - need for the further identification
|
|
|
:param inBreakTriggerProcessWOExeList: List of the processes, which will stop the execution. Example ["notepad"]
|
|
|
:return: True - logoff is successful
|
|
|
"""
|
|
|
if inBreakTriggerProcessWOExeList is None: inBreakTriggerProcessWOExeList = []
|
|
|
lResult = True
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionLogoff, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr, "inBreakTriggerProcessWOExeList": inBreakTriggerProcessWOExeList }, # 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)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else:
|
|
|
lCMDStr = "shutdown -L" # CMD logoff command
|
|
|
# Calculate the session Hex
|
|
|
lSessionHex = inGSettings["RobotRDPActive"]["RDPList"].get(inRDPSessionKeyStr,{}).get("SessionHex", None)
|
|
|
if lSessionHex:
|
|
|
lProcessListResult = {"ProcessWOExeList":[],"ProcessDetailList":[]}
|
|
|
if len(inBreakTriggerProcessWOExeList) > 0:
|
|
|
lProcessListResult = ProcessListGet(inProcessNameWOExeList=inBreakTriggerProcessWOExeList) # Run the task manager monitor
|
|
|
if len(lProcessListResult["ProcessWOExeList"]) == 0: # Start logoff if no process exist
|
|
|
# Run CMD - dont crosscheck because CMD dont return value to the clipboard when logoff
|
|
|
Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=lCMDStr, inModeStr="RUN", inLogger=inGSettings["Logger"], inRDPConfigurationItem=inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr])
|
|
|
inGSettings["RobotRDPActive"]["RDPList"].pop(inRDPSessionKeyStr,None) # Remove item from RDPList
|
|
|
return lResult
|
|
|
|
|
|
def RDPSessionResponsibilityCheck(inGSettings, inRDPSessionKeyStr):
|
|
|
"""
|
|
|
DEVELOPING, MAYBE NOT USEFUL Check RDP Session responsibility TODO NEED DEV + TEST
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inRDPSessionKeyStr: RDP Session string key - need for the further identification
|
|
|
:return: True every time
|
|
|
"""
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionResponsibilityCheck, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr }, # 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)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else:
|
|
|
lRDPConfigurationItem = inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr] # Get the alias
|
|
|
# set the fullscreen
|
|
|
# ATTENTION!!! Session hex can be updated!!!
|
|
|
Connector.SessionScreenFull(inSessionHex=lRDPConfigurationItem["SessionHex"], inLogger=inGSettings["Logger"], inRDPConfigurationItem=inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr])
|
|
|
time.sleep(1)
|
|
|
# Check RDP responsibility
|
|
|
lDoCheckResponsibilityBool = True
|
|
|
lDoCheckResponsibilityCountMax = 20
|
|
|
lDoCheckResponsibilityCountCurrent = 0
|
|
|
while lDoCheckResponsibilityBool:
|
|
|
# Check if counter is exceed - raise exception
|
|
|
if lDoCheckResponsibilityCountCurrent >= lDoCheckResponsibilityCountMax:
|
|
|
pass
|
|
|
#raise ConnectorExceptions.SessionWindowNotResponsibleError("Error when initialize the RDP session - RDP window is not responding!")
|
|
|
# Check responding
|
|
|
lDoCheckResponsibilityBool = not Connector.SystemRDPIsResponsible(inSessionHexStr = lRDPConfigurationItem["SessionHex"])
|
|
|
# Wait if is not responding
|
|
|
if lDoCheckResponsibilityBool:
|
|
|
time.sleep(3)
|
|
|
# increase the couter
|
|
|
lDoCheckResponsibilityCountCurrent+=1
|
|
|
return True
|
|
|
|
|
|
def RDPSessionProcessStartIfNotRunning(inGSettings, inRDPSessionKeyStr, inProcessNameWEXEStr, inFilePathStr, inFlagGetAbsPathBool=True):
|
|
|
"""
|
|
|
Start process in RDP if it is not running (check by the arg inProcessNameWEXEStr)
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
Orchestrator.RDPSessionProcessStartIfNotRunning(
|
|
|
inGSettings = gSettings,
|
|
|
inRDPSessionKeyStr = "RDPKey",
|
|
|
inProcessNameWEXEStr = 'Notepad.exe',
|
|
|
inFilePathStr = "path\\to\the\\executable\\file.exe"
|
|
|
inFlagGetAbsPathBool = True)
|
|
|
# Orchestrator will start the process in RDP session
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inRDPSessionKeyStr: RDP Session string key - need for the further identification
|
|
|
:param inProcessNameWEXEStr: Process name with extension (.exe). This arg allow to check the process is running. Example: "Notepad.exe"
|
|
|
:param inFilePathStr: Path to run process if it is not running.
|
|
|
:param inFlagGetAbsPathBool: True - get abs path from the relative path in inFilePathStr. False - else case
|
|
|
:return: True every time :)
|
|
|
"""
|
|
|
# Check thread
|
|
|
lResult = True
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lActivityItem = {
|
|
|
"Def": RDPSessionProcessStartIfNotRunning, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr, "inProcessNameWEXEStr": inProcessNameWEXEStr, "inFilePathStr": inFilePathStr, "inFlagGetAbsPathBool": inFlagGetAbsPathBool }, # 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)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lActivityItem)
|
|
|
else:
|
|
|
lCMDStr = CMDStr.ProcessStartIfNotRunning(inProcessNameWEXEStr, inFilePathStr, inFlagGetAbsPath= inFlagGetAbsPathBool)
|
|
|
# Calculate the session Hex
|
|
|
lSessionHex = inGSettings["RobotRDPActive"]["RDPList"].get(inRDPSessionKeyStr,{}).get("SessionHex", None)
|
|
|
# Run CMD
|
|
|
if lSessionHex:
|
|
|
Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=lCMDStr, inModeStr="CROSSCHECK", inLogger=inGSettings["Logger"],
|
|
|
inRDPConfigurationItem=inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr])
|
|
|
return lResult
|
|
|
|
|
|
def RDPSessionCMDRun(inGSettings, inRDPSessionKeyStr, inCMDStr, inModeStr="CROSSCHECK"):
|
|
|
"""
|
|
|
Send CMD command to the RDP session "RUN" window
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
lResultDict = Orchestrator.RDPSessionCMDRun(
|
|
|
inGSettings = gSettings,
|
|
|
inRDPSessionKeyStr = "RDPKey",
|
|
|
inModeStr = 'LISTEN')
|
|
|
# Orchestrator will send CMD to RDP and return the result (see return section)
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inRDPSessionKeyStr: RDP Session string key - need for the further identification
|
|
|
:param inCMDStr: Any CMD string
|
|
|
:param inModeStr: Variants:
|
|
|
"LISTEN" - Get result of the cmd command in result;
|
|
|
"CROSSCHECK" - Check if the command was successfully sent
|
|
|
"RUN" - Run without crosscheck and get clipboard
|
|
|
:return: # OLD > True - CMD was executed successfully
|
|
|
{
|
|
|
"OutStr": <> # Result string
|
|
|
"IsResponsibleBool": True|False # Flag is RDP is responsible - works only when inModeStr = CROSSCHECK
|
|
|
}
|
|
|
"""
|
|
|
lResult = {
|
|
|
"OutStr": None, # Result string
|
|
|
"IsResponsibleBool": False # Flag is RDP is responsible - works only when inModeStr = CROSSCHECK
|
|
|
}
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lProcessorActivityDict = {
|
|
|
"Def": RDPSessionCMDRun, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr, "inCMDStr": inCMDStr, "inModeStr": inModeStr }, # 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)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lProcessorActivityDict)
|
|
|
else:
|
|
|
#lResult = True
|
|
|
# Calculate the session Hex
|
|
|
lSessionHex = inGSettings["RobotRDPActive"]["RDPList"].get(inRDPSessionKeyStr,{}).get("SessionHex", None)
|
|
|
# Run CMD
|
|
|
if lSessionHex:
|
|
|
lResult = Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=inCMDStr, inModeStr=inModeStr, inLogger=inGSettings["Logger"],
|
|
|
inRDPConfigurationItem=inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr])
|
|
|
return lResult
|
|
|
|
|
|
def RDPSessionProcessStop(inGSettings, inRDPSessionKeyStr, inProcessNameWEXEStr, inFlagForceCloseBool):
|
|
|
"""
|
|
|
Send CMD command to the RDP session "RUN" window.
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
lResultDict = Orchestrator.RDPSessionProcessStop(
|
|
|
inGSettings = gSettings,
|
|
|
inRDPSessionKeyStr = "RDPKey",
|
|
|
inProcessNameWEXEStr = 'notepad.exe',
|
|
|
inFlagForceCloseBool = True)
|
|
|
# Orchestrator will send CMD to RDP and return the result (see return section)
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inRDPSessionKeyStr: RDP Session string key - need for the further identification
|
|
|
:param inProcessNameWEXEStr: Process name to kill. Example: 'notepad.exe'
|
|
|
:param inFlagForceCloseBool: True - force close the process. False - safe close the process
|
|
|
:return: True every time
|
|
|
"""
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionProcessStop, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr, "inProcessNameWEXEStr": inProcessNameWEXEStr, "inFlagForceCloseBool": inFlagForceCloseBool }, # 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)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else:
|
|
|
lResult = True
|
|
|
lCMDStr = f'taskkill /im "{inProcessNameWEXEStr}" /fi "username eq %USERNAME%"'
|
|
|
if inFlagForceCloseBool:
|
|
|
lCMDStr+= " /F"
|
|
|
# Calculate the session Hex
|
|
|
lSessionHex = inGSettings["RobotRDPActive"]["RDPList"].get(inRDPSessionKeyStr,{}).get("SessionHex", None)
|
|
|
# Run CMD
|
|
|
if lSessionHex:
|
|
|
Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=lCMDStr, inModeStr="CROSSCHECK", inLogger=inGSettings["Logger"], inRDPConfigurationItem=inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr])
|
|
|
return lResult
|
|
|
|
|
|
def RDPSessionFileStoredSend(inGSettings, inRDPSessionKeyStr, inHostFilePathStr, inRDPFilePathStr):
|
|
|
"""
|
|
|
Send file from Orchestrator session to the RDP session using shared drive in RDP (see RDP Configuration Dict, Shared drive)
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
lResultDict = Orchestrator.RDPSessionFileStoredSend(
|
|
|
inGSettings = gSettings,
|
|
|
inRDPSessionKeyStr = "RDPKey",
|
|
|
inHostFilePathStr = "TESTDIR\\Test.py",
|
|
|
inRDPFilePathStr = "C:\\RPA\\TESTDIR\\Test.py")
|
|
|
# Orchestrator will send CMD to RDP and return the result (see return section)
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inRDPSessionKeyStr: RDP Session string key - need for the further identification
|
|
|
:param inHostFilePathStr: Relative or absolute path to the file location on the Orchestrator side. Example: "TESTDIR\\Test.py"
|
|
|
:param inRDPFilePathStr: !Absolute! path to the destination file location on the RDP side. Example: "C:\\RPA\\TESTDIR\\Test.py"
|
|
|
:return: True every time
|
|
|
"""
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionFileStoredSend, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr, "inHostFilePathStr": inHostFilePathStr, "inRDPFilePathStr": inRDPFilePathStr }, # 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)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else:
|
|
|
lResult = True
|
|
|
lCMDStr = CMDStr.FileStoredSend(inHostFilePath = inHostFilePathStr, inRDPFilePath = inRDPFilePathStr)
|
|
|
# Calculate the session Hex
|
|
|
lSessionHex = inGSettings["RobotRDPActive"]["RDPList"].get(inRDPSessionKeyStr, {}).get("SessionHex", None)
|
|
|
#lSessionHex = inGlobalDict["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr]["SessionHex"]
|
|
|
# Run CMD
|
|
|
if lSessionHex:
|
|
|
Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=lCMDStr, inModeStr="LISTEN", inClipboardTimeoutSec = 120, inLogger=inGSettings["Logger"], inRDPConfigurationItem=inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr])
|
|
|
return lResult
|
|
|
|
|
|
def RDPSessionFileStoredRecieve(inGSettings, inRDPSessionKeyStr, inRDPFilePathStr, inHostFilePathStr):
|
|
|
"""
|
|
|
Recieve file from RDP session to the Orchestrator session using shared drive in RDP (see RDP Configuration Dict, Shared drive)
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
# USAGE
|
|
|
from pyOpenRPA import Orchestrator
|
|
|
|
|
|
lResultDict = Orchestrator.RDPSessionFileStoredRecieve(
|
|
|
inGSettings = gSettings,
|
|
|
inRDPSessionKeyStr = "RDPKey",
|
|
|
inHostFilePathStr = "TESTDIR\\Test.py",
|
|
|
inRDPFilePathStr = "C:\\RPA\\TESTDIR\\Test.py")
|
|
|
# Orchestrator will send CMD to RDP and return the result (see return section)
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:param inRDPSessionKeyStr: RDP Session string key - need for the further identification
|
|
|
:param inRDPFilePathStr: !Absolute! path to the destination file location on the RDP side. Example: "C:\\RPA\\TESTDIR\\Test.py"
|
|
|
:param inHostFilePathStr: Relative or absolute path to the file location on the Orchestrator side. Example: "TESTDIR\\Test.py"
|
|
|
:return: True every time
|
|
|
"""
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionFileStoredRecieve, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr, "inRDPFilePathStr": inRDPFilePathStr, "inHostFilePathStr": inHostFilePathStr }, # 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)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else:
|
|
|
lResult = True
|
|
|
lCMDStr = CMDStr.FileStoredRecieve(inRDPFilePath = inRDPFilePathStr, inHostFilePath = inHostFilePathStr)
|
|
|
# Calculate the session Hex
|
|
|
lSessionHex = inGSettings["RobotRDPActive"]["RDPList"].get(inRDPSessionKeyStr,{}).get("SessionHex", None)
|
|
|
# Run CMD
|
|
|
if lSessionHex:
|
|
|
Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=lCMDStr, inModeStr="LISTEN", inClipboardTimeoutSec = 120, inLogger=inGSettings["Logger"], inRDPConfigurationItem=inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr])
|
|
|
return lResult
|
|
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # #
|
|
|
# # # # # Start orchestrator
|
|
|
# # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
|
|
def GSettingsAutocleaner(inGSettings):
|
|
|
"""
|
|
|
HIDDEN Interval gSettings auto cleaner def to clear some garbage.
|
|
|
|
|
|
:param inGSettings: Global settings dict (singleton)
|
|
|
:return: None
|
|
|
"""
|
|
|
while True:
|
|
|
time.sleep(inGSettings["Autocleaner"]["IntervalSecFloat"]) # Wait for the next iteration
|
|
|
lL = inGSettings["Logger"]
|
|
|
if lL: lL.info(f"Autocleaner is running") # Info
|
|
|
lNowDatetime = datetime.datetime.now() # Get now time
|
|
|
# Clean old items in Client > Session > TechnicalSessionGUIDCache
|
|
|
lTechnicalSessionGUIDCacheNew = {}
|
|
|
for lItemKeyStr in inGSettings["Client"]["Session"]["TechnicalSessionGUIDCache"]:
|
|
|
lItemValue = inGSettings["Client"]["Session"]["TechnicalSessionGUIDCache"][lItemKeyStr]
|
|
|
if (lNowDatetime - lItemValue["InitDatetime"]).total_seconds() < inGSettings["Client"]["Session"]["LifetimeSecFloat"]: # Add if lifetime is ok
|
|
|
lTechnicalSessionGUIDCacheNew[lItemKeyStr]=lItemValue # Lifetime is ok - set
|
|
|
else:
|
|
|
if lL: lL.debug(f"Client > Session > TechnicalSessionGUIDCache > lItemKeyStr: Lifetime is expired. Remove from gSettings") # Info
|
|
|
inGSettings["Client"]["Session"]["TechnicalSessionGUIDCache"] = lTechnicalSessionGUIDCacheNew # Set updated Cache
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
|
|
from .. import __version__ # Get version from the package
|
|
|
|
|
|
def Orchestrator(inGSettings):
|
|
|
lL = inGSettings["Logger"]
|
|
|
# https://stackoverflow.com/questions/130763/request-uac-elevation-from-within-a-python-script
|
|
|
def is_admin():
|
|
|
try:
|
|
|
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)
|
|
|
|
|
|
# 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={} #Словарь отработанных активностей, ключ - кортеж (<activityType>, <datetime>, <processPath || processName>, <processArgs>)
|
|
|
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
|
|
|
lRobotRDPThreadControlDict = {"ThreadExecuteBool":True} # inThreadControlDict = {"ThreadExecuteBool":True}
|
|
|
lRobotRDPActiveThread = threading.Thread(target= RobotRDPActive.RobotRDPActive, kwargs={"inGSettings":gSettingsDict, "inThreadControlDict":lRobotRDPThreadControlDict})
|
|
|
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.ActivityListExecute(inGSettings=gSettingsDict,inActivityList=[BackwardCompatibility.v1_2_0_ProcessorOld2NewActivityDict(lActivityItem)])
|
|
|
# Processor thread
|
|
|
lProcessorThread = threading.Thread(target= Processor.ProcessorRunSync, kwargs={"inGSettings":gSettingsDict, "inRobotRDPThreadControlDict":lRobotRDPThreadControlDict})
|
|
|
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:MMStr" in lItem:
|
|
|
#Вид активности - запуск процесса
|
|
|
#Сформировать временной штамп, относительно которого надо будет проверять время
|
|
|
#часовой пояс пока не учитываем
|
|
|
lActivityDateTime=datetime.datetime.strptime(lItem["TimeHH:MMStr"],"%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. Parameters are not available to see.") #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__():
|
|
|
lSubmoduleFunctionName = "Settings"
|
|
|
lFileFullPath = sys.argv[1]
|
|
|
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)
|
|
|
gSettingsDict = None
|
|
|
if lSubmoduleFunctionName in dir(lTechModuleFromSpec):
|
|
|
# Run SettingUpdate function in submodule
|
|
|
gSettingsDict = getattr(lTechModuleFromSpec, lSubmoduleFunctionName)()
|
|
|
#################################################
|
|
|
Orchestrator(inGSettings=gSettingsDict) # Call the orchestrator
|