automationcameraautoitmilestone

Milestone XProtect Automation


I'm trying to create some automation code for Milestone XProtect, a camera surveillance software, and I need a bit of help. I was originally using Batch scripting and VBScript to attempt my goal, but it doesn't seem to work for me

    #include <MsgBoxConstants.au3>                          ;Import Message Box

Local Const $milestone = "C:\Program Files\Milestone\XProtect Smart Client\Client.exe"  ;Local Variable $milestone set to file path

Local $iFileExists = FileExists($milestone)                     ;Variable that sees if file exists($milestone)

If $iFileExists Then
    Run($milestone)
>   ;[Unknown Variables]                                ;***Figure out the "Window Title", Class, and Instance***
    Send("{TAB}")
    Send("{TAB}")
    Send("[Insert Camera IP Address Here]")                     ;Between [] different for each .exe I'll create
    Send("{ENTER}")
>   ;[Unknown Variables]                    ;***Figure out items in camera window to see when its fully loaded***
Else
    MsgBox($MB_SYSTEMMODAL, "", "Milestone XProtect wasn't found on this computer" & @CRLF)     ;Error Message "File not Found"
EndIf

As of right now, my code sets a variable of the path to Milestone on the computer, and the if statement checks if the file exists. If it exists, then it'll run the program. The next line of code is supposed to wait until the program is fully loaded before sending two tab keys, the ip address to the cameras server, then and enter key. The last line of code in the if statement is supposed to check and see if the cameras have loaded up fully before ending the program.

What I need help on are the two sections labeled [Unknown Variables] in my code:

  1. I need to know when the program is loaded up to the server selection screen
  2. I need to know when the cameras server has loaded completely before I end the program

Can anyone help?


