Jump to content

Multi-Task (easily run and mange many processes)


Recommended Posts

  • 4 months later...

Loving this code you came up with!  Have you made any mods to it lately?  I'll be testing it out over the next week and provide feedback.  Thanks!

Edited by RooperGee
Progress lies not in enhancing what is, but in advancing towards what will be. - Kahlil Gibran
Link to post
Share on other sites
  • 1 year later...

@Chimp Thank you for sharing this powerful UDF! It’s like MAGIC! 

Is it possible to have a function to terminate remaining tasks when required result is obtained. E.g. performing search of a user session on multiple terminal servers, how to make sure all remaining task when user is found. Also I find that it uses 100% of cpu when it’s running, are there methods to reduce or limit the cpu usage? Thanks again for your fantastic work! 

Link to post
Share on other sites

Hi @wuruoyu and thanks for your kind comment :). I'm glad you found this script useful.
When I wrote this udf I had in mind to add a way to limit the number of tasks running but, since I didn't have an urgent need, I haven't done it yet.
Now I think it can be a good opportunity to implement the helpful features you have proposed.
Here is a new version of the udf, which maintains full compatibility with the previous one, but which adds two functions:
_TaskLimit(): This function can set a limit to the number of tasks that can be spawn simultaneously.
You can use it at the beginning of the script to estabilsh the number of tasks that will be run simultaneusly, or you can also use it at any time during the script execution, so to reduce or increase the number of runned tasks on the fly. You could start with a lower number and increase it by steps, checking the cpu load in the while so to be able to calibrate it.
Note: you can still freely send all tasks you need also if the imposed limit is reached, the incoming requests are queued and their run is managed automatically so to stay below the max allowed.
_TaskRevokeAll(): This function allows to kill all running tasks and flush those queued still waiting to be run.
I hope that this new functions can be useful and hopefully free of bugs.
The example is nearly the same as the previous (sorry for the lazyness), the only differeces are, I've increased the number of tasks run; at the beginning (see line 14 of the script) I set the number of allowed tasks to only 1 _TaskLimit(1) so to emphatizate the difference in speed when, after 7 seconds of slow running, I will set the allowed MultiTasks to 20 (see line 68 of the scipt). You will see that is like if you insert the "turbo".
Any bug report and/or improvements are welcome.
Have fun

MultiTask_v2.au3

#include-once
; #include "Array.au3"

; #INDEX# =======================================================================================================================
; Title .........: Multi Task Management (tasks spawned by the user via the _TaskRun function of this udf)
; AutoIt Version :
; Language ......:
; Description ...: Functions that assists to execute and manage user's spawned tasks.
;                  Version 2 (beta) - includes 2 new functions:
;                  _TaskLimit       This function can set a limit to the number of tasks that can be spawn simultaneously.
;                                   You can still freely call _TaskRun() without limits, the only difference with a limit setted is
;                                   that requests exceeding the imposed limit are queued and ran gradually while other running tasks
;                                   ends so to not overstep the TaskLimit.
;                  _TaskRevokeAll   This function allows to kill all running tasks and flush those queued still waiting to be run
;
; Author(s) .....: Chimp (Gianni Addiego)
; ===============================================================================================================================

; #CURRENT# =====================================================================================================================
; _TaskLimit            This function can set a limit to the number of tasks that can be run simultaneously
; _TaskRun              Run a new task and stacks its references for later use
; _TasksCheckStatus     Check the status of all running tasks and call the CallBack function when a task completed
; _TaskRevokeAll        This function allows to kill all running tasks and flush those queued still waiting to be run
; ===============================================================================================================================

; #INTERNAL_USE_ONLY#============================================================================================================
; __FIFO                a simple implementation of a FIFO stack
; __Dequeue             run a queued task (only if free slots are available)
; __TaskRemove          frees task's stacked references
; __NOP                 No OPeration function
; ===============================================================================================================================
Global $aTasks[1][7] = [[0]] ; Create and initialize the task reference stack to 0

