Jump to content

Recommended Posts

Posted

I want to share a framework that I am using in my "Taskbar Program" project.

; ===============================================================================================================================
; SCRIPT:         Controller-Worker-Task Manager Framework
; AUTHOR:         Dao Van Trong - TRONG.PRO
; VERSION:        4.0 (Optimized, INI-driven, Tray-Only)
; DESCRIPTION:    This script implements a robust Controller-Worker-Task architecture for running background processes.
;                 It is designed to be a flexible framework for automation tasks.
;
; ARCHITECTURE:
;   - Controller: The main process that runs persistently. It has no GUI and is controlled entirely via a system tray icon.
;                 Its responsibilities include:
;                   1. Reading the configuration from 'Configs.ini'.
;                   2. Creating and managing the tray menu.
;                   3. Starting, stopping, and monitoring Worker processes.
;                   4. Launching one-shot Task processes.
;                   5. Automatically reloading the configuration when 'Configs.ini' is modified.
;
;   - Workers:    Long-running background processes that perform recurring tasks (e.g., monitoring, syncing).
;                 They are started and stopped by the Controller and run completely hidden.
;                 Each worker monitors the Controller and will self-terminate if the Controller exits.
;
;   - Tasks:      Short-lived background processes that perform a single, one-shot action (e.g., generate a report).
;                 They can be configured to run exclusively (locking all other tasks) or concurrently.
;                 They can also be configured to run as a separate sub-process or directly within the Controller's process.
;
; HOW IT WORKS:
;   - IPC (Inter-Process Communication): Communication between the Controller and Workers is handled via the Windows Registry.
;     The Controller writes a signal to a specific registry key to gracefully shut down a Worker.
;   - Asynchronous Operations: All monitoring (process status, config file changes) and Worker functions are handled
;     asynchronously using AdlibRegister, ensuring the Controller remains responsive.
;   - Configuration: All Workers and Tasks are defined in the master arrays within this script, but they are enabled,
;     disabled, and configured via an external 'Configs.ini' file. This allows for easy customization without
;     modifying the source code.
;   - Logging: The script generates a detailed log file ('manager.log') in the same directory, recording all major
;     events like startup, shutdown, worker status changes, and errors.
; ===============================================================================================================================

#include <TrayConstants.au3>
#include <Array.au3>
#include <File.au3>
#include <FileConstants.au3>

; ===============================================================================================================================
; --- GLOBAL CONSTANTS AND VARIABLES
; ===============================================================================================================================

; --- Settings ---
Global Const $g_sConfigFile = @ScriptDir & "\Configs.ini"
Global Const $g_sLogFile = @ScriptDir & "\Manager.log"
Global $g_sAppName = "Controller-Worker-Task Manager"
Global $g_iCheckConfigInterval = 10000
Global $g_sLastConfigModDate = ""

; --- Registry & IPC ---
Global Const $g_sRegBase = "HKCU\Software\ControllerWorkerApp"
Global Const $g_iAdlibCheckInterval = 1000
Global Const $g_iGracefulShutdownTimeout = 3000

; --- Status Constants for internal logic ---
Global Const $STATUS_STOPPED = 0, $STATUS_RUNNING = 1, $STATUS_ERROR = 2

; --- Task Execution Mode & Type Constants ---
Global Const $TASK_MODE_EXCLUSIVE = 0, $TASK_MODE_CONCURRENT = 1
Global Const $TASK_RUN_TYPE_SUBAPP = 0, $TASK_RUN_TYPE_DIRECT = 1

; --- Menu Type Constants ---
Global Const $MENU_TYPE_MAIN = 0, $MENU_TYPE_SUB = 1

; --- MASTER DATA STRUCTURES (Hard-coded definitions) ---
; [InternalName, DisplayName, FunctionName, MenuType]
Global Const $g_aMasterWorkers[10][4] = [ _
        ["CheckResources", "Check Resources", "Worker1Function", $MENU_TYPE_MAIN], _
        ["SyncFiles", "Sync Files", "Worker2Function", $MENU_TYPE_MAIN], _
        ["MonitorNetwork", "Monitor Network", "Worker3Function", $MENU_TYPE_MAIN], _
        ["BackupDB", "Backup DB", "Worker4Function", $MENU_TYPE_SUB], _
        ["CleanupLogs", "Cleanup Logs", "Worker5Function", $MENU_TYPE_SUB], _
        ["ProcessEmails", "Process Emails", "Worker6Function", $MENU_TYPE_SUB], _
        ["AutoScreenshot", "Auto Screenshot", "Worker7Function", $MENU_TYPE_SUB], _
        ["MonitorTemp", "Monitor Temp", "Worker8Function", $MENU_TYPE_SUB], _
        ["CheckUpdates", "Check Updates", "Worker9Function", $MENU_TYPE_SUB], _
        ["SecurityScan", "Security Scan", "Worker10Function", $MENU_TYPE_SUB] _
        ]