Solution

  • Solution

    I have put together some of the missing elements you needed in order to accomplish this task while maintaining simplicity, as well as added annotations to help describe each line.

    There are several ways to continue the automation process, such as offering a drop down list of different IP Address's or an Input box instead of hardcoding the address. You will just have to evolve the script over time adding one element at a time.

    AutoIt Script

    #include <MsgBoxConstants.au3>
    
    Local Const $milestone = "C:\Program Files\Milestone\XProtect Smart Client\Client.exe" ;Path to software
    
    Local $iFileExists = FileExists($milestone)                                 ;Check if the file exists, returns a 1 or 0
    
    If $iFileExists Then                                                        ;If FileExists = 1 (exists)
        ;$inputbox = InputBox("Title", "Enter the IP Address of the camera.", "127.0.0.1")  ;Uncomment out if you would like to prompt for input for the IP Address. Used on line 19.
        ;If @error Then Exit                                                    ;@Error can mean Cancel button was pushed, so exit if canceled.
        $iPID = Run($milestone)                                                 ;Runs the software and returns the Process ID
        Sleep(1000)                                                             ;sleep for 1 second
        If @error Then MsgBox(0, "Title", "The program could not launch.")      ;If Run returns a 0 or error, there was a problem
        $handle = _GetHandleFromPID($iPID)                                      ;Retrieve the handle of the program ran, jump to the function below
        If $handle = 0 Then MsgBox(0, "Title", "The handle could not be found.");If the Handle returned is 0, there was no match
        WinWaitActive($handle)                                                  ;Wait for the program to be activated
        Send("{TAB}")                                                           ;send keystrokes to active window
        Send("{TAB}")
        Send("[Insert Camera IP Address Here]")                                 ;Hardcoded IP Address
        ;Send($inputbox)                                                            ;Uncomment out this line and comment out the above line to utilize the input box method instead of hardcoding IP Addresses.
        Send("{ENTER}")
       ;[Unknown Variables]                                                     ;***Figure out items in camera window to see when its fully loaded***
    Else
        MsgBox($MB_SYSTEMMODAL, "", "Milestone XProtect wasn't found on this computer" & @CRLF)
    EndIf
    
    func _GetHandleFromPID($PID)                                                ;Call function with the PID returned from Run function
        $WinList = WinList()                                                    ;Assign WinList to a variable
        for $i = 1 to $WinList[0][0]                                            ;Run through each Window in WinList, 2D array [titles][handles]
            If WinGetProcess($WinList[$i][1]) = $PID then                       ;Look for a Window with the correct PID
                Return $WinList[$i][1]                                          ;Assign the matching Windows handle to the variable
            EndIf
        Next
        Return 0                                                                ;Return 0 if no matches were found
    EndFunc
    

    Window Handle

    Retrieving the window handle is important as it ensures the keystrokes are being sent to the last ran instance of the Milestone software, if several are running.

    Thoughts On Retrying

    As for the second question, there are many ways you could achieve checking for a connection with a timeout to retry. However, it mostly depends on the interaction available via AutoIt. A good place to start is the AutoIt Window Info Tool. You can drag and drop the crosshair to elements of a window to identify controls. Image below shows the tool when focusing the Windows Calculator.

    Example of Calculator.exe

    Example

    If there is a popup window that displays when a server cannot connect you could intercept that to signal a retry. There are options to search for an image or pixels on the screen if all you have to go off of is a blank video monitor. Or perhaps a good server connection will offer some type of alert that AuoIt can capture and successfully close when satisfied, or retry in X seconds if not.

    UPDATE

    Here is the program with the GUI and Combo Box option, this utilizes 2D arrays.

    #include <MsgBoxConstants.au3>
    #include <Array.au3>
    #include <ComboConstants.au3>
    #include <GUIConstantsEx.au3>
    #include <WindowsConstants.au3>
    
    Local Const $milestone = "C:\Program Files\Milestone\XProtect Smart Client\Client.exe" ;Path to software
    
    Local $iFileExists = FileExists($milestone)                                     ;Check if the file exists, returns a 1 or 0
    Global $list = [["Austin, TX","192.168.0.0"], ["Memphis, TN","192.168.0.1"]]    ;Enter all your selections, this builds a 2D Array [X][0] is the name [X][1] is the IP
    
    If $iFileExists Then                                                            ;If FileExists = 1 (exists) then
        Local $GUI = GUICreate("TITLE", 267, 115)                                   ;Create the GUI
        $button = GUICtrlCreateButton("Start", 96, 56, 65, 25)                      ;Create a button
        $server = GUICtrlCreateCombo("Select Server", 8, 8, 249, 25, $CBS_DROPDOWN) ;Create the combo box
        For $i = 0 To UBound($list) - 1                                             ;Populate combo box with first value (name) from array
            If $list[$i][0] <> "" Then $var = GUICtrlSetData($server, $list[$i][0]) ;Ensure array is not empty
        Next
    
        GUISetState(@SW_SHOW)
    
        While 1                                                                     ;Enter loop until user closes or presses button
            Switch GUIGetMsg()
                Case $GUI_EVENT_CLOSE                                               ;Exit when closed
                    Exit
                Case $button                                                        ;Store selection into variable, delete the GUI, and run the start function
                    $selection = GUICtrlRead($server)
                    GUIDelete()
                    start()
            EndSwitch
        WEnd
    Else                                                                            ;otherwise, message
        MsgBox($MB_SYSTEMMODAL, "", "Milestone XProtect wasn't found on this computer" & @CRLF)
    EndIf
    
    Func start()
        $iPID = Run($milestone)                                                     ;Runs the software and returns the Process ID
        Sleep(1000)                                                                 ;sleep for 1 second
        If @error Then MsgBox(0, "Title", "The program could not launch.")          ;If Run returns a 0 or error, there was a problem
        $handle = _GetHandleFromPID($iPID)                                          ;Retrieve the handle of the program ran, jump to the function below
        If $handle = 0 Then MsgBox(0, "Title", "The handle could not be found.")    ;If the Handle returned is 0, there was no match
        WinWaitActive($handle)                                                      ;Wait for the program to be activated
        Send("{TAB}")                                                               ;send keystrokes to active window
        Send("{TAB}")
        For $i = 0 to UBound($list) - 1                                             ;Find the IP address that matches the selection and send it
            If $list[$i][0] = $selection Then Send($list[$i][1])
        Next
        Send("{ENTER}")
        Exit                                                                        ;Exit for now until you can capture a succesful run
       ;[Unknown Variables]                                                         ;***Figure out items in camera window to see when its fully loaded***
    EndFunc
    
    func _GetHandleFromPID($PID)                                                    ;Call function with the PID returned from Run function
        $WinList = WinList()                                                        ;Assign WinList to a variable
        for $i = 1 to $WinList[0][0]                                                ;Run through each Window in WinList, 2D array [titles][handles]
            If WinGetProcess($WinList[$i][1]) = $PID then                           ;Look for a Window with the correct PID
                Return $WinList[$i][1]                                              ;Assign the matching Windows handle to the variable
            EndIf
        Next
        Return 0                                                                    ;Return 0 if no matches were found
    EndFunc
    

    UPDATE #2

    1. I have added a way to add new servers to the list and store them locally.
    2. I made a hidden hotkey to open up that file if changes need to be made or servers deleted, you can enter that file by pressing the F1 key on your keyboard. I will let you figure out how to further customize this.
    3. If no server is selected nothing happens, otherwise start the program.
    4. Currently this will launch the program out of focus and wait until it has focus again to input the keystrokes (in case someone wants to wait 1 second or 100 seconds before clicking back in the program).
    5. If this still does not suffice or the window takes control after the splash screen by default then change the sleep timer from 1000 (1 second) to whatever you want.
    6. Pay attention to the first region in the script, it requires some customizing. Set the path to the program, the title of THIS program, and an icon file.
    7. To compile it just use the AutoIt compiler, this will allow the icon to work properly.

    #include <MsgBoxConstants.au3>
    #include <Array.au3>
    #include <ComboConstants.au3>
    #include <ButtonConstants.au3>
    #include <EditConstants.au3>
    #include <GUIConstantsEx.au3>
    #include <WindowsConstants.au3>
    #include <GuiComboBox.au3>
    
    #Region EDIT THE BELOW INFORMATION-------------------------------------------------------------------------------------------------------------------------------------------
    Local Const $milestone = "C:\Program Files\Milestone\XProtect Smart Client\Client.exe"  ;Path to software
    Local $Title = "Program Title"                                                          ;Give me a name
    Local $icon = "Full\Path\To\Icon.ico"
    #EndRegion-------------------------------------------------------------------------------------------------------------------------------------------------------------------
    
    #Region Script---------------------------------------------------------------------------------------------------------------------------------------------------------------
    Local $iFileExists = FileExists($milestone)                                     ;Check if the file exists, returns a 1 or 0
    Global $list = IniReadSection(@TempDir & "\" & $Title & "\config.ini","Server") ;Read the ini file, this builds a 2D Array [X][0] is the key [X][1] is the value
    HotKeySet("{F1}", "help")
    
    If $iFileExists Then                                                            ;If FileExists = 1 (exists) then
        If Not FileExists(@TempDir & "\" & $Title) Then                             ;If config directory does not exists then
            Do
            DirCreate(@TempDir & "\" & $Title)                                      ;Create a directory in TempDir for the config.ini file to be stored locally
            Until FileExists(@TempDir & "\" & $Title)
            IniWrite(@TempDir & "\" & $Title & "\config.ini", "Server", "", "")
        EndIf
        Local $GUI = GUICreate($Title, 267, 115)                                    ;Create the GUI
        GUISetIcon($icon, -1)                                                       ;Create icon
        $start = GUICtrlCreateButton("Start", 40, 72, 65, 25)                       ;Create a button
        $create = GUICtrlCreateButton("Add Server", 160, 72, 65, 25)
        $server = GUICtrlCreateCombo("Select Server", 8, 8, 249, 25, $CBS_DROPDOWN) ;Create the combo box
        For $i = 1 To UBound($list) - 1                                             ;Populate combo box with first value (name) from array
            If $list[$i][0] <> "" Then GUICtrlSetData($server, $list[$i][0])        ;Ensure array is not empty and fill combox with KEYS
        Next
        $servername = GUICtrlCreateInput("Server Name", 8, 40, 121, 21)
        GUICtrlSetState(-1, $GUI_HIDE)
        $serverip = GUICtrlCreateInput("IP Address", 136, 40, 121, 21)
        GUICtrlSetState(-1, $GUI_HIDE)
    
        GUISetState(@SW_SHOW)
    
        While 1                                                                     ;Enter loop until user closes or presses button
            Switch GUIGetMsg()
                Case $GUI_EVENT_CLOSE                                               ;Exit when closed
                    Exit
                Case $start                                                         ;Store selection into variable, delete the GUI, and run the start function
                    $selection = GUICtrlRead($server)
                    If $selection <> "Select Server" Then
                        GUIDelete()
                        start()
                    EndIf
                Case $create
                    If GUICtrlRead($create) = "Add Server" Then
                        GUICtrlSetState($servername, $GUI_SHOW)
                        GUICtrlSetState($serverip, $GUI_SHOW)
                        GUICtrlSetData($create, "Create")
                    Else
                        If (GUICtrlRead($servername) <> "" And GUICtrlRead($servername) <> "Server Name" And GUICtrlRead($serverip) <> "" And GUICtrlRead($serverip) <> "IP Address") Then
                            IniWrite(@TempDir & "\" & $Title & "\config.ini", "Server", GUICtrlRead($servername), GUICtrlRead($serverip))
                            GUICtrlSetState($servername, $GUI_HIDE)
                            GUICtrlSetState($serverip, $GUI_HIDE)
                            GUICtrlSetData($create, "Add Server")
                        Else
                            MsgBox($MB_ICONINFORMATION, $Title, "Invalid Server Name and IP Address.")
                        EndIf
                        For $i = UBound($list) - 1 To 0 Step -1
                            _GUICtrlComboBox_DeleteString($server, $i)
                        Next
                        Local $list = IniReadSection(@TempDir & "\" & $Title & "\config.ini","Server")
                        For $i = 1 To UBound($list) - 1
                            If $list[$i][0] <> "" Then GUICtrlSetData($server, $list[$i][0])
                        Next
                    EndIf
            EndSwitch
        WEnd
    Else                                                                            ;otherwise, message
        MsgBox($MB_SYSTEMMODAL, "", "Milestone XProtect wasn't found on this computer" & @CRLF)
    EndIf
    
    Func start()
        $iPID = Run($milestone,"", @SW_SHOWNOACTIVATE)                              ;Runs the software and returns the Process ID
        Sleep(1000)                                                                 ;sleep for 1 second
        If @error Then MsgBox(0, $Title, "The program could not launch.")           ;If Run returns a 0 or error, there was a problem
        $handle = _GetHandleFromPID($iPID)                                          ;Retrieve the handle of the program ran, jump to the function below
        If $handle = 0 Then MsgBox(0, $Title, "The handle could not be found.")     ;If the Handle returned is 0, there was no match
        Sleep(1000)
        WinWaitActive($handle)                                                      ;Wait for the program to be activated
        Send("{TAB}")                                                               ;send keystrokes to active window
        Send("{TAB}")
        For $i = 0 to UBound($list) - 1                                             ;Find the IP address that matches the selection and send it
            If $list[$i][0] = $selection Then Send($list[$i][1])
        Next
        Send("{ENTER}")
        Exit                                                                        ;Exit for now until you can capture a succesful run
    EndFunc
    
    func _GetHandleFromPID($PID)                                                    ;Call function with the PID returned from Run function
        $WinList = WinList()                                                        ;Assign WinList to a variable
        for $i = 1 to $WinList[0][0]                                                ;Run through each Window in WinList, 2D array [titles][handles]
            If WinGetProcess($WinList[$i][1]) = $PID then                           ;Look for a Window with the correct PID
                Return $WinList[$i][1]                                              ;Assign the matching Windows handle to the variable
            EndIf
        Next
        Return 0                                                                    ;Return 0 if no matches were found
    EndFunc
    
    Func help()
        ShellExecute(@TempDir & "\" & $Title & "\config.ini")
    EndFunc
    #EndRegion--------------------------------------------------------------------------------------------------------------------------------------------------------------------