; #FUNCTION# ====================================================================================================================
; Name ..........: _TaskRun
; Description ...: Runs a new task as specified in $sCommand
; Syntax ........: _TaskRun($vIndex, $sCommand[, $sParser = "__NOP"[, $vTimeout = False]])
; Parameters ....: $vIndex              - A variant value passed by the caller as a reference to this task.
;                                         this value will be returned to the callback function along with results.
;
;                  $sCommand            - A string value containing the full command to be executed
;
;                  $sParser             - [optional] The name of the function to be called at the end of this task.
;                                         Default is __NOP that is a "do nothing" function.
;                                         P.S.
;                                         the $aParser function will be called by the _TasksCheckStatus() function when
;                                         this task will finish to run:
;                                         an 1D array with 4 elements will be passed to the called function:
;                                         element [0]   the caller's index reference of this task
;                                         element [1]   the StdOut result of this task
;                                         element [2]   the StdErr result of this task
;                                         element [3]   the time spent by this task
;
;                  $vTimeout            - [optional] A variant value. Default is False.
;                                         if specified is the number of seconds for the Timeout.
;                                         After this amount of seconds, if this task is still running, it is killed
;
; Return values .: the total number of stacked tasks
; Author ........: Chimp (Gianni Addiego)
; Modified ......:
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _TaskRun($vIndex, $sCommand, $sParser = "__NOP", $vTimeout = False)
    ;
    ; Stack structure:
    ;
    ;   +-----+
    ; 0 | nr. | <-- [0][0] number of running tasks
    ;   +-----+-----+-----+-----+-----+-----+-----+
    ; 1 |  0  |  1  |  2  |  3  |  4  |  5  |  6  |
    ;   +-----+-----+-----+-----+-----+-----+-----+
    ; n |  *  |  *  |  *  |  *  |  *  |  *  |  *  |
    ;      |     |     |     |     |     |     |
    ;      |     |     |     |     |     |   [n][6] setted with the TimerInit() value at the start of this task.
    ;      |     |     |     |     |     |
    ;      |     |     |     |     |   [n][5] contains the required Timeout in seconds (Default is set to False = no timeout)
    ;      |     |     |     |     |
    ;      |     |     |     |   [n][4] The CallBack function name that will receive the results of this task
    ;      |     |     |     |
    ;      |     |     |   [n][3] the error message returned by the StdErr stream of this task
    ;      |     |     |
    ;      |     |   [n][2] the result of the command returned by the StdOut stream of this task
    ;      |     |
    ;      |   [n][1] the reference of this task passed by the user (will be passed back to the caller along with results)
    ;      |
    ;    [n][0] the PID of this running task
    ;

    If _TaskLimit() And ($aTasks[0][0] = _TaskLimit()) Then  ; queue this request
        Local $aPush[4] = [$vIndex, $sCommand, $sParser, $vTimeout]
        ; __FIFO($aPush)
        Return SetError(0, __FIFO($aPush), $aTasks[0][0])
    EndIf

    ; ---------------------------------------------------------------------------------------------------------------
    ReDim $aTasks[$aTasks[0][0] + 2][7]     ; add a new element to the stack
    $aTasks[0][0] += 1     ; add 1 to the number of running tasks

    ; Run the passed command with the I/O streams redirected
    $aTasks[$aTasks[0][0]][0] = Run($sCommand, "", @SW_HIDE, 6)     ; 6 -> $STDOUT_CHILD (0x2) +  $STDERR_CHILD (0x4)

    ; store references of this task to the stack
    $aTasks[$aTasks[0][0]][1] = $vIndex
    $aTasks[$aTasks[0][0]][4] = $sParser

    $aTasks[$aTasks[0][0]][5] = $vTimeout     ; can be False or the number of seconds for the timeout of this task
    $aTasks[$aTasks[0][0]][6] = TimerInit()     ; store the TimerInit() value for this task

    Return $aTasks[0][0]     ; return the total number of running tasks
    ; ---------------------------------------------------------------------------------------------------------------
EndFunc   ;==>_TaskRun

; #FUNCTION# ====================================================================================================================
; Name ..........: _TasksCheckStatus
; Description ...: Scans the status of all active tasks and checks if some task has finished its job
;                  This function should be called as aften as possible
; Syntax ........: _TaskCheckStatus()
; Parameters ....: None
; Return values .: number of still running tasks;  * see remarks
; Author ........: Chimp (Gianni Addiego)
; Modified ......:
; Remarks .......: When a task finish, it is removed from the stack and results are passed to the callback function.
;                  An 1D array with 4 elements will be passed to the called function:
;                  element [0]  the caller's index reference of this task
;                  element [1]  the StdOut result of this task
;                  element [2]  the StdErr result of this task
;                  element [3]  the time spent by this task (approximative time)
;                               is an approximate datum because it also includes the delay
;                               added by the main loop before calling _TasksCheckStatus()
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _TasksCheckStatus()

    ; first check if there are tasks waiting to be executed.
    __Dequeue()

    If $aTasks[0][0] = 0 Then Return 0 ; if no tasks then return
    Local $bEndTask ; will be setted to True if the checked task has finished or is killed by timeout
    Local $iPointer = 1 ; start checking from first task in the stack

    Local $aResults[4] ; this Array will be loaded with the results of the task
    Local $sCallBack ; the name of the function to call when a task ends its job
    Local $aArgs[2] = ["CallArgArray", ""] ; "special" array will be loaded with parameters for the CallBack function

    While $iPointer <= $aTasks[0][0]

        $bEndTask = False

        $aTasks[$iPointer][2] &= StdoutRead($aTasks[$iPointer][0]) ; read and store the StdOut stream
        $aTasks[$iPointer][3] &= StderrRead($aTasks[$iPointer][0]) ; read and store the StdErr stream

        ; If @error Then ; if there is an @error is because this task has finished its job
        If Not ProcessExists($aTasks[$iPointer][0]) Then ; if this task has finished its job
            $bEndTask = True ; flag the end of the work

        ElseIf $aTasks[$iPointer][5] <> False Then ; if task is still running see if a Timeout check was rquired and if so if is been reached
            If Int(TimerDiff($aTasks[$iPointer][6]) / 1000) >= $aTasks[$iPointer][5] Then ; timeout reached or exceeded!!
                $aTasks[$iPointer][3] = "@error Timeout " & $aTasks[$iPointer][3] ; insert an error message at the beginning of the StdErr stream
                StdioClose($aTasks[$iPointer][0]) ; close I/O streams
                ProcessClose($aTasks[$iPointer][0]) ; kill this process
                $bEndTask = True ; flag the end of this task
            EndIf
        EndIf

        If $bEndTask Then ; if this task has finished, get its results and send to the CallBack function

            $aResults[0] = $aTasks[$iPointer][1] ; ............. Index (the caller reference)
            $aResults[1] = $aTasks[$iPointer][2] ; ............. the StdOut generated by this task
            $aResults[2] = $aTasks[$iPointer][3] ; ............. the StdErr generated by this task
            $aResults[3] = TimerDiff($aTasks[$iPointer][6]) ; .. time spent by this task

            $sCallBack = $aTasks[$iPointer][4] ; the name of the function to be called

            $aArgs[1] = $aResults ; second element of the "CallArgArray" special array contains the $aResults array
            ;                       loaded with the parameters to be send to the CallBack function. (array in array)
            ;                       (the CallBack function will receive only the 1D 4 elements array $aResults)

            __TaskRemove($iPointer) ; remove references of this task from the stack

            ; call the CallBack function and pass the results of this task.
            ; (CallBack function should return as soon as possible because it stops the CheckStatus for the other tasks)
            ;
            Call($sCallBack, $aArgs) ; Call CallBack function  --->---+
            ;                                                         |
            ;  <--- and return here  ---------------------------------+
        EndIf

        $iPointer += 1 ; check next task
    WEnd
    ; check again if there are tasks waiting to be executed.
    __Dequeue()

    Return $aTasks[0][0]