; [InternalName, DisplayName, FunctionName, MenuType, ExecutionMode, RunType]
Global Const $g_aMasterTasks[10][6] = [ _
        ["QuickCleanup", "Quick Cleanup", "TaskA_Function", $MENU_TYPE_MAIN, $TASK_MODE_EXCLUSIVE, $TASK_RUN_TYPE_SUBAPP], _
        ["GenerateReport", "Generate Report", "TaskB_Function", $MENU_TYPE_MAIN, $TASK_MODE_EXCLUSIVE, $TASK_RUN_TYPE_SUBAPP], _
        ["SendNotification", "Send Notification (Direct)", "TaskJ_Function", $MENU_TYPE_MAIN, $TASK_MODE_EXCLUSIVE, $TASK_RUN_TYPE_DIRECT], _
        ["TaskC", "Run Task C (Direct)", "TaskC_Function", $MENU_TYPE_SUB, $TASK_MODE_CONCURRENT, $TASK_RUN_TYPE_DIRECT], _
        ["TaskD", "Run Task D", "TaskD_Function", $MENU_TYPE_SUB, $TASK_MODE_CONCURRENT, $TASK_RUN_TYPE_SUBAPP], _
        ["TaskE", "Run Task E", "TaskE_Function", $MENU_TYPE_SUB, $TASK_MODE_CONCURRENT, $TASK_RUN_TYPE_SUBAPP], _
        ["TaskF", "Run Task F", "TaskF_Function", $MENU_TYPE_SUB, $TASK_MODE_CONCURRENT, $TASK_RUN_TYPE_SUBAPP], _
        ["TaskG", "Run Task G", "TaskG_Function", $MENU_TYPE_SUB, $TASK_MODE_CONCURRENT, $TASK_RUN_TYPE_SUBAPP], _
        ["TaskH", "Run Task H", "TaskH_Function", $MENU_TYPE_SUB, $TASK_MODE_CONCURRENT, $TASK_RUN_TYPE_SUBAPP], _
        ["TaskI", "Run Task I", "TaskI_Function", $MENU_TYPE_SUB, $TASK_MODE_CONCURRENT, $TASK_RUN_TYPE_SUBAPP] _
        ]

; --- Dynamic Data Structures (Populated from Master based on INI) ---
Global $g_aWorkers, $g_iNumWorkers
Global $g_aTasks, $g_iNumTasks

; --- Tray Menu Control Variables ---
Global $g_aWorkerTrayItems[1], $g_aTaskTrayItems[1], $g_iTrayExit

; --- Shared Global Variables for Worker processes ---
Global $g_sWorkerInternalName, $g_iControllerPID, $g_sWorkerRegKey


; ===============================================================================================================================
; --- MAIN SCRIPT LOGIC
; ===============================================================================================================================

_Main()

;/**
; * @brief Main entry point of the script.
; *
; * Determines whether to run as the Controller or as a sub-process (Worker/Task)
; * based on the command-line arguments.
; */
Func _Main()
    If $CmdLine[0] > 0 Then
        Local $aCmd = StringSplit($CmdLine[1], ":")
        If $aCmd[0] < 2 Then Exit
        Local $sType = $aCmd[1], $sInternalName = $aCmd[2], $iControllerPID = ($CmdLine[0] > 1 ? $CmdLine[2] : 0)
        Switch $sType
            Case "worker"
                _RunAsWorker($sInternalName, $iControllerPID)
            Case "task"
                _RunAsTask($sInternalName)
        EndSwitch
    Else
        _RunAsController()
    EndIf
EndFunc   ;==>_Main


; ===============================================================================================================================
; --- CONTROLLER FUNCTIONS
; ===============================================================================================================================

