From 4741b53083eb2a44521cc496a749e8b3d733917e Mon Sep 17 00:00:00 2001 From: Ivan Maslov Date: Sat, 22 Jan 2022 09:10:39 +0300 Subject: [PATCH] process - add StatusSave / Restore, fix some troubles in StopSafe and StatusCheck --- .../Orchestrator/Managers/Process.py | 99 +++++++++++++++---- 1 file changed, 82 insertions(+), 17 deletions(-) diff --git a/Sources/pyOpenRPA/Orchestrator/Managers/Process.py b/Sources/pyOpenRPA/Orchestrator/Managers/Process.py index cb0abba9..2c7b232d 100644 --- a/Sources/pyOpenRPA/Orchestrator/Managers/Process.py +++ b/Sources/pyOpenRPA/Orchestrator/Managers/Process.py @@ -49,6 +49,8 @@ class Process(): mAgentMuteBool = False # Mute any sends to agent while some action is perfomed + mStatusSavedStr = None # Saved status to the further restore + def MuteWait(self): """ Internal def. Wait when class is apply to send new activities to the agent @@ -178,14 +180,16 @@ class Process(): self.Start(inIsManualBool=False) return self.mStatusStr - def StopSafe(self, inIsManualBool = True) -> str: + def StopSafe(self, inIsManualBool = True, inStopSafeTimeoutSecFloat = None) -> str: """ Manual/Auto stop safe. Stop safe is the operation which send signal to process to terminate own work (send term signal to process). Managers.Process wait for the mStopSafeTimeoutSecFloat seconds. After that, if process is not terminated - self will StopForce it. Manual stop safe will block scheduling execution. To return schedule execution use def Manual2Auto :param inIsManualBool: Default is True - Mark this operation as manual - StatusCheckStart/Stop will be blocked - only StatusCheck will be working. False - Auto operation + :param inStopSafeTimeoutSecFloat: Default value goes from the instance. You can specify time is second to wait while safe stop. After that program will stop force :return: Process status. See self.mStatusStr. 0_STOPPED 1_STOPPED_MANUAL 2_STOP_SAFE 3_STOP_SAFE_MANUAL 4_STARTED 5_STARTED_MANUAL """ + if inStopSafeTimeoutSecFloat is None: inStopSafeTimeoutSecFloat = self.mStopSafeTimeoutSecFloat self.MuteWait() self.mAgentMuteBool=True # Send activity item to agent - wait result @@ -205,31 +209,33 @@ class Process(): # Interval check is stopped lTimeStartFloat = time.time() lIntervalCheckSafeStatusFLoat = 15.0 - while "SAFE" in self.mStatusStr and (time.time() - lTimeStartFloat) < self.mStopSafeTimeoutSecFloat: + while "SAFE" in self.mStatusStr and (time.time() - lTimeStartFloat) < inStopSafeTimeoutSecFloat: self.StatusCheck() + if "SAFE" not in self.mStatusStr: break time.sleep(lIntervalCheckSafeStatusFLoat) if "SAFE" in self.mStatusStr: # Log info about process lL = __Orchestrator__.OrchestratorLoggerGet() - lL.info(f"Managers.Process ({self.mAgentHostNameStr}, {self.mAgentUserNameStr}, {self.mProcessNameWOExeStr}): Safe stop has been wait for {self.mStopSafeTimeoutSecFloat} sec. Now do the force stop.") - self.StopForce(inIsManualBool=inIsManualBool) + lL.info(f"Managers.Process ({self.mAgentHostNameStr}, {self.mAgentUserNameStr}, {self.mProcessNameWOExeStr}): Safe stop has been wait for {inStopSafeTimeoutSecFloat} sec. Now do the force stop.") + self.StopForce(inIsManualBool=inIsManualBool,inMuteIgnoreBool=True) # Log info about process - self.StatusChangeLog() - self.mAgentMuteBool=False + # self.StatusChangeLog() status check has already log status (see above) + self.mAgentMuteBool = False return self.mStatusStr - def StopSafeCheck(self) -> str: + def StopSafeCheck(self, inStopSafeTimeoutSecFloat = None) -> str: """ Stop safe program if auto started (4_STARTED). + :param inStopSafeTimeoutSecFloat: Default value goes from the instance. You can specify time is second to wait while safe stop. After that program will stop force :return: Process status. See self.mStatusStr. 0_STOPPED 1_STOPPED_MANUAL 2_STOP_SAFE 3_STOP_SAFE_MANUAL 4_STARTED 5_STARTED_MANUAL """ self.MuteWait() if self.mStatusStr == "4_STARTED": - self.StopSafe(inIsManualBool=False) + self.StopSafe(inIsManualBool=False, inStopSafeTimeoutSecFloat = inStopSafeTimeoutSecFloat) return self.mStatusStr - def StopForce(self, inIsManualBool = True) -> str: + def StopForce(self, inIsManualBool = True, inMuteIgnoreBool = False) -> str: """ Manual/Auto stop force. Force stop don't wait process termination - it just terminate process now. Manual stop safe will block scheduling execution. To return schedule execution use def Manual2Auto @@ -237,8 +243,9 @@ class Process(): :param inIsManualBool: Default is True - Mark this operation as manual - StatusCheckStart/Stop will be blocked - only StatusCheck will be working. False - Auto operation :return: Process status. See self.mStatusStr. 0_STOPPED 1_STOPPED_MANUAL 2_STOP_SAFE 3_STOP_SAFE_MANUAL 4_STARTED 5_STARTED_MANUAL """ - self.MuteWait() - self.mAgentMuteBool=True + if inMuteIgnoreBool == False: self.MuteWait() + lMuteWorkBool = False + if self.mAgentMuteBool==False: self.mAgentMuteBool=True; lMuteWorkBool=True # Send activity item to agent - wait result lCMDStr = f'taskkill /F /im "{self.mProcessNameWOExeStr}.exe" /fi "username eq %USERNAME%"' lActivityItemStart = __Orchestrator__.ProcessorActivityItemCreate( @@ -253,7 +260,8 @@ class Process(): self.mStatusStr = "0_STOPPED" # Log info about process self.StatusChangeLog() - self.mAgentMuteBool=False + if lMuteWorkBool == True: + self.mAgentMuteBool=False return self.mStatusStr def StopForceCheck(self) -> str: @@ -289,6 +297,37 @@ class Process(): self.StopForce(inIsManualBool=inIsManualBool) return self.Start(inIsManualBool=inIsManualBool) + def StatusSave(self): + """ + Save current status of the process. After that you can restore process activity. Work when orchestrator is restarted. Don't save "STOP_SAFE" status > "STOPPED" + + :return: Process status. See self.mStatusStr. 0_STOPPED 1_STOPPED_MANUAL 2_STOP_SAFE 3_STOP_SAFE_MANUAL 4_STARTED 5_STARTED_MANUAL + """ + lWarnSafeBool = True + if self.mStatusStr == "2_STOP_SAFE": self.mStatusSavedStr = "0_STOPPED" + elif self.mStatusStr == "3_STOP_SAFE_MANUAL": self.mStatusSavedStr = "1_STOPPED_MANUAL" + else: self.mStatusSavedStr = self.mStatusStr; lWarnSafeBool = False + if lWarnSafeBool==True: __Orchestrator__.OrchestratorLoggerGet().warning(f"Managers.Process ({self.mAgentHostNameStr}, {self.mAgentUserNameStr}, {self.mProcessNameWOExeStr}): Safe status has been catched when safe > change saved status to stopped.") + return self.mStatusStr + + def StatusRestore(self): + """ + Execute the StatusCheck, after that restore status to the saved state (see StatusSave). Work when orchestrator is restarted. + + :return: Process status. See self.mStatusStr. 0_STOPPED 1_STOPPED_MANUAL 2_STOP_SAFE 3_STOP_SAFE_MANUAL 4_STARTED 5_STARTED_MANUAL + """ + self.StatusCheck() # check current status + # Do some action + if self.mStatusSavedStr != self.mStatusStr: + lManualBool = False + if "MANUAL" in self.mStatusSavedStr: + lManualBool = True + if "STOPPED" in self.mStatusSavedStr and "STOPPED" not in self.mStatusStr: + self.StopSafe(inIsManualBool=lManualBool) + if "STARTED" in self.mStatusSavedStr and "STARTED" not in self.mStatusStr: + self.Start(inIsManualBool=lManualBool) + return self.mStatusStr + def StatusChangeLog(self): """ Lof information about status change @@ -302,15 +341,16 @@ class Process(): def StatusCheck(self): """ - Check if process is alive. The def will save the manual flag is exists. + Check if process is alive. The def will save the manual flag is exists. Don't wait mute but set mute if it is not set. :return: Process status. See self.mStatusStr. 0_STOPPED 1_STOPPED_MANUAL 2_STOP_SAFE 3_STOP_SAFE_MANUAL 4_STARTED 5_STARTED_MANUAL """ # Send activity item to agent - wait result lLogBool = False lActivityItemUserProcessList = __Orchestrator__.ProcessorActivityItemCreate(inDef="ProcessWOExeUpperUserListGet") - self.MuteWait() - self.mAgentMuteBool=True + #self.MuteWait() + lMuteWorkBool = False + if self.mAgentMuteBool==False: self.mAgentMuteBool=True; lMuteWorkBool=True lGUIDStr = __Orchestrator__.AgentActivityItemAdd(inHostNameStr=self.mAgentHostNameStr,inUserStr=self.mAgentUserNameStr,inActivityItemDict=lActivityItemUserProcessList) lUserProcessList = __Orchestrator__.AgentActivityItemReturnGet(inGUIDStr=lGUIDStr) if self.mProcessNameWOExeStr.upper() in lUserProcessList: @@ -325,7 +365,7 @@ class Process(): if self.mStatusStr is None: self.mStatusStr = "0_STOPPED"; lLogBool=True # Log info about process if lLogBool == True: self.StatusChangeLog() - self.mAgentMuteBool = False + if lMuteWorkBool==True: self.mAgentMuteBool = False return self.mStatusStr def StatusCheckStart(self): """ @@ -422,7 +462,7 @@ def ProcessStart(inAgentHostNameStr: str, inAgentUserNameStr: str, inProcessName if lProcess is not None: return lProcess.Start(inIsManualBool=inIsManualBool) -def ProcessStopSafe(inAgentHostNameStr: str, inAgentUserNameStr: str, inProcessNameWOExeStr: str, inIsManualBool: bool = True) -> str: +def ProcessStopSafe(inAgentHostNameStr: str, inAgentUserNameStr: str, inProcessNameWOExeStr: str, inIsManualBool: bool = True, inStopSafeTimeoutSecFloat = None) -> str: """ Manual/Auto stop safe. Stop safe is the operation which send signal to process to terminate own work (send term signal to process). Managers.Process wait for the mStopSafeTimeoutSecFloat seconds. After that, if process is not terminated - self will StopForce it. Manual stop safe will block scheduling execution. To return schedule execution use def Manual2Auto @@ -431,6 +471,7 @@ def ProcessStopSafe(inAgentHostNameStr: str, inAgentUserNameStr: str, inProcessN :param inAgentUserNameStr: Agent user name in any case. Required to identify Process :param inProcessNameWOExeStr: The process name without extension .exe (the key of the Process instance). Any case - will be processed to the upper case :param inIsManualBool: Default is True - Mark this operation as manual - StatusCheckStart/Stop will be blocked - only StatusCheck will be working. False - Auto operation + :param inStopSafeTimeoutSecFloat: Default value goes from the instance. You can specify time is second to wait while safe stop. After that program will stop force :return: Process status. See self.mStatusStr. Process instance has the following statuses: - 0_STOPPED @@ -466,6 +507,30 @@ def ProcessStopForce(inAgentHostNameStr: str, inAgentUserNameStr: str, inProcess lProcess = ProcessGet(inAgentHostNameStr = inAgentHostNameStr, inAgentUserNameStr = inAgentUserNameStr, inProcessNameWOExeStr=inProcessNameWOExeStr) if lProcess is not None: return lProcess.StopForce(inIsManualBool=inIsManualBool) +def ProcessStatusSave(inAgentHostNameStr: str, inAgentUserNameStr: str, inProcessNameWOExeStr: str): + """ + Save current status of the process. After that you can restore process activity. Work when orchestrator is restarted. Don't save "STOP_SAFE" status > "STOPPED" + + :return: Process status. See self.mStatusStr. 0_STOPPED 1_STOPPED_MANUAL 2_STOP_SAFE 3_STOP_SAFE_MANUAL 4_STARTED 5_STARTED_MANUAL + """ + lProcess = ProcessGet(inAgentHostNameStr=inAgentHostNameStr, inAgentUserNameStr=inAgentUserNameStr, + inProcessNameWOExeStr=inProcessNameWOExeStr) + if lProcess is not None: + lProcess.StatusSave() + return lProcess.mStatusStr + +def ProcessStatusRestore(inAgentHostNameStr: str, inAgentUserNameStr: str, inProcessNameWOExeStr: str): + """ + Execute the StatusCheck, after that restore status to the saved state (see StatusSave). Work when orchestrator is restarted. + + :return: Process status. See self.mStatusStr. 0_STOPPED 1_STOPPED_MANUAL 2_STOP_SAFE 3_STOP_SAFE_MANUAL 4_STARTED 5_STARTED_MANUAL + """ + lProcess = ProcessGet(inAgentHostNameStr=inAgentHostNameStr, inAgentUserNameStr=inAgentUserNameStr, + inProcessNameWOExeStr=inProcessNameWOExeStr) + if lProcess is not None: + lProcess.StatusRestore() + return lProcess.mStatusStr + def ProcessStatusCheck(inAgentHostNameStr: str, inAgentUserNameStr: str, inProcessNameWOExeStr: str) -> str: """ Check if process is alive. The def will save the manual flag is exists.