EndFunc   ;==>_TasksCheckStatus

Func _TaskLimit($vLimit = Default)
    Local Static $iLoadLimit = 0
    If IsKeyword($vLimit) Then Return $iLoadLimit
    $iLoadLimit = Abs($vLimit)
EndFunc   ;==>_TaskLimit

Func __Dequeue()
    ; check if there are tasks waiting to be executed.
    ;  if so, pull the queued tasks from the FIFO stack and run it
    If _TaskLimit() And __FIFO('', True) And ($aTasks[0][0] < _TaskLimit()) Then
        Local $aPull ; [4] = [$vIndex, $sCommand, $sParser, $vTimeout]
        While __FIFO('', True) And ($aTasks[0][0] < _TaskLimit())
            $aPull = __FIFO() ; pull next queued task
            _TaskRun($aPull[0], $aPull[1], $aPull[2], $aPull[3]) ; and run it
            ; ConsoleWrite('*') ; Debug purpose
        WEnd
        ; ConsoleWrite(@CRLF & @TAB & $aTasks[0][0] & @TAB & _TaskLimit() & @CRLF) ; Debug purpose
    EndIf
EndFunc   ;==>__Dequeue

; #FUNCTION# ====================================================================================================================
; Name ..........: __FIFO
; Description ...: A simple implementation of a FIFO stack (can be used to pull or push data from/to the stack)
;
; Syntax ........: _FIFO([$vItem = ''[, $vServiceCall = False]])
; Parameters ....: $vItem               - [optional] A variant value. Default is ''.
;                                         this parameter can be empty or filled with data and behaves like:
;                                         - if empty:  the function pulls and return data from the stack
;                                         - if filled: the function push that data to te stack
;                  $vServiceCall        - [optional] A KeyWord or a value. Default is False.
;                                         this parameter allows to perform some internal "methods"
;                                         'Default' or 1 : Reindex the Key Names starting from 1 (internal use)
;                                         'Null' or 2    : Remove all items from the stack
;                                         'True' or 3    : Return the number of elements stored in the stack (returns 0 if stack is empty)
;                                         When this parameter is used the previous parameter $vItem is simply ignored.
;
; Return values .:                      - when the function is used to Push data:
;                                         returns nothing and set @extended with the total number of elements in the stack
;                                       - when the function is used to Pull data:
;                                         returns the pulled data and set @extended with the number of elements still in the stack.
;                                         If you try to pull data from an empty stack you get an empty string and @error is set to 1
;
;                                         For each function call the @extended flag is set with the number of items still in the stack
; Author ........: Chimp (Gianni Addiego)
; Modified ......:
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func __FIFO($vItem = '', $vServiceCall = False)
    Static Local $oFIFOStack = ObjCreate("Scripting.Dictionary")
    Static Local $iNDX = 1, $KEYWORD_DEFAULT = 1, $KEYWORD_NULL = 2, $KEYWORD_True = 3
    Local $vPull = ''
    If $vServiceCall Or IsKeyword($vServiceCall) Then ; check second parameter
        Local $iService = IsKeyword($vServiceCall) ? IsKeyword($vServiceCall) : $vServiceCall ; Default = 1, Null = 2
        If String($vServiceCall) = "True" Then $iService = $KEYWORD_True
        Switch $iService
            Case $KEYWORD_DEFAULT ; 1 or 'Default' Keyword was passed
                ; we use the Default Keyword to "normalize" all keys index starting from 1
                If $oFIFOStack.Count Then
                    Local $aKeys = $oFIFOStack.keys
                    For $i = 0 To UBound($aKeys) - 1
                        $oFIFOStack.key($aKeys[$i]) = $i + 1
                    Next
                    $iNDX = 1
                EndIf
                Return SetExtended($oFIFOStack.Count, '')

            Case $KEYWORD_NULL ; 2 or 'Null' Keyword was passed WARNING: the stack is being emptied
                ; we use the Null KeyWord or value 2 to remove all items from the stack
                $oFIFOStack.RemoveAll
                Return SetExtended($oFIFOStack.Count, '')

            Case $KEYWORD_True ; 3 or 'True' Keyword was passed just return items count
                Return SetExtended($oFIFOStack.Count, $oFIFOStack.Count)

            Case Else
                ; Return SetError(0, $oFIFOStack.Count, $oFIFOStack.Count)
                Return SetError(1, $oFIFOStack.Count)
        EndSwitch
    EndIf
    If $vItem = '' Then ; no Item to stack, just pull from stack then
        If $oFIFOStack.Count Then ; ensure stack is not empty
            $vPull = $oFIFOStack.Item($iNDX) ; get the first item still in stack
            $oFIFOStack.Remove($iNDX) ; remove item from stack
            $iNDX += 1 ; raise the bottom index
            Return SetError(0, $oFIFOStack.Count, $vPull) ; return item to caller
        Else
            Return SetError(1, 0, '')
        EndIf
    Else
        $oFIFOStack.ADD($oFIFOStack.Count + $iNDX, $vItem) ; Add item
        SetExtended($oFIFOStack.Count)
    EndIf