;/**
; * @brief Initializes the Controller process.
; *
; * This function sets up the entire Controller environment:
; * 1. Cleans up any leftover registry keys from previous runs.
; * 2. Loads the configuration from Configs.ini.
; * 3. Creates the tray menu.
; * 4. Registers Adlib functions for background monitoring.
; * 5. Starts the main event loop.
; */
Func _RunAsController()
    _CleanupAllRegistryKeys()
    _LoadConfiguration()
    _CreateTrayMenu()
    AdlibRegister("_CheckStatus_Adlib", $g_iAdlibCheckInterval)
    AdlibRegister("_CheckForConfigChanges_Adlib", $g_iCheckConfigInterval)
    _WriteLog("INFO: Controller started successfully.")
    _ControllerMainLoop()
EndFunc   ;==>_RunAsController

;/**
; * @brief Loads and parses the configuration from Configs.ini.
; *
; * If Configs.ini exists, it reads the settings and the disable lists.
; * If not, it uses the default configuration (all master items enabled).
; * It then populates the dynamic $g_aWorkers and $g_aTasks arrays with only the enabled items.
; * This function is optimized to count items first, then Dim the arrays once to avoid ReDim in a loop.
; */
Func _LoadConfiguration()
    Local $sDisabledWorkers = "", $sDisabledTasks = ""
    If FileExists($g_sConfigFile) Then
        $g_sLastConfigModDate = FileGetTime($g_sConfigFile, $FT_MODIFIED, $FT_STRING)
        $g_sAppName = IniRead($g_sConfigFile, "Settings", "AppName", "Controller Framework")
        $g_iCheckConfigInterval = IniRead($g_sConfigFile, "Settings", "CheckConfigInterval", 10000)
        $sDisabledWorkers = "," & IniRead($g_sConfigFile, "Workers", "Disable", "") & ","
        $sDisabledTasks = "," & IniRead($g_sConfigFile, "Tasks", "Disable", "") & ","
    Else
        $g_sLastConfigModDate = ""
        $sDisabledWorkers = ","
        $sDisabledTasks = ","
    EndIf

    Local $iWorkerCount = 0, $iTaskCount = 0
    For $i = 0 To UBound($g_aMasterWorkers) - 1
        If Not StringInStr($sDisabledWorkers, "," & $g_aMasterWorkers[$i][0] & ",") Then $iWorkerCount += 1
    Next
    For $i = 0 To UBound($g_aMasterTasks) - 1
        If Not StringInStr($sDisabledTasks, "," & $g_aMasterTasks[$i][0] & ",") Then $iTaskCount += 1
    Next

    $g_iNumWorkers = $iWorkerCount
    $g_iNumTasks = $iTaskCount
    Dim $g_aWorkers[$g_iNumWorkers][7]
    Dim $g_aTasks[$g_iNumTasks][7]

    Local $iWorkerIdx = 0, $iTaskIdx = 0
    For $i = 0 To UBound($g_aMasterWorkers) - 1
        Local $sInternalName = $g_aMasterWorkers[$i][0]
        If StringInStr($sDisabledWorkers, "," & $sInternalName & ",") Then ContinueLoop
        $g_aWorkers[$iWorkerIdx][0] = $sInternalName
        $g_aWorkers[$iWorkerIdx][1] = $g_aMasterWorkers[$i][1]
        $g_aWorkers[$iWorkerIdx][2] = $g_aMasterWorkers[$i][2]
        $g_aWorkers[$iWorkerIdx][3] = 0
        $g_aWorkers[$iWorkerIdx][4] = False
        $g_aWorkers[$iWorkerIdx][5] = $STATUS_STOPPED
        $g_aWorkers[$iWorkerIdx][6] = $g_aMasterWorkers[$i][3]
        $iWorkerIdx += 1
    Next
    For $i = 0 To UBound($g_aMasterTasks) - 1
        Local $sInternalName = $g_aMasterTasks[$i][0]
        If StringInStr($sDisabledTasks, "," & $sInternalName & ",") Then ContinueLoop
        $g_aTasks[$iTaskIdx][0] = $sInternalName
        $g_aTasks[$iTaskIdx][1] = $g_aMasterTasks[$i][1]
        $g_aTasks[$iTaskIdx][2] = $g_aMasterTasks[$i][2]
        $g_aTasks[$iTaskIdx][3] = 0
        $g_aTasks[$iTaskIdx][4] = $g_aMasterTasks[$i][4]
        $g_aTasks[$iTaskIdx][5] = $g_aMasterTasks[$i][5]
        $g_aTasks[$iTaskIdx][6] = $g_aMasterTasks[$i][3]
        $iTaskIdx += 1
    Next
