parent
c4f835b3cf
commit
f93028125c
@ -0,0 +1,186 @@
|
|||||||
|
import psutil, datetime, logging, os, json
|
||||||
|
# # # # # # # ORCHESTRATOR CONTROL PANEL <Robot name> # # # # # # #
|
||||||
|
# Init parameters
|
||||||
|
gProcessNameWOEXE = "OpenRPA_RobotRDPActive"
|
||||||
|
gRDPStartFilePath = os.path.abspath(os.path.join(os.getcwd(), "..\\Utils\\RobotRDPActive\\pyOpenRPA.Tools.RobotRDPActive_x64.cmd")) # cwd is orchestrator working directory
|
||||||
|
gOrchestratorToRobotKeyList = ["Storage","RobotRDPActive","OrchestratorToRobotStorage"]
|
||||||
|
gRobotToOrchestratorKeyList = ["Storage","RobotRDPActive","RobotToOrchestratorStorage"]
|
||||||
|
gRobotRDPActiveDefsFolderPath = ""
|
||||||
|
|
||||||
|
# Function, which is generate Dict for front-endswith
|
||||||
|
def ControlPanelRenderDict(inGSettings):
|
||||||
|
"""result={
|
||||||
|
"HeaderLeftText":"<Robot name>",
|
||||||
|
"HeaderRightText":"<header>",
|
||||||
|
"DataStorageKey":"RobotRDPActive", #Use key for set current dict in mGlobal.DataStorage["DataStorageKey"] on client side
|
||||||
|
"SubheaderText": "State: <span style=\"color:green\">Turned on</span>",
|
||||||
|
"BodyKeyValueList":[
|
||||||
|
{"Key":"Session list","Value":""},
|
||||||
|
{"Key":"Session list","Value":""}
|
||||||
|
],
|
||||||
|
"FooterText":"Last update: 9:38:00 09.10.2019",
|
||||||
|
"FooterButtonX2List":[
|
||||||
|
{"Text":"Turn on", "Color":"green", "Link":"", "OnClick": lOnClickRunButton.replace("\\","\\\\")},
|
||||||
|
],
|
||||||
|
"FooterButtonX1List":[
|
||||||
|
{"Text":"Kill", "Color":"red", "Link":"", "OnClick": lOnClickForceCloseButton.replace("\\","\\\\")}
|
||||||
|
]
|
||||||
|
}"""
|
||||||
|
# START :: Create activities :: START #
|
||||||
|
## RDP Start
|
||||||
|
lActivityRDPStartEscaped = (f"mGlobal.Controller.CMDRunText('start cmd /K {gRDPStartFilePath}');").replace("\\","\\\\") # Need escape because this is render on client side and after that it goes to server side :(
|
||||||
|
## RDP Safe turn off
|
||||||
|
lActivityRDPSafeOffEscaped = (f"mGlobal.Processor.ServerValueSet({json.dumps(gOrchestratorToRobotKeyList+['SafeTurnOff'])},true);").replace("\\","\\\\")
|
||||||
|
## RDP Kill
|
||||||
|
lActivityRDPKillEscaped = (f"mGlobal.Controller.CMDRunText('taskkill /F /im {gProcessNameWOEXE}.exe');").replace("\\","\\\\")
|
||||||
|
## Recieve file from RDP session to Orchestrator session
|
||||||
|
lActivityFileRDP2OrchestratorDict = [
|
||||||
|
{
|
||||||
|
"ModulePath": f"{os.path.join(gRobotRDPActiveDefsFolderPath,'Defs_SessionIndex.py')}", # "Session\\SessionDefs.py"
|
||||||
|
"DefName":"FileStoredRecieve", # Function name
|
||||||
|
"ArgList":[], # Args list
|
||||||
|
"ArgDict":{"inSessionIndex": 0, "inHostFilePath": "testRecieve.txt","inRDPFilePath": "C:\\Temp\\testRecieve.txt"} # Args dictionary
|
||||||
|
}
|
||||||
|
]
|
||||||
|
lActivityFileRDP2Orchestrator = f"mGlobal.Processor.ServerValueSet(['Storage','RobotRDPActive','OrchestratorToRobotResetStorage','ActivityList'],{json.dumps(lActivityFileRDP2OrchestratorDict)});"
|
||||||
|
# END :: Create activities :: END #
|
||||||
|
# START :: Init result dict template :: START #
|
||||||
|
lResultDict={
|
||||||
|
"HeaderLeftText":"Keep active RDP sessions",
|
||||||
|
"HeaderRightText":"Tech",
|
||||||
|
"DataStorageKey":"RobotRDPActive", #Use key for set current dict in mGlobal.DataStorage["DataStorageKey"] on client side
|
||||||
|
"SubheaderText":"<Subheader text>",
|
||||||
|
"BodyKeyValueList":[
|
||||||
|
{"Key":"Session list","Value":""}
|
||||||
|
],
|
||||||
|
"FooterText":"Last update: 9:38:00 09.10.2019",
|
||||||
|
"FooterButtonX2List":[
|
||||||
|
{"Text":"Turn on", "Color":"green", "Link":"", "OnClick": lActivityRDPStartEscaped},
|
||||||
|
{"Text":"Safe turn off", "Color":"orange", "Link":"", "OnClick": lActivityRDPSafeOffEscaped}
|
||||||
|
],
|
||||||
|
"FooterButtonX1List":[
|
||||||
|
{"Text":"Kill", "Color":"red", "Link":"", "OnClick": lActivityRDPKillEscaped}
|
||||||
|
]#,
|
||||||
|
#"GlobalStorage": inGSettings.get("Storage",{}) # UNCOMMENT FOR DEBUG PURPOSE TO WATCH inGSettings on client side
|
||||||
|
}
|
||||||
|
# END :: Init result dict template :: END #
|
||||||
|
# START :: Fill BodyKeyValueList :: START #
|
||||||
|
## Read RDPList
|
||||||
|
lRDPList = TechDictKeyList_ItemGet(inDict=inGSettings, inKeyList=gRobotToOrchestratorKeyList+["RDPList"], inDefault=[]):
|
||||||
|
lFullScreenSessionIndex = TechDictKeyList_ItemGet(inDict=inGSettings, inKeyList=gRobotToOrchestratorKeyList+["FullScreenSessionIndex"], inDefault=None):
|
||||||
|
lRDPListIndex = 0
|
||||||
|
for lItem in lRDPList:
|
||||||
|
### Lable that session has fullscreen
|
||||||
|
lLabelSessionFullScreen = ""
|
||||||
|
lLabelIsIgnored = ""
|
||||||
|
### Link set full screen
|
||||||
|
lOnClickSetFullScreen = f"mGlobal.Processor.ServerValueSet(['Storage','RobotRDPActive','OrchestratorToRobotStorage','FullScreenSessionIndex'],{lRDPListIndex});"
|
||||||
|
lSetFullScreenA = f'<a onclick="{lOnClickSetFullScreen}" style=\"color:blue\">Set fullscreen</a>'
|
||||||
|
if lRDPListIndex == lFullScreenSessionIndex:
|
||||||
|
lLabelSessionFullScreen = '<span style=\"color:blue\">[Fullscreen]</span>'
|
||||||
|
lOnClickSetFullScreen = f"mGlobal.Processor.ServerValueSet(['Storage','RobotRDPActive','OrchestratorToRobotStorage','FullScreenSessionIndex'],null);"
|
||||||
|
lSetFullScreenA = f'<a onclick="{lOnClickSetFullScreen}" style=\"color:blue\">Set minimized</a>'
|
||||||
|
lIgnoreIndexListOnClick = "$.ajax({type: 'POST', url: 'RobotRDPActive/IgnoreIndexListAppend', data: '"+str(lRDPListIndex)+"', success: function(lData,l2,l3){}, dataType: 'text'});"
|
||||||
|
lIgnoreIndexListLink = f'<a onclick="{lIgnoreIndexListOnClick}" style=\"color:red\">Ignore</a>'
|
||||||
|
### Check if in ignore
|
||||||
|
if lRDPListIndex in inGSettings.get("Storage",{}).get("RobotRDPActive",{}).get("OrchestratorToRobotStorage",{}).get("IgnoreIndexList",[]):
|
||||||
|
lLabelIsIgnored = '<span style=\"color:red\">[Ignored]</span>'
|
||||||
|
lIgnoreIndexListOnClick = "$.ajax({type: 'POST', url: 'RobotRDPActive/IgnoreIndexListRemove', data: '"+str(lRDPListIndex)+"', success: function(lData,l2,l3){}, dataType: 'text'});"
|
||||||
|
lIgnoreIndexListLink = f'<a onclick="{lIgnoreIndexListOnClick}" style=\"color:red\">Unignore</a>'
|
||||||
|
### Session state
|
||||||
|
lItemSessionState='<span style=\"color:red\">Disconnected</span>'
|
||||||
|
if lItem.get("SessionIsWindowResponsibleBool",False):
|
||||||
|
lItemSessionState='<span style=\"color:green\">Connected</span>'
|
||||||
|
lResultDict["BodyKeyValueList"].append({"Key":f"[{str(lRDPListIndex)}]{lLabelSessionFullScreen}{lLabelIsIgnored}{lItem.get('Host','localhost')}:{lItem.get('Port','--')}","Value":f"{lItem.get('Login','--')}, {lItem.get('SessionHex','--')}, State {lItemSessionState}, {lSetFullScreenA}, {lIgnoreIndexListLink}"})
|
||||||
|
lRDPListIndex = lRDPListIndex + 1
|
||||||
|
# END :: Fill BodyKeyValueList :: END #
|
||||||
|
# START :: Fill SubheaderText :: START #
|
||||||
|
## Variants
|
||||||
|
lSubheaderRunTrueText="State: <span style=\"color:green\">Turned on</span>"
|
||||||
|
lSubheaderRunFalseText="State: <span style=\"color:red\">Turned off</span>"
|
||||||
|
if CheckIfProcessRunning(gProcessNameWOEXE):
|
||||||
|
lResultDict["SubheaderText"]=lSubheaderRunTrueText
|
||||||
|
else:
|
||||||
|
lResultDict["SubheaderText"]=lSubheaderRunFalseText
|
||||||
|
# END :: Fill SubheaderText :: END #
|
||||||
|
# Fill FooterText
|
||||||
|
lResultDict["FooterText"]=f'Last update: {datetime.datetime.now().strftime("%H:%M:%S %d.%m.%Y")}'
|
||||||
|
return lResultDict
|
||||||
|
# Check in control panel, that process is runnning
|
||||||
|
def CheckIfProcessRunning(processName):
|
||||||
|
'''Check if there is any running process that contains the given name processName.'''
|
||||||
|
#Iterate over the all the running process
|
||||||
|
for proc in psutil.process_iter():
|
||||||
|
try:
|
||||||
|
# Check if process name contains the given name string.
|
||||||
|
if processName.lower() in proc.name().lower():
|
||||||
|
return True
|
||||||
|
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
#Add to ignore list - AJAX request from client side
|
||||||
|
def IgnoreIndexListAppend(inRequest,inConfiguration):
|
||||||
|
lIgnoreList = TechDictKeyList_ItemGet(inDict=inConfiguration, inKeyList=gOrchestratorToRobotKeyList+["IgnoreIndexList"], inDefault=[]):
|
||||||
|
lIgnoreIndex={}
|
||||||
|
if inRequest.headers.get('Content-Length') is not None:
|
||||||
|
lInputByteArrayLength = int(inRequest.headers.get('Content-Length'))
|
||||||
|
lInputByteArray=inRequest.rfile.read(lInputByteArrayLength)
|
||||||
|
#Превращение массива байт в объект
|
||||||
|
lIgnoreIndex=int(lInputByteArray.decode('utf8'))
|
||||||
|
#check if index not in list
|
||||||
|
if lIgnoreIndex not in lIgnoreList:
|
||||||
|
#append to list
|
||||||
|
lIgnoreList.append(lIgnoreIndex)
|
||||||
|
#remove from Ignore list - AJAX request from client side
|
||||||
|
def IgnoreIndexListRemove(inRequest,inConfiguration):
|
||||||
|
lIgnoreList = TechDictKeyList_ItemGet(inDict=inConfiguration, inKeyList=gOrchestratorToRobotKeyList+["IgnoreIndexList"], inDefault=[]):
|
||||||
|
lIgnoreIndex={}
|
||||||
|
if inRequest.headers.get('Content-Length') is not None:
|
||||||
|
lInputByteArrayLength = int(inRequest.headers.get('Content-Length'))
|
||||||
|
lInputByteArray=inRequest.rfile.read(lInputByteArrayLength)
|
||||||
|
#Превращение массива байт в объект
|
||||||
|
lIgnoreIndex=int(lInputByteArray.decode('utf8'))
|
||||||
|
#check if index not in list
|
||||||
|
if lIgnoreIndex in lIgnoreList:
|
||||||
|
#append to list
|
||||||
|
lIgnoreList.remove(lIgnoreIndex) #Remove delete the element lIgnoreIndex
|
||||||
|
# Technical def - Get item by the list of keys
|
||||||
|
def TechDictKeyList_ItemGet(inDict, inKeyList, inDefault={}):
|
||||||
|
lResult=inDict
|
||||||
|
for lItem in inKeyList:
|
||||||
|
if lResult:
|
||||||
|
lResult = lResult.get(lItem,None)
|
||||||
|
if not lResult:
|
||||||
|
lResult=inDefault
|
||||||
|
return lResult
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
#Orchestrator settings
|
||||||
|
def SettingsUpdate(inGSettings):
|
||||||
|
#Add RobotRDPActive in control panel
|
||||||
|
inGSettings["ControlPanelDict"]["RobotList"].append({"RenderFunction": ControlPanelRenderDict})
|
||||||
|
#Default structure
|
||||||
|
inGSettings["Storage"]["RobotRDPActive"]={
|
||||||
|
"OrchestratorToRobotResetStorage":{"SafeTurnOff":False},
|
||||||
|
"OrchestratorToRobotStorage":{
|
||||||
|
"FullScreenSessionIndex":None,
|
||||||
|
"IgnoreIndexList": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#Add methods
|
||||||
|
inGSettings["Server"]["URLList"].append(
|
||||||
|
{
|
||||||
|
"Method":"POST",
|
||||||
|
"URL": "/RobotRDPActive/IgnoreIndexListAppend", #URL of the request
|
||||||
|
"MatchType": "Equal", #"BeginWith|Contains|Equal|EqualCase",
|
||||||
|
"ResponseDefRequestGlobal": IgnoreIndexListAppend #Function with str result
|
||||||
|
}
|
||||||
|
)
|
||||||
|
inGSettings["Server"]["URLList"].append(
|
||||||
|
{
|
||||||
|
"Method":"POST",
|
||||||
|
"URL": "/RobotRDPActive/IgnoreIndexListRemove", #URL of the request
|
||||||
|
"MatchType": "Equal", #"BeginWith|Contains|Equal|EqualCase",
|
||||||
|
"ResponseDefRequestGlobal": IgnoreIndexListRemove #Function with str result
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return inGSettings
|
@ -1,3 +1,12 @@
|
|||||||
1. Create user for Orchestrator console session (pay attention for administrator rights - is needed for RobotRDPActive) - how to create user look windows instructions
|
1. Create user for Orchestrator console session (pay attention for administrator rights - is needed for RobotRDPActive) - how to create user look windows instructions
|
||||||
2. Create AutoAdminLogon in windows for Orchestrator user - go in console session when restart (Utils/AutoLogon(ProtectedPassword).zip) - https://docs.microsoft.com/en-us/sysinternals/downloads/autologon
|
2. Create AutoAdminLogon in windows for Orchestrator user - go in console session when restart (Utils/AutoLogon(ProtectedPassword).zip) - https://docs.microsoft.com/en-us/sysinternals/downloads/autologon
|
||||||
3. Add Orchestrator in startup - win+r > shell:startup > add shortcut of orchestrator
|
3. Add Orchestrator in startup - win+r > shell:startup > add shortcut of orchestrator. Add shortcut to pyOpenRPA.Orchestrator_x64_administrator_startup.cmd if you want to auto start orchestrator as administrator
|
||||||
|
|
||||||
|
Screen active is require administrator rights because of tscon cmd usage (to switch to console desktop session after the RDP).
|
||||||
|
! ATTENTION ! Screen active can be used only with local administrator rights on the machine
|
||||||
|
|
||||||
|
OpenRPA suggest 2 ways to automate run
|
||||||
|
Way 1 (create task in windows task scheduler to run with extended rights ScreenActive or Orchestrator)
|
||||||
|
|
||||||
|
Way 2 (use shortcut ScreenActive| Orchestrator to run with administrator rights in shortcut options)
|
||||||
|
To turn off UAC window got to Menu > Change User Account Control Settings > Select Never notify
|
Binary file not shown.
@ -0,0 +1,2 @@
|
|||||||
|
cd %~dp0
|
||||||
|
start pyOpenRPA.Orchestrator_x64_shortcut_admin.lnk
|
Loading…
Reference in new issue