EndFunc   ;==>__FIFO

; Internal use. Removes all task (running and queued) and relative references from the stack
Func _TaskRevokeAll() ; a countermand on all tasks (running and queued)
    ; If $aTasks[0][0] = 0 Then Return 0 ; if no tasks then return
    Local $iPid
    For $i = $aTasks[0][0] To 1 Step -1
        $iPid = $aTasks[$i][0]
        __TaskRemove($i) ; remove references of this task from the stack
        ProcessClose($iPid) ; kill this process
    Next
    __FIFO('', Null) ; empty the stack
EndFunc   ;==>_TaskRevokeAll

; Internal use. Remove the task references from the stack
Func __TaskRemove($iElement)
    #cs
        If $iElement > $aTasks[0][0] Or $iElement < 1 Then Return
        StdioClose($aTasks[$iElement][0])
        _ArrayDelete($aTasks, $iElement)
    #ce
    ; - new --------------------------------------------------
    ; remove element without the _Array* udf
    If $iElement > 0 And $iElement <= $aTasks[0][0] Then
        StdioClose($aTasks[$iElement][0])
        If $aTasks[0][0] > 1 Then
            For $i = 0 To UBound($aTasks, 2) - 1
                $aTasks[$iElement][$i] = $aTasks[$aTasks[0][0]][$i]
            Next
        EndIf
    Else
        Return SetError(1) ; queue is empty or the required element is out of bound
    EndIf
    $aTasks[0][0] -= 1
    ReDim $aTasks[$aTasks[0][0] + 1][UBound($aTasks, 2)]
    Return $aTasks[0][0] ; returns the number of tasks still running
EndFunc   ;==>__TaskRemove

; Internal use. An empty function
Func __NOP($aDummy)
    ; NOP (No OPeration)
EndFunc   ;==>__NOP

Example_v2

; #RequireAdmin
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GuiListView.au3>
#include '.\MultiTask_v2.au3'
;
; IMPORTANT following array should be populated with many real HostNames
; here are just nonsense items used as placeholders
Global $aIPList[] = [@ComputerName, @IPAddress1, @ComputerName, @ComputerName, 'InexistentClient', @ComputerName, _
        @ComputerName, @IPAddress1, @ComputerName, @ComputerName, 'InexistentClient', @ComputerName]

Global $hGrid ; The ListView Handle
Global $ahGrid[1] ; An array to keep handles of any listview row
_TaskLimit(1)
Example()
MsgBox(0, "Debug:", "Done" & @CRLF & "Hit OK to exit")

Func Example()
    Local $Form1 = GUICreate("Clients status", 760, 400)
    ;
    ; Create the ListView
    $hGrid = GUICtrlCreateListView("HostName|IP|Info|Last reboot|CPU|Last logon|Current logon", 0, 0, 760, 400)
    _GUICtrlListView_SetColumnWidth($hGrid, 0, 140) ; HostName
    _GUICtrlListView_SetColumnWidth($hGrid, 1, 100) ; IP
    _GUICtrlListView_SetColumnWidth($hGrid, 2, 80) ; Ping info ms or Off or Unknown or Timeout)
    _GUICtrlListView_SetColumnWidth($hGrid, 3, 120) ; Last Reboot
    _GUICtrlListView_SetColumnWidth($hGrid, 4, 40) ; cpu load
    ; last 2 columns a mutually exclusive. If there is a user logged it's shown on the last column
    ; if there is not a user logged then the last user that was logged  is shown on column 4 instead
    _GUICtrlListView_SetColumnWidth($hGrid, 5, 140) ; Last logged UserId (if nobody is logged now)
    _GUICtrlListView_SetColumnWidth($hGrid, 6, 140) ; Currently logged UserId
    _GUICtrlListView_SetExtendedListViewStyle($hGrid, BitOR($LVS_EX_GRIDLINES, $LVS_EX_FULLROWSELECT)) ; show grid; select whole row
    ;
    GUISetState(@SW_SHOW)

    ; following line is needed if you have to refill the listview
    ; _GUICtrlListView_DeleteAllItems(GUICtrlGetHandle($hGrid)) ; empty the listview

    ReDim $ahGrid[UBound($aIPList)]

    ; this loop will run all needed tasks at once.
    ; The results of the tasks will be managed by the callback functions (nearly) autonomously and asynchronously
    For $i = 0 To UBound($aIPList) - 1
        $ahGrid[$i] = _GUICtrlListView_AddItem($hGrid, "") ; create a listview row
        ;
        ; ... for each client ...
        ;
        ; spawn ping commands. result will be send to the _PingParse() function
        _TaskRun($i, "Ping -a -n 1 -4 " & $aIPList[$i], "_PingParse")

        ; spawn commands to recall LoggedOn User. Result will be send to the _LoggedOn() function
        _TaskRun($i, "wmic /node:" & $aIPList[$i] & " computersystem get username /value", "_LoggedOn", 5) ; 5 = timeout in 5 seconds

        ; spawn commands to recall Last reboot time. result will be send to the _LastReboot() function
        _TaskRun($i, "wmic /node:" & $aIPList[$i] & " os get lastbootuptime /value", "_LastReboot", 5)

        ; spawn commands to recall % of CPU load. result will be send to the _CPU_load() function
        _TaskRun($i, "wmic /node:" & $aIPList[$i] & " cpu get loadpercentage /value", "_CPU_load", 7)
    Next

    ; now, while all tasks are running
    ; we could perform other activities in the meantime
    ; or we can wait the end of all the tasks
    Local $Timer = TimerInit()
    Do
        Sleep(250)
        ; you could perform other jobs here while waiting for the tasks to finish
        If TimerDiff($Timer) > 7000 Then _TaskLimit(20) ; after 7 seconds increase the allowed tasks to 20 (insert the turbo)
    Until Not _TasksCheckStatus() ; <-- this function performs management of running tasks
    ;                                   and should be called as often as possible