EndFunc   ;==>_LoadConfiguration

;/**
; * @brief Creates the entire tray menu structure based on the loaded configuration.
; *
; * It iterates through the dynamic $g_aWorkers and $g_aTasks arrays and creates
; * main menu items or sub-menu items based on their MenuType property.
; * This ensures that the menu item index directly corresponds to the data array index.
; */
Func _CreateTrayMenu()
    Opt("TrayMenuMode", 3)
    TraySetToolTip($g_sAppName)
    ReDim $g_aWorkerTrayItems[$g_iNumWorkers]
    ReDim $g_aTaskTrayItems[$g_iNumTasks]

    Local $hSubWorkersMenu = 0, $hSubTasksMenu = 0
    For $i = 0 To $g_iNumWorkers - 1
        If $g_aWorkers[$i][6] = $MENU_TYPE_MAIN Then
            $g_aWorkerTrayItems[$i] = TrayCreateItem("[OFF] " & $g_aWorkers[$i][1])
        Else
            If $hSubWorkersMenu = 0 Then $hSubWorkersMenu = TrayCreateMenu("Sub Workers")
            $g_aWorkerTrayItems[$i] = TrayCreateItem("[OFF] " & $g_aWorkers[$i][1], $hSubWorkersMenu)
        EndIf
    Next
    TrayCreateItem("")
    For $i = 0 To $g_iNumTasks - 1
        If $g_aTasks[$i][6] = $MENU_TYPE_MAIN Then
            $g_aTaskTrayItems[$i] = TrayCreateItem($g_aTasks[$i][1])
        Else
            If $hSubTasksMenu = 0 Then $hSubTasksMenu = TrayCreateMenu("Sub Tasks")
            $g_aTaskTrayItems[$i] = TrayCreateItem($g_aTasks[$i][1], $hSubTasksMenu)
        EndIf
    Next
    TrayCreateItem("")
    $g_iTrayExit = TrayCreateItem("Exit")
    TraySetState(1)
EndFunc   ;==>_CreateTrayMenu

;/**
; * @brief The main event loop for the Controller.
; *
; * It waits for and dispatches tray menu click events to the appropriate handler functions.
; */
Func _ControllerMainLoop()
    While 1
        Local $iTrayMsg = TrayGetMsg()
        Switch $iTrayMsg
            Case 0
            Case $g_iTrayExit
                _ExitController()
            Case Else
                Local $iIndex = _GetIndexFromTrayID($iTrayMsg, $g_aWorkerTrayItems)
                If $iIndex <> -1 Then
                    _HandleWorkerClick($iIndex)
                    ContinueLoop
                EndIf
                $iIndex = _GetIndexFromTrayID($iTrayMsg, $g_aTaskTrayItems)
                If $iIndex <> -1 Then
                    _HandleTaskClick($iIndex)
                    ContinueLoop
                EndIf
        EndSwitch
        Sleep(100)
    WEnd
EndFunc   ;==>_ControllerMainLoop

;/**
; * @brief Handles a click on a Worker menu item.
; * @param $iIndex The index of the clicked worker in the $g_aWorkers array.
; */
Func _HandleWorkerClick($iIndex)
    _UpdateWorkerState($iIndex, Not $g_aWorkers[$iIndex][4])
EndFunc   ;==>_HandleWorkerClick

;/**
; * @brief Handles a click on a Task menu item.
; * @param $iIndex The index of the clicked task in the $g_aTasks array.
; */
Func _HandleTaskClick($iIndex)
    _RunTask($iIndex)
EndFunc   ;==>_HandleTaskClick

;/**
; * @brief Central function to change a worker's state (on/off).
; * @param $iIndex The index of the worker.
; * @param $bNewState The new state to apply (True for ON, False for OFF).
; */
Func _UpdateWorkerState($iIndex, $bNewState)
    $g_aWorkers[$iIndex][4] = $bNewState
    If $bNewState Then
        _StartWorker($iIndex)
    Else
        _StopWorker($iIndex)
    EndIf
    _UpdateTrayItemForWorker($iIndex)
EndFunc   ;==>_UpdateWorkerState

;/**
; * @brief Updates a worker's tray menu item text and checked state.
; * @param $iIndex The index of the worker.
; */
Func _UpdateTrayItemForWorker($iIndex)
    Local $sPrefix, $iTrayState
    If $g_aWorkers[$iIndex][4] Then
        $sPrefix = "[ON] "
        $iTrayState = $TRAY_CHECKED
    Else
        $sPrefix = "[OFF] "
        $iTrayState = $TRAY_UNCHECKED
    EndIf
    TrayItemSetText($g_aWorkerTrayItems[$iIndex], $sPrefix & $g_aWorkers[$iIndex][1])
    TrayItemSetState($g_aWorkerTrayItems[$iIndex], $iTrayState)
EndFunc   ;==>_UpdateTrayItemForWorker

;/**
; * @brief Starts a worker sub-process.
; * @param $iIndex The index of the worker.
; */
Func _StartWorker($iIndex)
    If ProcessExists($g_aWorkers[$iIndex][3]) Then Return
    Local $sCommand = 'worker:' & $g_aWorkers[$iIndex][0]
    _WriteLog("INFO: Starting Worker '" & $g_aWorkers[$iIndex][1] & "'...")
    Local $iPID = _RunScript($sCommand)
    If $iPID > 0 Then
        $g_aWorkers[$iIndex][3] = $iPID
        $g_aWorkers[$iIndex][5] = $STATUS_RUNNING
    Else
        _WriteLog("ERROR: Failed to start Worker '" & $g_aWorkers[$iIndex][1] & "'.")
        $g_aWorkers[$iIndex][4] = False
        $g_aWorkers[$iIndex][5] = $STATUS_ERROR
    EndIf
EndFunc   ;==>_StartWorker

;/**
; * @brief Stops a worker sub-process gracefully.
; * @param $iIndex The index of the worker.
; */
Func _StopWorker($iIndex)
    Local $iPID = $g_aWorkers[$iIndex][3]
    If $iPID = 0 Or Not ProcessExists($iPID) Then
        $g_aWorkers[$iIndex][3] = 0
        $g_aWorkers[$iIndex][5] = $STATUS_STOPPED
        Return
    EndIf
    _WriteLog("INFO: Stopping Worker '" & $g_aWorkers[$iIndex][1] & "' (PID: " & $iPID & ")...")
    Local $sRegKey = $g_sRegBase & "\" & $g_aWorkers[$iIndex][0]
    RegWrite($sRegKey, "Signal", "REG_SZ", "exit")
    If @error Then _WriteLog("ERROR: Failed to write exit signal to registry for " & $g_aWorkers[$iIndex][0])
    Local $iResult = ProcessWaitClose($iPID, $g_iGracefulShutdownTimeout / 1000)
    If $iResult = 0 Then
        _WriteLog("WARN: Worker PID " & $iPID & " did not respond. Forcing shutdown.")
        ProcessClose($iPID)
    EndIf
    RegDelete($sRegKey)
    $g_aWorkers[$iIndex][3] = 0
    $g_aWorkers[$iIndex][5] = $STATUS_STOPPED
EndFunc   ;==>_StopWorker

;/**
; * @brief Runs a task either directly or as a sub-process.
; * @param $iIndex The index of the task.
; */
Func _RunTask($iIndex)
    Local $sTaskName = $g_aTasks[$iIndex][1]
    _WriteLog("INFO: Running Task '" & $sTaskName & "'...")
    If $g_aTasks[$iIndex][5] = $TASK_RUN_TYPE_DIRECT Then
        TrayItemSetState($g_aTaskTrayItems[$iIndex], $TRAY_DISABLE)
        Call($g_aTasks[$iIndex][2], $sTaskName)
        TrayItemSetState($g_aTaskTrayItems[$iIndex], $TRAY_ENABLE)
        _WriteLog("INFO: Direct Task '" & $sTaskName & "' has completed.")
    Else
        If $g_aTasks[$iIndex][3] <> 0 And ProcessExists($g_aTasks[$iIndex][3]) Then Return
        Local $sCommand = 'task:' & $g_aTasks[$iIndex][0]
        Local $iPID = _RunScript($sCommand, False)
        If $iPID > 0 Then
            $g_aTasks[$iIndex][3] = $iPID
            _UpdateAllTaskMenusState()
        Else
            _WriteLog("ERROR: Failed to run Task '" & $sTaskName & "'.")
        EndIf
    EndIf