EndFunc   ;==>Example

; below are CallBack functions

; #FUNCTION# ====================================================================================================================
; Name ..........: _PingParse (a callback function)
; Description ...: this function analize the output of a ping command and "extract" needed infos
;                  it fills columns 0 1 and 2 of the list view ($aTarget[0] is the line number of the listview to be filled)
; Syntax ........: _PingParse($aTarget[, $bDomain = True])
; Parameters ....: $sTarget             - An array with Ping results passed by the MultiTasks as soon as any ping task ends
;                                         the passed array contains following data:
;                                         $aTarget[0] Index for this task
;                                         $aTarget[1] StdOut from ping (the whole output of the ping)
;                                         $aTarget[2] StdErr from ping
;                                         $aTarget[3] Time spent by this task to complete (is NOT the ping roundtrip time)
;                  $bDomain             - [optional] A binary value. Default is True. (keep domain info in host name)
;
; Return values .: None. It Fills Listview columns 0, 1 and 2
;                                         column 0 : resolved HostName or ""
;                                         column 1 : IP address or "" (this can contain last known IP even if now is offline)
;                                         column 2 : roundtrip time or "Unknown" or "timeout" or "Off"
;
; Author ........: Chimp
; ===============================================================================================================================
Func _PingParse($aTarget, $bDomain = True)
    ; $aTarget contains 4 elements: [0] Index, [1] StdOut, [2] StdErr, [3] time spent by this task

    Local $sOutput = $aTarget[1] ; stdout
    Local $0, $1, $2, $3, $aMs
    ; Local $iAnswer = -1, $iName = -1
    Local $aResult[3] = ["", "", ""] ; [0]ms, [1]HostName, [2]IP

    $aMs = StringRegExp($sOutput, "([0-9]*)ms", 3)
    If Not @error Then ; Ping replayed
        $aResult[0] = $aMs[UBound($aMs) - 1] ; average ms
    Else
        ; $aResult[0] = "off"
    EndIf

    $0 = StringInStr($sOutput, "Ping")
    $1 = StringInStr($sOutput, "[") ; HostName decoded?

    If $1 Then ; HostName decoded
        $2 = StringInStr($sOutput, "]")
        $3 = StringInStr($sOutput, " ", 0, -2, $1)
        $aResult[1] = StringMid($sOutput, $3 + 1, $1 - $3 - 1) ; HostName
        $aResult[2] = StringMid($sOutput, $1 + 1, $2 - $1 - 1) ; IP
    Else

        If $0 Then ; pinging an IP address?
            ; $aResult[1] = "" ; no HostName
            Local $aFindIP = StringRegExp($sOutput, "\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", 3)
            If Not @error Then $aResult[2] = $aFindIP[0]
        Else
            ; unknown HostName
            $aResult[0] = "Unknown"
            $aResult[1] = $aIPList[$aTarget[0]] ; retrieve HostName from the $aIPList array

        EndIf
    EndIf

    If $bDomain = False Then
        Local $aSplit = StringSplit($aResult[1], ".", 2) ; 2 = $STR_NOCOUNT
        $aResult[1] = $aSplit[0] ; romove .domain part from the HostName
    EndIf

    If StringLeft($aTarget[2], 14) = "@error Timeout" Then $aResult[0] = "Timeout"

    ; Now that we have the infos, we compile related cells in ListView
    ;                            grid    row-handle            data         column
    _GUICtrlListView_SetItemText($hGrid, $ahGrid[$aTarget[0]], $aResult[1], 0) ;    first column "HostName"
    _GUICtrlListView_SetItemText($hGrid, $ahGrid[$aTarget[0]], $aResult[2], 1) ;    second column "IP address"
    _GUICtrlListView_SetItemText($hGrid, $ahGrid[$aTarget[0]], $aResult[0], 2) ;    third column "Infos about the ping"

    ; ConsoleWrite("Debug: " & "-> " & $aResult[0] & @TAB & $aResult[1] & @TAB & $aResult[2] & @CRLF)