EndFunc   ;==>_RunTask

;/**
; * @brief Adlib function to monitor the status of running workers and tasks.
; */
Func _CheckStatus_Adlib()
    For $i = 0 To $g_iNumWorkers - 1
        If $g_aWorkers[$i][4] And Not ProcessExists($g_aWorkers[$i][3]) Then
            _WriteLog("WARN: Worker '" & $g_aWorkers[$i][1] & "' died. Restarting...")
            $g_aWorkers[$i][5] = $STATUS_ERROR
            _UpdateTrayItemForWorker($i)
            _StartWorker($i)
        EndIf
    Next
    Local $bTaskStateChanged = False
    For $i = 0 To $g_iNumTasks - 1
        If $g_aTasks[$i][5] = $TASK_RUN_TYPE_SUBAPP And $g_aTasks[$i][3] > 0 And Not ProcessExists($g_aTasks[$i][3]) Then
            $g_aTasks[$i][3] = 0
            $bTaskStateChanged = True
        EndIf
    Next
    If $bTaskStateChanged Then _UpdateAllTaskMenusState()
EndFunc   ;==>_CheckStatus_Adlib

;/**
; * @brief Checks if any exclusive task is currently running as a sub-process.
; * @return True if an exclusive task is running, otherwise False.
; */
Func _IsExclusiveTaskRunning()
    For $i = 0 To $g_iNumTasks - 1
        If $g_aTasks[$i][4] = $TASK_MODE_EXCLUSIVE And $g_aTasks[$i][3] > 0 And ProcessExists($g_aTasks[$i][3]) Then
            Return True
        EndIf
    Next
    Return False
EndFunc   ;==>_IsExclusiveTaskRunning

;/**
; * @brief Updates the enabled/disabled state of all task menu items based on current activity.
; */
Func _UpdateAllTaskMenusState()
    Local $bExclusiveRunning = _IsExclusiveTaskRunning()
    For $i = 0 To $g_iNumTasks - 1
        If $bExclusiveRunning Then
            TrayItemSetState($g_aTaskTrayItems[$i], $TRAY_DISABLE)
        Else
            If $g_aTasks[$i][3] > 0 And ProcessExists($g_aTasks[$i][3]) Then
                TrayItemSetState($g_aTaskTrayItems[$i], $TRAY_DISABLE)
            Else
                TrayItemSetState($g_aTaskTrayItems[$i], $TRAY_ENABLE)
            EndIf
        EndIf
    Next
EndFunc   ;==>_UpdateAllTaskMenusState

;/**
; * @brief Adlib function to check for modifications to the Configs.ini file.
; */
Func _CheckForConfigChanges_Adlib()
    Local $iCurrentModDate = FileGetTime($g_sConfigFile, $FT_MODIFIED, $FT_STRING)
    If $iCurrentModDate <> $g_sLastConfigModDate Then
        _WriteLog("INFO: Configuration file change detected. Reloading...")
        _UnregisterAllMasterAdlibs()
        _StopAllWorkers()
        TraySetState(2)
        _LoadConfiguration()
        _CreateTrayMenu()
        _WriteLog("INFO: Configuration reloaded and menu recreated.")
    EndIf
EndFunc   ;==>_CheckForConfigChanges_Adlib

;/**
; * @brief Stops all currently active workers.
; */
Func _StopAllWorkers()
    For $i = 0 To $g_iNumWorkers - 1
        If $g_aWorkers[$i][4] Then _StopWorker($i)
    Next
EndFunc   ;==>_StopAllWorkers

;/**
; * @brief A generic function to run the script as a sub-process.
; * @param $sArgument The argument to pass to the new process (e.g., "worker:MyWorker").
; * @param $bPassControllerPID If True, the current controller's PID is passed as the second argument.
; * @return The PID of the new process, or 0 on failure.
; */
Func _RunScript($sArgument, $bPassControllerPID = True)
    Local $sControllerPID = ($bPassControllerPID ? " " & @AutoItPID : "")
    Local $sCommand = '"' & @ScriptFullPath & '" "' & $sArgument & '"' & $sControllerPID
    Local $sExecutable = (@Compiled ? "" : '"' & @AutoItExe & '" ')
    Return Run($sExecutable & $sCommand, @ScriptDir, @SW_HIDE)
EndFunc   ;==>_RunScript