EndFunc   ;==>_PingParse

Func _LastReboot($aParameters) ; Last reboot DateTime
    $aParameters[1] = StringStripWS($aParameters[1], 8)
    Local $equal = StringInStr($aParameters[1], "=")
    If $equal Then
        _GUICtrlListView_AddSubItem($hGrid, $ahGrid[$aParameters[0]], WMIDateStringToDate(StringMid($aParameters[1], $equal + 1)), 3) ; column 3
    EndIf
EndFunc   ;==>_LastReboot

Func _CPU_load($aParameters) ; % of CPU load
    $aParameters[1] = StringStripWS($aParameters[1], 8)
    Local $equal = StringInStr($aParameters[1], "=")
    If $equal Then
        _GUICtrlListView_AddSubItem($hGrid, $ahGrid[$aParameters[0]], StringMid($aParameters[1], $equal + 1), 4) ; column 4
    EndIf
EndFunc   ;==>_CPU_load

Func _LoggedOn($aParameters) ; User now logged
    $aParameters[1] = StringStripWS($aParameters[1], 8)
    ; if none is logged, then find the user that was last logged (also using _TaskRun)
    If $aParameters[1] = "" Or $aParameters[1] = "UserName=" Then
        ; following syntax is by @iamtheky (thanks)
        ; https://www.autoitscript.com/forum/topic/189845-regexp-pattern-in-findstr-dos-command/?do=findComment&comment=1363106
        Local $sCmd = 'cmd /c FOR /F "usebackq skip=2 tokens=1-3" %A IN (`REG QUERY ' & '"\\' & $aIPList[$aParameters[0]] & _
                '\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI" /REG:64 /v LastLoggedOnUser 2^>nul`) Do @echo %C'
        _TaskRun($aParameters[0], $sCmd, "_LastLogged", 5) ; find last logged user and, when ready, send result to _LastLogged()
    Else
        ; if someone is logged then write username to column 6
        Local $aUser = StringSplit($aParameters[1], "=", 2) ; 2 = $STR_NOCOUNT
        _GUICtrlListView_AddSubItem($hGrid, $ahGrid[$aParameters[0]], $aUser[UBound($aUser) - 1], 6) ; column 6
    EndIf
EndFunc   ;==>_LoggedOn

Func _LastLogged($aParameters)
    _GUICtrlListView_AddSubItem($hGrid, $ahGrid[$aParameters[0]], $aParameters[1], 5) ; column 5
EndFunc   ;==>_LastLogged


Func WMIDateStringToDate($dtmDate) ; thanks to @kylomas
    ; https://www.autoitscript.com/forum/topic/169252-wmi-password-age-issue/?do=findComment&comment=1236082
    ; reformat date to mm/dd/yyyy hh:mm:ss and zero fill single digit values
    Return StringRegExpReplace(StringRegExpReplace($dtmDate, '(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}).*', '$2/$3/$1 $4:$5:$6'), '(?<!\d)(\d/)', '0$1')
EndFunc   ;==>WMIDateStringToDate

 

 

image.jpeg.9f1a974c98e9f77d824b358729b089b0.jpeg Chimp

small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Link to post
Share on other sites

Hi @Chimp forgot to ask in my previous post, the "Callback" function Call($sCallBack, $aArgs), with #Au3Stripper_Parameters=/pe /rm /so (also using #Au3Stripper_Ignore_Funcs=$sCallBack), the script compiled successfully, but the function "_TaskRun" doesn't do seem to do anything.

No big deal if there is no workaround. Thanks again!

Edited by wuruoyu
Link to post
Share on other sites
  • Developers
7 hours ago, wuruoyu said:

but the function "_TaskRun" doesn't do seem to do anything.

Pretty sure you got a warning when running au3stripper about this might happen! ...right?

Jos

SciTE4AutoIt3 Full installer Download page   - Beta files       Read before posting     How to post scriptsource   Forum etiquette  Forum Rules 
 
Live for the present,
Dream of the future,
Learn from the past.
  :)

Link to post
Share on other sites

@Jos I am not getting warning regarding _TaskRun function. I do get a warning about “unresolvable func - $sCallBack” if I don’t use #Au3Stripper_Ignore_Funcs=$sCallBack

https://www.autoitscript.com/autoit3/scite/docs/SciTE4AutoIt3/Au3Stripper.html

The Au3stripper manual did state that call() is one of the affected functions. Just wondering if there are other replaceable methods/functions for call().

 

Link to post
Share on other sites
  • Developers

I understand that the warning/error is about the Call statement as au3stripper has no way of knowing what the value of  $sCallBack is.
The #Au3Stripper_Ignore_Funcs= statement should contain the target Func name, not a variable, so in this case this might help avoiding issues:

#Au3Stripper_Ignore_Funcs= _TaskRun

Jos

SciTE4AutoIt3 Full installer Download page   - Beta files       Read before posting     How to post scriptsource   Forum etiquette  Forum Rules 
 
Live for the present,
Dream of the future,
Learn from the past.
  :)

Link to post
Share on other sites
  • 1 year later...

Hi @Chimp and thanks for sharing this interesting UDF.

I have a question, maybe you can support me in implementing this .au3 in my code:

I developed a tool to real-time monitor certain crypto assets, as in the image.

Right now I simply cycle 

For $i = 0 To $MaxPairs - 1
        _RealTimePublicValue($s_TradingPlatform, $a_Pairs[$i][0], $a_Pairs[$i][1], $i)
    Next

and

Func _RealTimePublicValue($s_TradingPlatform, $Crypto, $LocalValue, $index) ; OHLC
    If $s_TradingPlatform = "Crypto.com" Then
        Local $pstring = $Crypto & "_" & $LocalValue
        Local $sResult = CryptoQueryPublic("get-ticker", "instrument_name=" & $pstring) ;Ticker
        $ask = _StringBetween($sResult, '"a":', ',')
        If IsArray($ask) Then
            $last = _StringBetween($sResult, '"a":', ',')
            $High = _StringBetween($sResult, '"h":', ',')
            $Low = _StringBetween($sResult, '"l":', ',')
            $Change = _StringBetween($sResult, '"c":', '}')
        Else
            _send("Pair " & $pstring & " not allowed, change currency", $giallo)
            Return
        EndIf
        _WriteToGui($pstring, $last[0] - $Change[0], $High[0], $Low[0], $last[0], $index)
    ElseIf $s_TradingPlatform = "Coinbase Pro" Then
        Local $pstring = $Crypto & "-" & $LocalValue
        Local $sResult = CoinbaseProQueryPublic("GET", "products/" & $pstring & "/stats") ; Stats
        $opening = _StringBetween($sResult, '"open":"', '"')
        If IsArray($opening) Then
;~          $opening = _StringBetween($sResult, '"open":"', '"')
            $High = _StringBetween($sResult, '"high":"', '"')
            $Low = _StringBetween($sResult, '"low":"', '"')
            $last = _StringBetween($sResult, '"last":"', '"')
        Else
            _send("Pair " & $pstring & " not allowed, change currency", $giallo)
            Return
        EndIf
        _WriteToGui($pstring, $opening[0], $High[0], $Low[0], $last[0], $index)
    EndIf
EndFunc   ;==>_RealTimePublicValue

and use _WriteToGui() to write all data into the GUI.

In this way the process is done in sequence. I would like to have it done in parallel for all my assets, since I would like to perform other actions (i.e. selling or buying) and now I have to wait all the cycle to complete. This leads in being stuck waiting the cycle. And it's not immediate since the call to Exchange may cost around .2 to .5 sec each pair i look for.

I would like also to add @Danyfirex to this thread, maybe he can be of support as well.

Thanks

Marco

screenshot.1659.jpg

Link to post
Share on other sites

Hi @marko001

more or less, as far as I can see from that snippet, the delay would be caused by the CryptoQueryPublic() and/or CoinbaseProQueryPublic() functions, which have to wait for a response from a site (I assume). Therefore, to "parallelize" those "queries" you should create a standalone version of these functions so that multiple instances can be "run" without the need to wait for the returned response and "take" the responses and display them only when those are available ( doing something else in the meantime). This, long story short, is what I can say, but just from that snippet, I can't help you that much. Couldn't you post the whole list? or at least those two functions and also the _WriteToGui function? (but the whole listing would be better)

 

image.jpeg.9f1a974c98e9f77d824b358729b089b0.jpeg Chimp

small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Link to post
Share on other sites

Hi @Chimp and thanks for reply. 

This is part of the While:

Case $FRM_Dashboard
            Switch $nMsg[0]
                Case $SaveButton
                    _send("Saving Cache", $azzurro)
                    _SaveCache()
                Case $LoadButton
                    _send("Loading Cache", $azzurro)
                    _LoadCache()
                Case $GUI_EVENT_CLOSE
;~                  FileDelete(@ScriptDir & "\Cache.txt")
                    ExitLoop
                Case $B_Telegram
                    GUISetState(@SW_SHOW, $FRM_TelegramConfig)
                Case $B_Return2Assets
                    AdlibUnRegister("_MACDANALYSIS")
                    AdlibUnRegister("_LASTVALUES")
                    _CloseOpen($FRM_Dashboard, $FRM_Assets)
                    GUIDelete($FRM_Dashboard)
                    _CreateDashboardForm()
                Case $B_Simulation
                    _TradeOrSimulate()
                Case $B_ShowBuyOptions
                    GUISetState(@SW_DISABLE, $FRM_Dashboard)
                    GUISetState(@SW_SHOW, $FRM_PurchaseOptions)
                Case $B_MonitorAll
                    _MonitorAllPairs()
                Case Else
                    For $i = 0 To 14
                        If $nMsg[0] = $B_Activate[$i] Then
                            If $a_Pairs[$i][4] = 0 Then
                                _StartMonitor($i)
                            Else
                                _StopMonitor($i)
                            EndIf
                        ElseIf $nMsg[0] = $CB_ShowGraph[$i] Then
                            _MACDANALYSIS()
                        ElseIf $nMsg[0] = $R_LowRisk[$i] Then
                            _SetLowRisk($i)
                        ElseIf $nMsg[0] = $R_MidRisk[$i] Then
                            _SetMidRisk($i)
                        ElseIf $nMsg[0] = $R_HighRisk[$i] Then
                            _SetHighRisk($i)
                        ElseIf $nMsg[0] = $R_CustomRisk[$i] Then
                            GUISetState(@SW_DISABLE, $FRM_Dashboard)
                            _OpenTradeSettingsGUI($i)
                        ElseIf $nMsg[0] = $CB_BuyButton[$i] Then
                            _ReplyBuyButtonStatus($i)
                        EndIf
                    Next
            EndSwitch