;/**
; * @brief Performs all necessary cleanup before the Controller exits.
; */
Func _ExitController()
    _WriteLog("INFO: Controller shutting down...")
    _UnregisterAllMasterAdlibs()
    _CleanupAllRegistryKeys()
    _StopAllWorkers()
    Exit
EndFunc   ;==>_ExitController

;/**
; * @brief Unregisters all possible Adlib callbacks defined in the master lists.
; *
; * This is a crucial cleanup step to prevent orphaned callbacks, especially when reloading the configuration.
; */
Func _UnregisterAllMasterAdlibs()
    AdlibUnRegister("_CheckStatus_Adlib")
    AdlibUnRegister("_CheckForConfigChanges_Adlib")
    For $i = 0 To UBound($g_aMasterWorkers) - 1
        AdlibUnRegister($g_aMasterWorkers[$i][2])
    Next
EndFunc   ;==>_UnregisterAllMasterAdlibs

;/**
; * @brief Deletes the entire registry key used by the application for IPC.
; */
Func _CleanupAllRegistryKeys()
    RegDelete($g_sRegBase)
    If @error Then _WriteLog("ERROR: Failed to delete base registry key: " & $g_sRegBase)
EndFunc   ;==>_CleanupAllRegistryKeys

;/**
; * @brief Finds the array index corresponding to a given tray item ID.
; * @param $nID The tray item ID to find.
; * @param $aTrayItems The array of tray item IDs to search in.
; * @return The array index if found, otherwise -1.
; */
Func _GetIndexFromTrayID($nID, ByRef $aTrayItems)
    For $i = 0 To UBound($aTrayItems) - 1
        If $aTrayItems[$i] = $nID Then Return $i
    Next
    Return -1
EndFunc   ;==>_GetIndexFromTrayID

;/**
; * @brief Writes a message to both the console and the log file.
; *
; * This function provides a synchronized way to log events. Opening and closing the file
; * for each write minimizes the chance of file corruption from multiple processes.
; * @param $sMessage The message to log.
; */
Func _WriteLog($sMessage)
    Local $sTimeStamp = @YEAR & "/" & @MON & "/" & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC
    Local $sFormattedMessage = $sTimeStamp & " - " & $sMessage & @CRLF
    ConsoleWrite($sFormattedMessage)
    Local $hFile = FileOpen($g_sLogFile, 2 + 8) ; Open in Write + Append mode to lock the file during write
    If $hFile = -1 Then Return
    FileWrite($hFile, $sFormattedMessage)
    FileClose($hFile)
EndFunc   ;==>_WriteLog


; ===============================================================================================================================
; --- WORKER / TASK SUB-PROCESS EXECUTION
; ===============================================================================================================================

;/**
; * @brief Initializes the script when run as a Worker sub-process.
; * @param $sInternalName The internal name of the worker to run.
; * @param $iControllerPID The PID of the parent Controller process to monitor.
; */
Func _RunAsWorker($sInternalName, $iControllerPID)
    $g_sWorkerInternalName = $sInternalName
    $g_iControllerPID = $iControllerPID
    $g_sWorkerRegKey = $g_sRegBase & "\" & $sInternalName
    RegDelete($g_sWorkerRegKey)
    Local $sFunction = ""
    For $i = 0 To UBound($g_aMasterWorkers) - 1
        If $g_aMasterWorkers[$i][0] = $sInternalName Then
            $sFunction = $g_aMasterWorkers[$i][2]
            ExitLoop
        EndIf
    Next
    If $sFunction = "" Then Exit
    AdlibRegister($sFunction, 1000)
    AdlibRegister("_WorkerHeartbeat_Adlib", 2000)
    While 1
        Sleep(100)
    WEnd
EndFunc   ;==>_RunAsWorker

;/**
; * @brief Adlib function for workers to monitor the Controller and exit signals.
; */
Func _WorkerHeartbeat_Adlib()
    If $g_iControllerPID > 0 And Not ProcessExists($g_iControllerPID) Then
        _WorkerExitGracefully()
    EndIf
    RegRead($g_sWorkerRegKey, "Signal")
    If @error Then
        _WorkerExitGracefully()
        Return
    EndIf
    If RegRead($g_sWorkerRegKey, "Signal") = "exit" Then
        _WorkerExitGracefully()
    EndIf
EndFunc   ;==>_WorkerHeartbeat_Adlib