where basically, during constant assets update, I can buy/sell/create graphs...

This is the _WriteToGui()

Func _WriteToGui($pstring, $opening, $High, $Low, $last, $index)
    GUISwitch($FRM_Dashboard)
    Local $DeltaY = 24
    If $createitems Then
        _send("Filling OHLC values for " & $pstring, $verde)
        $I_Currency[$index] = GUICtrlCreateInput($pstring, 40, 32 + $DeltaY * $index, 60, 21, BitOR($ES_READONLY, $ES_RIGHT))
        _SetCtrlFontAndColor(-1, 0, 0xFFFFFF, 0x161616)
        $I_Open[$index] = GUICtrlCreateInput(Round($opening, 3), 110, 32 + $DeltaY * $index, 57, 21, BitOR($ES_READONLY, $ES_RIGHT))
        _SetCtrlFontAndColor(-1, 0, 0xFFFFFF, 0x161616)
        $I_High[$index] = GUICtrlCreateInput(Round($High, 3), 110 + 62, 32 + $DeltaY * $index, 57, 21, BitOR($ES_READONLY, $ES_RIGHT))
        _SetCtrlFontAndColor(-1, 0, 0xFFFFFF, 0x161616)
        $I_Low[$index] = GUICtrlCreateInput(Round($Low, 3), 110 + 62 + 62, 32 + $DeltaY * $index, 57, 21, BitOR($ES_READONLY, $ES_RIGHT))
        _SetCtrlFontAndColor(-1, 0, 0xFFFFFF, 0x161616)
        $I_Current[$index] = GUICtrlCreateInput(Round($last, 3), 110 + 62 + 62 + 62, 32 + $DeltaY * $index, 57, 21, BitOR($ES_READONLY, $ES_RIGHT))
        _SetCtrlFontAndColor(-1, 0, 0xFFFFFF, 0x161616)
        $I_DailyPerc[$index] = GUICtrlCreateInput(Round((($last / $opening) - 1) * 100, 2), 110 + 62 + 62 + 62 + 62, 32 + $DeltaY * $index, 57, 21, BitOR($ES_READONLY, $ES_CENTER))
        _SetCtrlFontAndColor(-1, 0, 0xFFFFFF, 0x161616)
    Else
        GUICtrlSetData($I_Open[$index], Round($opening, 3))
        GUICtrlSetData($I_High[$index], Round($High, 3))
        GUICtrlSetData($I_Low[$index], Round($Low, 3))
        GUICtrlSetData($I_Current[$index], Round($last, 3))
        GUICtrlSetData($I_DailyPerc[$index], Round((($last / $opening) - 1) * 100, 2))
    EndIf
    If $last >= $I_CurrentTemp[$index] Then
        GUICtrlSetColor($I_Current[$index], $verde)
    Else
        GUICtrlSetColor($I_Current[$index], 0xED7D31) ; RED
    EndIf
    If GUICtrlRead($I_DailyPerc[$index]) >= 0 Then
        GUICtrlSetColor($I_DailyPerc[$index], $verde)
    Else
        GUICtrlSetColor($I_DailyPerc[$index], 0xED7D31) ; RED
    EndIf
    $I_CurrentTemp[$index] = $last
EndFunc   ;==>_WriteToGui

The whole GUI has more data than these, they are filled by other funcs, but they are just calc:
screenshot_1671.thumb.jpg.45752272cd1e7df395e8089a530749c3.jpg

So, basically, I have different functions that should run in parallel:

1) Data retrieving from Exchange (1 to n where n is the number of assets I have on GUI) - every 5 secs

2) Candlestick retrieving (again for every asset in GUI) - every 5 mins

Based on these the system will decide (based on settings) to buy/sell/stay

More, the checkboxes on the left enable a graph for the checked pair. If enabled it load, for every enabled graph, candlesticks for 100 tics and last values

screenshot_1672.jpg.f7aa956dcbcd4a3a7042414a4b5ea136.jpg

and also this should run in parallel.

That's it.

It would be nice if I could have, in parallel

  • N tasks running to call APIs to retrieve data for the N pairs  (or 1 doing the cycle)
  • N tasks running to monitor values of the N pairs and perform actions (or 1 doing the cycle)
  • M tasks running to draw the graphs, the checked ones
  • Availability to click any button on the Dashboard

Thanks and let me know if you need further details,

Marco

 

Edited by marko001
Link to post
Share on other sites

Hi @marko001 , seems an interesting program..... well, ok for the _WriteToGui() function, but I don't see the CryptoQueryPublic() and CoinbaseProQueryPublic() functions which I think are the ones to run in multitask..... however, although it seems to me that this program lends itself well to exploiting the multitask features offered by my udf, I believe that a small rewriting of some parts or at least a reorganization of the overall flow is necessary to allow the execution of the "multitasking" and relative parsing of the returned data .... it is not a trivial change I think ... and for sure an overview of the whole script is needed. You can send it to me if you want, or maybe (better yet) post it here if you prefer, so I or maybe others who are interested too can hopefully try to edit and adapt it.

 

image.jpeg.9f1a974c98e9f77d824b358729b089b0.jpeg Chimp

small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Link to post
Share on other sites

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...