;/**
; * @brief Performs all necessary cleanup for a Worker before it exits.
; */
Func _WorkerExitGracefully()
    AdlibUnRegister("_WorkerHeartbeat_Adlib")
    Local $sFunction = ""
    For $i = 0 To UBound($g_aMasterWorkers) - 1
        If $g_aMasterWorkers[$i][0] = $g_sWorkerInternalName Then
            $sFunction = $g_aMasterWorkers[$i][2]
            ExitLoop
        EndIf
    Next
    If $sFunction <> "" Then AdlibUnRegister($sFunction)
    RegDelete($g_sWorkerRegKey)
    Exit
EndFunc   ;==>_WorkerExitGracefully

;/**
; * @brief Initializes the script when run as a Task sub-process.
; * @param $sInternalName The internal name of the task to run.
; */
Func _RunAsTask($sInternalName)
    Local $sFunction, $sDisplayName
    For $i = 0 To UBound($g_aMasterTasks) - 1
        If $g_aMasterTasks[$i][0] = $sInternalName Then
            $sDisplayName = $g_aMasterTasks[$i][1]
            $sFunction = $g_aMasterTasks[$i][2]
            ExitLoop
        EndIf
    Next
    If $sFunction = "" Then Exit
    Call($sFunction, $sDisplayName)
    Exit
EndFunc   ;==>_RunAsTask


; ===============================================================================================================================
; --- SPECIFIC WORKER/TASK FUNCTIONS (IMPLEMENTATION)
; ===============================================================================================================================

Func Worker1Function()
    _WriteLog("Worker 'Check Resources' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
EndFunc   ;==>Worker1Function
Func Worker2Function()
    _WriteLog("Worker 'Sync Files' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
EndFunc   ;==>Worker2Function
Func Worker3Function()
    _WriteLog("Worker 'Monitor Network' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
EndFunc   ;==>Worker3Function
Func Worker4Function()
    _WriteLog("Worker 'Backup DB' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
EndFunc   ;==>Worker4Function
Func Worker5Function()
    _WriteLog("Worker 'Cleanup Logs' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
EndFunc   ;==>Worker5Function
Func Worker6Function()
    _WriteLog("Worker 'Process Emails' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
EndFunc   ;==>Worker6Function
Func Worker7Function()
    _WriteLog("Worker 'Auto Screenshot' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
EndFunc   ;==>Worker7Function
Func Worker8Function()
    _WriteLog("Worker 'Monitor Temp' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
EndFunc   ;==>Worker8Function
Func Worker9Function()
    _WriteLog("Worker 'Check Updates' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
EndFunc   ;==>Worker9Function
Func Worker10Function()
    _WriteLog("Worker 'Security Scan' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
EndFunc   ;==>Worker10Function

Func TaskA_Function($sName)
    _WriteLog("Task '" & $sName & "' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
    Sleep(2000)
EndFunc   ;==>TaskA_Function
Func TaskB_Function($sName)
    _WriteLog("Task '" & $sName & "' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
    Sleep(3000)
EndFunc   ;==>TaskB_Function
Func TaskC_Function($sName)
    _WriteLog("Task '" & $sName & "' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
    Sleep(1500)
EndFunc   ;==>TaskC_Function
Func TaskD_Function($sName)
    _WriteLog("Task '" & $sName & "' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
    Sleep(1500)
EndFunc   ;==>TaskD_Function
Func TaskE_Function($sName)
    _WriteLog("Task '" & $sName & "' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
    Sleep(1500)
EndFunc   ;==>TaskE_Function
Func TaskF_Function($sName)
    _WriteLog("Task '" & $sName & "' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
    Sleep(1500)
EndFunc   ;==>TaskF_Function
Func TaskG_Function($sName)
    _WriteLog("Task '" & $sName & "' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
    Sleep(1500)
EndFunc   ;==>TaskG_Function
Func TaskH_Function($sName)
    _WriteLog("Task '" & $sName & "' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
    Sleep(1500)
EndFunc   ;==>TaskH_Function
Func TaskI_Function($sName)
    _WriteLog("Task '" & $sName & "' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
    Sleep(1500)
EndFunc   ;==>TaskI_Function
Func TaskJ_Function($sName)
    _WriteLog("Task '" & $sName & "' is running...")
    ; This is PlaceHoder - Your Code Repalce Here
    Sleep(1000)
EndFunc   ;==>TaskJ_Function

!

Regards,
 

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...