Jump to content

PTRobot API DLLs for Primera


GPinzone
 Share

Recommended Posts

Primera makes CD/DVD/Bluray disc duplicators. I own one, but I'm not happy with the software I'm forced to use. Unfortunately, most other software products can't recognize or control the robot. (IMGBurn does but it will not send the disc to the printer and it doesn't handle certain error conditions to my satisfaction.)

This AutoIt script requires the Primera API which contains a few DLLs that AutoIt interfaces with. I create a number of AutoIt functions that match the ones in the API, plus a few new ones to help streamline the process. There's no GUI as I designed this to be for the command line. The program gives the commands to load, burn, print, and unload a disc. It implements IMGBurn through a command line interface to do the burning, but you can use any disc burning program you like. (Do NOT turn on support for Primera duplicators in IMGBurn if using this script.) Refer to the Primera API documentation for more info.

I did not provide functions for every possible API feature. However, I included everything you would need for normal disc processing.

This is my largest AutoIt script for me and it was the first time I attempted calls to DLL files.

UPDATED CODE IN POST #3

Gerard J. Pinzone

gpinzone AT yahoo.com

Edited by GPinzone
Gerard J. Pinzonegpinzone AT yahoo.com
Link to comment
Share on other sites

Nice intro to DLLs! :(

Very cool that Primera supports a usable API, too.

Might have to look into the Pimera as they appear to have more of an open mind.

I have a Rimage burner/printer at work, but that company is openly hostile to anyone wanting to program for it. You will pay (dearly) for their software or get stuffed. I dared to ask one of their tech support reps about drivers for Linux once and he reacted like I had asked him for bomb grade plutonium. This will be our last Rimage.

:graduated:

Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law
Link to comment
Share on other sites

Updated code:

#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GuiListBox.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#include <ListboxConstants.au3>

#include <Array.au3>
#include <INet.au3>
#include <Misc.au3>

If _Singleton("Primera", 1) = 0 Then
    ; We know the script is already running. Let the user know.
    MsgBox(0, "Error: Multiple Copies", "This script is already running. Using multiple copies of this script at the same time is unsupported!")
    Exit
EndIf

Opt('MustDeclareVars', 1)

Global Const $aisEntities[246][2] = [[34, 'quot'],[38, 'amp'],[39, 'apos'],[60, 'lt'],[62, 'gt'],[160, 'nbsp'],[161, 'iexcl'],[162, 'cent'],[163, 'pound'],[164, 'curren'],[165, 'yen'],[166, 'brvbar'],[167, 'sect'],[168, 'uml'],[169, 'copy'],[170, 'ordf'],[171, 'laquo'],[172, 'not'],[173, 'shy'],[174, 'reg'],[175, 'macr'],[176, 'deg'],[177, 'plusmn'],[180, 'acute'],[181, 'micro'],[182, 'para'],[183, 'middot'],[184, 'cedil'],[186, 'ordm'],[187, 'raquo'],[191, 'iquest'],[192, 'Agrave'],[193, 'Aacute'],[194, 'Acirc'],[195, 'Atilde'],[196, 'Auml'],[197, 'Aring'],[198, 'AElig'],[199, 'Ccedil'],[200, 'Egrave'],[201, 'Eacute'],[202, 'Ecirc'],[203, 'Euml'],[204, 'Igrave'],[205, 'Iacute'],[206, 'Icirc'],[207, 'Iuml'],[208, 'ETH'],[209, 'Ntilde'],[210, 'Ograve'],[211, 'Oacute'],[212, 'Ocirc'],[213, 'Otilde'],[214, 'Ouml'],[215, 'times'],[216, 'Oslash'],[217, 'Ugrave'],[218, 'Uacute'],[219, 'Ucirc'],[220, 'Uuml'],[221, 'Yacute'],[222, 'THORN'],[223, 'szlig'],[224, 'agrave'],[225, 'aacute'],[226, 'acirc'],[227, 'atilde'],[228, 'auml'],[229, 'aring'],[230, 'aelig'],[231, 'ccedil'],[232, 'egrave'],[233, 'eacute'],[234, 'ecirc'],[235, 'euml'],[236, 'igrave'],[237, 'iacute'],[238, 'icirc'],[239, 'iuml'],[240, 'eth'],[241, 'ntilde'],[242, 'ograve'],[243, 'oacute'],[244, 'ocirc'],[245, 'otilde'],[246, 'ouml'],[247, 'divide'],[248, 'oslash'],[249, 'ugrave'],[250, 'uacute'],[251, 'ucirc'],[252, 'uuml'],[253, 'yacute'],[254, 'thorn'],[255, 'yuml'],[338, 'OElig'],[339, 'oelig'],[352, 'Scaron'],[353, 'scaron'],[376, 'Yuml'],[402, 'fnof'],[710, 'circ'],[732, 'tilde'],[913, 'Alpha'],[914, 'Beta'],[915, 'Gamma'],[916, 'Delta'],[917, 'Epsilon'],[918, 'Zeta'],[919, 'Eta'],[920, 'Theta'],[921, 'Iota'],[922, 'Kappa'],[923, 'Lambda'],[924, 'Mu'],[925, 'Nu'],[926, 'Xi'],[927, 'Omicron'],[928, 'Pi'],[929, 'Rho'],[931, 'Sigma'],[932, 'Tau'],[933, 'Upsilon'],[934, 'Phi'],[935, 'Chi'],[936, 'Psi'],[937, 'Omega'],[945, 'alpha'],[946, 'beta'],[947, 'gamma'],[948, 'delta'],[949, 'epsilon'],[950, 'zeta'],[951, 'eta'],[952, 'theta'],[953, 'iota'],[954, 'kappa'],[955, 'lambda'],[956, 'mu'],[957, 'nu'],[958, 'xi'],[959, 'omicron'],[960, 'pi'],[961, 'rho'],[962, 'sigmaf'],[963, 'sigma'],[964, 'tau'],[965, 'upsilon'],[966, 'phi'],[967, 'chi'],[968, 'psi'],[969, 'omega'],[977, 'thetasym'],[978, 'upsih'],[982, 'piv'],[8194, 'ensp'],[8195, 'emsp'],[8201, 'thinsp'],[8204, 'zwnj'],[8205, 'zwj'],[8206, 'lrm'],[8207, 'rlm'],[8211, 'ndash'],[8212, 'mdash'],[8216, 'lsquo'],[8217, 'rsquo'],[8218, 'sbquo'],[8220, 'ldquo'],[8221, 'rdquo'],[8222, 'bdquo'],[8224, 'dagger'],[8225, 'Dagger'],[8226, 'bull'],[8230, 'hellip'],[8240, 'permil'],[8242, 'prime'],[8243, 'Prime'],[8249, 'lsaquo'],[8250, 'rsaquo'],[8254, 'oline'],[8260, 'frasl'],[8364, 'euro'],[8465, 'image'],[8472, 'weierp'],[8476, 'real'],[8482, 'trade'],[8501, 'alefsym'],[8592, 'larr'],[8593, 'uarr'],[8594, 'rarr'],[8595, 'darr'],[8596, 'harr'],[8629, 'crarr'],[8656, 'lArr'],[8657, 'uArr'],[8658, 'rArr'],[8659, 'dArr'],[8660, 'hArr'],[8704, 'forall'],[8706, 'part'],[8707, 'exist'],[8709, 'empty'],[8711, 'nabla'],[8712, 'isin'],[8713, 'notin'],[8715, 'ni'],[8719, 'prod'],[8721, 'sum'],[8722, 'minus'],[8727, 'lowast'],[8730, 'radic'],[8733, 'prop'],[8734, 'infin'],[8736, 'ang'],[8743, 'and'],[8744, 'or'],[8745, 'cap'],[8746, 'cup'],[8747, 'int'],[8764, 'sim'],[8773, 'cong'],[8776, 'asymp'],[8800, 'ne'],[8801, 'equiv'],[8804, 'le'],[8805, 'ge'],[8834, 'sub'],[8835, 'sup'],[8836, 'nsub'],[8838, 'sube'],[8839, 'supe'],[8853, 'oplus'],[8855, 'otimes'],[8869, 'perp'],[8901, 'sdot'],[8968, 'lceil'],[8969, 'rceil'],[8970, 'lfloor'],[8971, 'rfloor'],[9001, 'lang'],[9002, 'rang'],[9674, 'loz'],[9824, 'spades'],[9827, 'clubs'],[9829, 'hearts'],[9830, 'diams']]

Global $hPTROBOTDLL = DllOpen("U:\Macros\PTRobot.dll")
Global $RobotType
Global $RobotAddress
Global $NumRobots
Global $DriveAddress[10]
Global $NumDrives
Global $NumPrinters
Global $NumBins
Global $SystemState
Global $SystemError
Global $NumDiscsProcessed = 0
Global $DriveLetter[10]
Global $NumSets = 1
Global $NoDocDisc = False

Global $Form_Status = GUICreate("IMGBurn AutoIt Script", 614, 163, 193, 124, BitOR($GUI_SS_DEFAULT_GUI, $WS_SIZEBOX, $WS_THICKFRAME))
Global $Label_Status = GUICtrlCreateLabel("", 16, 104, 580, 50, BitOR($SS_NOPREFIX, $SS_SUNKEN, $WS_BORDER))
Global $Label_Status_Title = GUICtrlCreateLabel("Status:", 16, 80, 37, 17, $SS_NOPREFIX)
Global $Label_NumSets_Title = GUICtrlCreateLabel("Total Number of Sets:", 424, 8, 107, 17, $SS_NOPREFIX)
Global $Label_NumSets = GUICtrlCreateLabel($NumSets, 568, 8, 26, 17, $SS_NOPREFIX)
Global $Label_Set_Title = GUICtrlCreateLabel("Current Set:", 424, 32, 60, 17, $SS_NOPREFIX)
Global $Label_Set = GUICtrlCreateLabel("1", 568, 32, 26, 17, $SS_NOPREFIX)
Global $Label_Title = GUICtrlCreateLabel("", 16, 24, 392, 17, $SS_NOPREFIX)
Global $Label_Job_Title = GUICtrlCreateLabel("Job:", 16, 8, 24, 17)
Global $Label_DiscsProcessed_Title = GUICtrlCreateLabel("Number of discs processed:", 424, 80, 135, 17, $SS_NOPREFIX)
Global $Label_DiscsProcessed = GUICtrlCreateLabel($NumDiscsProcessed, 568, 80, 26, 17)
Global $Label_Disc_Title = GUICtrlCreateLabel("Disc:", 424, 56, 28, 17, $SS_NOPREFIX)
Global $Label_Disc = GUICtrlCreateLabel("1", 456, 56, 28, 17, BitOR($SS_CENTER, $SS_NOPREFIX))
Global $Label_NumDiscss_Title = GUICtrlCreateLabel("of", 488, 56, 21, 17, BitOR($SS_CENTER, $SS_NOPREFIX))
Global $Label_NumDiscs = GUICtrlCreateLabel("", 512, 56, 39, 17, BitOR($SS_CENTER, $SS_NOPREFIX))

Global Const $MAX_NUMSETS = 50

Global Const $LOCATION_AUTO = 0
Global Const $LOCATION_RIGHT = 1
Global Const $LOCATION_LEFT = 2
Global Const $LOCATION_PRINTER = 100
Global Const $LOCATION_REJECT = 200

Global Const $CLEARDRIVE_NO = 0
Global Const $CLEARDRIVE_YES = 1

Global Const $DRIVE_OPEN = 0
Global Const $DRIVE_CLOSE = 1

; SET OUTPUT BINS HERE
Global Const $CD_BIN_INPUT = $LOCATION_LEFT
Global Const $DVD_BIN_INPUT = $LOCATION_RIGHT
Global Const $BIN_OUTPUT = $LOCATION_REJECT

Global Const $PQ_LOW = 0
Global Const $PQ_MED = 1
Global Const $PQ_BETTER = 2
Global Const $PQ_HIGH = 3
Global Const $PQ_BEST = 4

Global Const $PTACT_ALIGNPRINTER = 0x00000001
Global Const $PTACT_IGNOREINKLOW = 0x00000002
Global Const $PTACT_DISABLEPWRBUTTON = 0x00000004
Global Const $PTACT_REINIT_DRIVES = 0x00000008
Global Const $PTACT_IDENTIFY = 0x00000010
Global Const $PTACT_CANCELCMD = 0x00000020
Global Const $PTACT_ENABLEPWRBUTTON = 0x00000040
Global Const $PTACT_RESETSYSTEM = 0x00000080
Global Const $PTACT_CHECKDISCS = 0x00000100 ; Check number of discs in bins
Global Const $PTACT_CLEANCARTRIDGES = 0x00000200 ; Clean the cartridges
Global Const $PTACT_CALIBRATE_ONE_DISC = 0x00000400 ; SE, II, Pro: Calibrate for one disc (user must put one disc in each bin).
Global Const $PTACT_CHANGE_CARTRIDGE = 0x00000800 ; SE, II, Pro: Start the cartridge change procedure
Global Const $PTACT_END_CARTRIDGE_CHANGE = 0x00001000 ; SE: End the cartridge change (can close lid also)
Global Const $PTACT_SHIP_POSITION = 0x00002000 ; SE, II, Pro: Move the picker to the shipping position
Global Const $PTACT_RESET_LEFT_INK_LEVELS = 0x00004000 ; II: Clears the ink spits for the LEFT cartridge
Global Const $PTACT_RESET_RIGHT_INK_LEVELS = 0x00008000 ; II: Clears the ink spits for the RIGHT cartridge
Global Const $PTACT_ALLOW_NO_CARTRIDGES = 0x00010000 ; SE, II, Pro: Allows unit to operate non-printing robotics without a cartridge
Global Const $PTACT_XI_LIGHT_OFF = 0x00020000
Global Const $PTACT_XI_LIGHT_ON = 0x00040000
Global Const $PTACT_XI_LIGHT_FLASH = 0x00080000
Global Const $PTACT_UNHOOK_PICKER = 0x00100000
Global Const $PTACT_AUTOPRINTER_MODE = 0x00200000 ; DP4100: can perform a faster multiple copy print-only job by calling PTRobot_SetPrintCopies() prior to calling the print function (e.g. PTRobot_PrintFile()).
Global Const $PTACT_FAN_ON = 0x00400000 ; DP4100: turn on system fan
Global Const $PTACT_FAN_OFF = 0x00800000 ; DP4100: turn off system fan

Global Const $SYSERR_PTR_TRAY = 1
Global Const $SYSERR_CART_CODE = 2
Global Const $SYSERR_INPUT_EMPTY = 3
Global Const $SYSERR_PTR_COMM = 4
Global Const $SYSERR_CLR_EMPTY = 5
Global Const $SYSERR_BLK_EMPTY = 6
Global Const $SYSERR_BOTH_EMPTY = 7
Global Const $SYSERR_PICK = 8
Global Const $SYSERR_ARM_MOVE = 9
Global Const $SYSERR_CART_MOVE = 10
Global Const $SYSERR_INTERNAL_SW = 12
Global Const $SYSERR_NO_ROBODRIVES = 13
Global Const $SYSERR_OFFLINE = 14
Global Const $SYSERR_COVER_OPEN = 15
Global Const $SYSERR_PRINTER_PICK = 16
Global Const $SYSERR_MULTIPLE_PICK = 17
Global Const $SYSERR_MULTIPLEDISCS_IN_PRINTER = 18
Global Const $SYSERR_MULTIPLEDISCS_IN_RECORDER = 19
Global Const $SYSERR_DROPPED_DISC_RECORDER = 20
Global Const $SYSERR_DROPPED_DISC_BIN1 = 28
Global Const $SYSERR_DROPPED_DISC_BIN2 = 29
Global Const $SYSERR_DROPPED_DISC_PRINTER = 33
Global Const $SYSERR_DROPPED_DISC_REJECT = 34
Global Const $SYSERR_DROPPED_DISC_UNKNOWN = 35
Global Const $SYSERR_ALIGNNEEDED = 36
Global Const $SYSERR_COLOR_INVALID = 37
Global Const $SYSERR_BLACK_INVALID = 38
Global Const $SYSERR_BOTH_INVALID = 39
Global Const $SYSERR_NOCARTS = 40
Global Const $SYSERR_K_IN_CMY = 41
Global Const $SYSERR_CMY_IN_K = 42
Global Const $SYSERR_SWAPPED = 43
Global Const $SYSERR_PIGONPRO = 44
Global Const $SYSERR_ALIGNFAILED = 45
Global Const $SYSERR_DROPPED_DISC_PRINTER_FATAL = 46
Global Const $SYSERR_MULTIPLEDISCS_IN_RIGHTBIN = 47
Global Const $SYSERR_MULTIPLEDISCS_IN_LEFTBIN = 48
Global Const $SYSERR_CLR_EMPTY_FINAL = 49
Global Const $SYSERR_BLK_EMPTY_FINAL = 50
Global Const $SYSERR_BOTH_EMPTY_FINAL = 51
Global Const $SYSERR_WAITING_FOR_PRINTER = 52
Global Const $SYSERR_NO_DISC_IN_PRINTER = 53
Global Const $SYSERR_BUSY = 54
Global Const $SYSERR_PURGE = 55
Global Const $SYSERR_DOCK_SENSOR = 56
Global Const $SYSERR_ALREADY_PRINTED = 57
Global Const $SYSERR_UNKNOWN_HARDWARE = 58

Global Const $SYSSTATE_IDLE = 0
Global Const $SYSSTATE_BUSY = 1
Global Const $SYSSTATE_ERROR = 2

Global Const $ROBOT_DISCPUBLISHER = 0 ; Disc Publisher I
Global Const $ROBOT_DISCPUBLISHERII = 1 ; Disc Publisher II
Global Const $ROBOT_DISCPUBLISHERPRO = 2 ; Disc Publisher PRO
Global Const $ROBOT_COMPOSERMAX = 3 ; ComposerMAX
Global Const $ROBOT_RACKMOUNT_DPII = 4 ; Disc Publisher XR
Global Const $ROBOT_DISCPUBLISHER_XRP = 5 ; Disc Publisher XRP
Global Const $ROBOT_DISCPUBLISHER_SE = 6 ; Disc Publisher SE
Global Const $ROBOT_DISCPUBLISHERPRO_XI = 7 ; Disc Publisher Xi Series
Global Const $ROBOT_DISCPUBLISHER_4100 = 8 ; Disc Publisher 4100 Series

Global Const $PTROBOT_FEATURE_NOT_IMPLEMENTED = 520

Global Const $CUSTOM_FILE_NOT_FOUND = 901
Global Const $CUSTOM_FILE_FORMAT_WRONG = 902
Global Const $CUSTOM_INVALID_DRIVE_LETTER = 903

Func processJobs($Title, $Queue)

    Local $TimeStamp, $DupeTime, $DiscSet
    Local $QueueSize = UBound($Queue, 1)

    ; Store the time the process begins
    $TimeStamp = TimerInit()

    ; Exit script if ImgBurn is already running
    If ProcessExists("ImgBurn.exe") Then
        MsgBox(0, "IMGBurn AutoIt Script", "ImgBurn is already running.")
        Exit
    EndIf

    ; Configure GUI
    GUICtrlSetData($Label_Title, $Title)
    GUICtrlSetData($Label_NumSets, $NumSets)
    GUICtrlSetData($Label_NumDiscs, $QueueSize)

    GUISetState(@SW_SHOW, $Form_Status)

    ; Disable Screen Saver
    disableScreenSaver()

    PTRobot_Initialize()
    PTRobot_EnumRobots()
    PTRobot_EnumDrives()
    PTRobot_GetRobotInfo()

    ; Repeat process for each set of discs
    For $DiscSet = 1 To $NumSets

        ; Process the Queue
        PTRobot_ProcessQueue($Queue)
        ; Update GUI
        GUICtrlSetData($Label_Set, $DiscSet)

    Next

    PTRobot_Destroy()

    ; Enable Screen Saver
    enableScreenSaver()

    ; Calculate how long the process took
    $DupeTime = CalcDupeTime(TimerDiff($TimeStamp))

    sendEmail($Title, $Queue, $NumSets, $DupeTime)

EndFunc   ;==>processJobs

Func getNumSets()

    Local $nNumSets

    ; Check command line
    If $CmdLine[0] > 0 Then
        If $CmdLine[1] < 1 Or $CmdLine[1] > $MAX_NUMSETS Then
            MsgBox(0, "IMGBurn AutoIt Script", "Number of jobs must be between 1 and " & $MAX_NUMSETS & ".")
            Exit
        Else
            $nNumSets = $CmdLine[1]
        EndIf
    Else
        ; Return -1 if no command line argument given
        $nNumSets = -1
    EndIf

    Return $nNumSets

EndFunc   ;==>getNumSets

Func PTRobot_ProcessQueue($Queue)
    Local $QueueSize = UBound($Queue, 1)
    Local $Disc, $SourceBin
    Local $MissingFiles, $WrongFileFormat
    Local $result
    Local Const $LABEL_COL = 0 ; Array column used for disc labels.
    Local Const $IMAGE_COL = 1 ; Array column used for disc image files.

    ; Verify files exist.
    For $Disc = 0 To $QueueSize - 1
        If Not $Queue[$Disc][$LABEL_COL] = "" Then
            If Not FileExists($Queue[$Disc][$LABEL_COL]) Then
                $MissingFiles = $MissingFiles & $Queue[$Disc][$LABEL_COL] & @CRLF
            ElseIf Not _ValidateImageFile($Queue[$Disc][$LABEL_COL]) Then
                $WrongFileFormat = $WrongFileFormat & $Queue[$Disc][$LABEL_COL] & @CRLF
            EndIf
        EndIf
        If Not FileExists($Queue[$Disc][$IMAGE_COL]) Then
            $MissingFiles = $MissingFiles & $Queue[$Disc][$IMAGE_COL] & @CRLF
        EndIf
    Next

    If Not $MissingFiles = "" Then
        MsgBox(48, "Queue Error", "Files not found:" & @CRLF & $MissingFiles)
        PTRobot_Destroy()
        Exit ($CUSTOM_FILE_NOT_FOUND)
    EndIf

    If Not $WrongFileFormat = "" Then
        MsgBox(48, "Queue Error", "File format incorrect:" & @CRLF & $WrongFileFormat)
        PTRobot_Destroy()
        Exit ($CUSTOM_FILE_FORMAT_WRONG)
    EndIf

    PTRobot_Wait()

    For $Disc = 0 To $QueueSize - 1

        If StringInStr($Queue[$Disc][$LABEL_COL], "DVD", 1) Or StringInStr($Queue[$Disc][$IMAGE_COL], "DVD", 1) Then
            $SourceBin = $DVD_BIN_INPUT
        Else
            $SourceBin = $CD_BIN_INPUT
        EndIf

        GUICtrlSetData($Label_Disc, $Disc + 1) ; Update GUI

        WriteStatus("Loading drive..." & @CRLF)
        If $NumDiscsProcessed < $NumDrives Then ; First disc load in drive should check for disc in burner.
            PTRobot_LoadDrive(Mod($NumDiscsProcessed, $NumDrives), $SourceBin, $CLEARDRIVE_YES) ; Load first disc from source bin.
        Else
            PTRobot_LoadDrive(Mod($NumDiscsProcessed, $NumDrives), $SourceBin, $CLEARDRIVE_NO) ; Load subsequent disc from source bin.
        EndIf

        PTRobot_Wait()

        WriteStatus("Starting disc writer..." & @CRLF)
        $result = PTRobot_BurnImage(Mod($NumDiscsProcessed, $NumDrives), $Queue[$Disc][$IMAGE_COL]) ; Burn image file to disc.

        ; If disc was aborted, skip to next disc.
        If $result = 1 Then
            WriteStatus("Skipping " & $Queue[$Disc][$IMAGE_COL] & "..." & @CRLF)
            $NumDiscsProcessed = $NumDiscsProcessed + 1 ; Increment global counter.
            GUICtrlSetData($Label_DiscsProcessed, $NumDiscsProcessed)

            ContinueLoop
        EndIf

        PTRobot_Wait()

        If $Queue[$Disc][$LABEL_COL] = "" Or $NumPrinters < 1 Then
            WriteStatus("Unloading drive..." & @CRLF)
            PTRobot_UnLoadDrive(Mod($NumDiscsProcessed, $NumDrives), $BIN_OUTPUT) ; Unload disc to output bin.
        Else
            ; Check if burn was ignored.
            If $result = 2 Then
                $result = MsgBox(36, "Print...", "Print disc?" & @CRLF)
                ; No
                If $result = 7 Then
                    PTRobot_UnLoadDrive(Mod($NumDiscsProcessed, $NumDrives), $BIN_OUTPUT) ; Unload disc to output bin.
                    PTRobot_Wait()
                    $NumDiscsProcessed = $NumDiscsProcessed + 1 ; Increment global counter.
                    GUICtrlSetData($Label_DiscsProcessed, $NumDiscsProcessed)
                    ContinueLoop
                EndIf
            EndIf

            WriteStatus("Loading printer..." & @CRLF)
            PTRobot_LoadPrinterFromDrive(Mod($NumDiscsProcessed, $NumDrives)) ; Send disc to printer.

            PTRobot_Wait()

            WriteStatus("Printing label..." & @CRLF)
            PTRobot_PrintFile($Queue[$Disc][$LABEL_COL]) ; Print label.

            PTRobot_Wait()

            WriteStatus("Unloading printer..." & @CRLF)
            PTRobot_UnLoadPrinter($BIN_OUTPUT) ; Unload disc to output bin.
        EndIf

        $NumDiscsProcessed = $NumDiscsProcessed + 1 ; Increment global counter.
        GUICtrlSetData($Label_DiscsProcessed, $NumDiscsProcessed)

        PTRobot_Wait()
    Next
EndFunc   ;==>PTRobot_ProcessQueue

Func PTRobot_BurnImage($num, $Filename)
    Local $ErrorLevel
    Local $result = 0

    While 1 ; Infinte loop for "Retry" on failure.
        $ErrorLevel = RunWait('"C:\Program Files (x86)\ImgBurn\ImgBurn.exe" /MODE WRITE /SRC "' & $Filename & '" /DEST ' & $DriveLetter[$num] & ': /COPIES 1 /VERIFY NO /SPEED AUTO /TESTMODE NO /DELETEIMAGE NO /EJECT YES /WAITFORMEDIA /START /CLOSE /NOSAVESETTINGS')

        If $ErrorLevel Then
            PTRobot_OpenCloseDrive($num, $DRIVE_OPEN)
            WriteStatusError("Error: Disc write failed! - Exit Code: " & $ErrorLevel & @CRLF)
            WriteStatusError("Error: " & $Filename & " not successfully written!" & @CRLF)
            $result = MsgBox(50, "Burn Error", "Disc write failed!" & @CRLF & "Exit Code: " & $ErrorLevel)
            ; Abort
            If $result = 3 Then
                WriteStatus("Rejecting disc..." & @CRLF)
                PTRobot_UnLoadDrive($num, $LOCATION_REJECT) ; Send to reject pile.
                $result = MsgBox(36, "Burn Error", "Close program?" & @CRLF)
                PTRobot_Wait()
                ; Yes
                If $result = 6 Then
                    PTRobot_Destroy()
                    Exit ($ErrorLevel)
                    ; No
                Else
                    $result = 1 ; Return value for aborted error.
                    ExitLoop
                EndIf
                ; Retry
            ElseIf $result = 4 Then
                PTRobot_OpenCloseDrive($num, $DRIVE_CLOSE)
                ; Ignore
            ElseIf $result = 5 Then
                $result = 2 ; Return value for ignored error.
                ExitLoop
            EndIf
            ; Success
        Else
            WriteStatus("File: " & $Filename & " successfully written." & @CRLF)
            ExitLoop
        EndIf
    WEnd

    Return $result
EndFunc   ;==>PTRobot_BurnImage

Func PTRobot_Wait()
    Local $ErrorMsg

    Sleep(1000)
    While Not PTRobot_GetRobotStatus()
        If $SystemError Then
            PTRobot_ErrorMsgBox($SystemError)
        ElseIf Not $SystemState Then
            ExitLoop
        Else
            Sleep(1000)
        EndIf
    WEnd

    Sleep(1000)
    While Not PTRobot_GetRobotStatus()
        If $SystemError Then
            If $SystemError = $SYSERR_CLR_EMPTY Then
                $ErrorMsg = "WARNING:  The color cartridge is LOW on ink."
                WriteStatusError("Error: " & $ErrorMsg & @CRLF)
                PTRobot_KillSystemError(0)
                Sleep(1000)
            ElseIf $SystemError = $SYSERR_BLK_EMPTY Then
                $ErrorMsg = "WARNING:  The black cartridge is LOW on ink."
                WriteStatusError("Error: " & $ErrorMsg & @CRLF)
                PTRobot_KillSystemError(0)
                Sleep(1000)
            ElseIf $SystemError = $SYSERR_BOTH_EMPTY Then
                $ErrorMsg = "WARNING:  Both ink cartridges are LOW on ink."
                WriteStatusError("Error: " & $ErrorMsg & @CRLF)
                PTRobot_KillSystemError(0)
                Sleep(1000)
            Else
                PTRobot_ErrorMsgBox($SystemError)
            EndIf
        ElseIf Not $SystemState Then
            ExitLoop
        Else
            Sleep(1000)
        EndIf
    WEnd




EndFunc   ;==>PTRobot_Wait

Func PTRobot_GetErrorString()
    Local $result
    Local $err = @error

    Local $dwErrorNum
    Local $wszErrorString
    Local $dwMaxLength
    Local $dwLanguage

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetErrorString", "hwnd", $RobotAddress, "dword", $dwErrorNum, "wstr", $wszErrorString, "dword", $dwMaxLength, "dword", $dwLanguage)

    $dwErrorNum = $result[2]
    $wszErrorString = $result[3]
    $dwMaxLength = $result[4]
    $dwLanguage = $result[5]

    MsgBox(0, "", "Status: " & $result[0] & @CRLF & _
            "System Number Error: " & $dwErrorNum & @CRLF & _
            "Error String: " & $wszErrorString & @CRLF & _
            "Max Length: " & $dwMaxLength & @CRLF & _
            "Language: " & $dwLanguage)

    Return $result[0]
EndFunc   ;==>PTRobot_GetErrorString

Func PTRobot_Initialize()
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_Initialize")

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf

EndFunc   ;==>PTRobot_Initialize

Func PTRobot_Destroy()
    Local $result
    Local $err = @error

    PTRobot_Wait()
    DllCall($hPTROBOTDLL, "int", "PTRobot_SystemAction", "hwnd", $RobotAddress, "dword", $PTACT_XI_LIGHT_OFF)
    PTRobot_Wait()

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_Destroy")

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        Exit ($result[0])
    EndIf

EndFunc   ;==>PTRobot_Destroy

Func PTRobot_EnumRobots()
    Local $result
    Local $err = @error

    Local $hRobots = DllStructCreate("hwnd[10]")
    Local $dwNumRobots = DllStructCreate("dword")
    DllStructSetData($dwNumRobots, 1, 10) ; Set input to be the size of the array

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_EnumRobots", "ptr", DllStructGetPtr($hRobots), "ptr", DllStructGetPtr($dwNumRobots))

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf

    ; Set Global Variables
    $RobotAddress = DllStructGetData($hRobots, 1, 1)
    $NumRobots = DllStructGetData($dwNumRobots, 1)
EndFunc   ;==>PTRobot_EnumRobots

Func PTRobot_EnumDrives()
    Local $result
    Local $err = @error

    Local $hDrives = DllStructCreate("hwnd[10]")
    Local $dwNumDrives = DllStructCreate("dword")
    DllStructSetData($dwNumDrives, 1, 10) ; Set input to be the size of the array

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_EnumDrives", "hwnd", $RobotAddress, "ptr", DllStructGetPtr($hDrives), "ptr", DllStructGetPtr($dwNumDrives))

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf

    ; Set Global Variables
    $NumDrives = DllStructGetData($dwNumDrives, 1)
    ReDim $DriveAddress[$NumDrives]
    ReDim $DriveLetter[$NumDrives]
    For $i = 0 To $NumDrives - 1
        $DriveAddress[$i] = DllStructGetData($hDrives, 1, $i + 1)
        $DriveLetter[$i] = Chr(Dec(StringRight($DriveAddress[$i], 2)))
        ;MsgBox(0, "Debug", "Drive " & $i & ": " & $DriveLetter[$i])
        If Not StringRegExp($DriveLetter[$i], "^[A-Z]$") Then
            PTRobot_ErrorMsgBox($CUSTOM_INVALID_DRIVE_LETTER)
            PTRobot_Destroy()
            Exit ($CUSTOM_INVALID_DRIVE_LETTER)
        EndIf
    Next

EndFunc   ;==>PTRobot_EnumDrives

Func PTRobot_GetRobotStatus()
    Local $result
    Local $err = @error

    Local $dRobotStatus = DllStructCreate("dword dwSystemState;dword dwSystemError;dword dwCurrColorSpits;dword dwCurrBlackSpits;dword dwFullColorSpits;dword dwFullBlackSpits")

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetRobotStatus", "hwnd", $RobotAddress, "ptr", DllStructGetPtr($dRobotStatus))

    ;MsgBox(0, "", "Status: "         & $result[0]                                          & @CRLF & _
    ;              "System State: "   & DllStructGetData($dRobotStatus, "dwSystemState")    & @CRLF & _
    ;              "System Error: "   & DllStructGetData($dRobotStatus, "dwSystemError")    & @CRLF & _
    ;              "CurrColorSpits: " & DllStructGetData($dRobotStatus, "dwCurrColorSpits") & @CRLF & _
    ;              "CurrBlackSpits: " & DllStructGetData($dRobotStatus, "dwCurrBlackSpits") & @CRLF & _
    ;              "FullColorSpits: " & DllStructGetData($dRobotStatus, "dwFullColorSpits") & @CRLF & _
    ;              "FullBlackSpits: " & DllStructGetData($dRobotStatus, "dwFullBlackSpits"));

    ; Set Global Variables
    $SystemState = DllStructGetData($dRobotStatus, "dwSystemState")
    $SystemError = DllStructGetData($dRobotStatus, "dwSystemError")

    Return $result[0]
EndFunc   ;==>PTRobot_GetRobotStatus

Func PTRobot_GetRobotInfo()
    Local $result
    Local $err = @error

    Local $dRobotInfo = DllStructCreate("hwnd hRobot;char tszRobotDesc[100];dword dwRobotType;dword dwNumDrives;dword dwNumPrinters;dword dwNumBins;dword dwDriveColumns;dword dwDriveRows;char tszRobotFirmware[20];dword dwOptions;dword dwAction;hwnd hDrives[10];dword dwDriveBusType")

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetRobotInfo", "hwnd", $RobotAddress, "ptr", DllStructGetPtr($dRobotInfo))

    ;MsgBox(0, "", "Status: "                  & $result[0]                                        & @CRLF & _
    ;              "Robot: "                   & DllStructGetData($dRobotInfo, "hRobot")           & @CRLF & _
    ;              "Robot Type: "              & DllStructGetData($dRobotInfo, "tszRobotDesc")     & @CRLF & _
    ;              "Number of Drives: "        & DllStructGetData($dRobotInfo, "dwNumDrives")      & @CRLF & _
    ;              "Number of Printers: "      & DllStructGetData($dRobotInfo, "dwNumPrinters")    & @CRLF & _
    ;              "Number of Bins: "          & DllStructGetData($dRobotInfo, "dwNumBins")        & @CRLF & _
    ;              "Number of Drive Columns: " & DllStructGetData($dRobotInfo, "dwDriveColumns")   & @CRLF & _
    ;              "Number of Drive Rows: "    & DllStructGetData($dRobotInfo, "dwDriveRows")      & @CRLF & _
    ;              "Robot FW Version: "        & DllStructGetData($dRobotInfo, "tszRobotFirmware") & @CRLF & _
    ;              "Robot Options: "           & DllStructGetData($dRobotInfo, "dwOptions")        & @CRLF & _
    ;              "Robot Actions: "           & DllStructGetData($dRobotInfo, "dwAction")         & @CRLF & _
    ;              "Drive Bus Type: "          & DllStructGetData($dRobotInfo, "dwDriveBusType"))

    $RobotType = DllStructGetData($dRobotInfo, "tszRobotDesc")
    $NumDrives = DllStructGetData($dRobotInfo, "dwNumDrives")
    $NumPrinters = DllStructGetData($dRobotInfo, "dwNumPrinters")
    $NumBins = DllStructGetData($dRobotInfo, "dwNumBins")

    Return $result[0]
EndFunc   ;==>PTRobot_GetRobotInfo

Func PTRobot_GetDriveInfo($num)
    Local $result
    Local $err = @error

    Local $dDrvInfo = DllStructCreate("hwnd hDrive;char tszDriveName[132];char tszFirmwareVer[40];char tszSerialNum[40];hwnd hRobot;dword dwDriveColumn;dword dwDriveRow")

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetDriveInfo", "hwnd", $DriveAddress[$num], "ptr", DllStructGetPtr($dDrvInfo))

    ;MsgBox(0, "", "Status: "           & $result[0] & @CRLF & _
    ;              "Drive: "            & DllStructGetData($dDrvInfo, "hDrive")         & @CRLF & _
    ;              "Drive Name: "       & DllStructGetData($dDrvInfo, "tszDriveName")   & @CRLF & _
    ;              "Firmware Version: " & DllStructGetData($dDrvInfo, "tszFirmwareVer") & @CRLF & _
    ;              "Serial Number: "    & DllStructGetData($dDrvInfo, "tszSerialNum")   & @CRLF & _
    ;              "Robot: "            & DllStructGetData($dDrvInfo, "hRobot")         & @CRLF & _
    ;              "Drive Column: "     & DllStructGetData($dDrvInfo, "dwDriveColumn")  & @CRLF & _
    ;              "Drive Row: "        & DllStructGetData($dDrvInfo, "dwDriveRow"))

    Return $result[0]
EndFunc   ;==>PTRobot_GetDriveInfo

Func PTRobot_LoadDrive($num, $dwFromLocation, $dwFirstDiscLoad)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_LoadDrive", "hwnd", $RobotAddress, "hwnd", $DriveAddress[$num], "dword", $dwFromLocation, "dword", $dwFirstDiscLoad)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_LoadDrive

Func PTRobot_LoadPrinter($dwFromLocation)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_LoadPrinter", "hwnd", $RobotAddress, "dword", $dwFromLocation)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_LoadPrinter

Func PTRobot_LoadPrinterFromDrive($num)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_LoadPrinterFromDrive", "hwnd", $RobotAddress, "hwnd", $DriveAddress[$num])

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_LoadPrinterFromDrive

Func PTRobot_PrintFile($strFilename)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_PrintFile", "hwnd", $RobotAddress, "str", $strFilename, "dword", 0)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit
    EndIf
EndFunc   ;==>PTRobot_PrintFile

Func PTRobot_UnLoadPrinter($dwToLocation)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_UnLoadPrinter", "hwnd", $RobotAddress, "dword", $dwToLocation)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_UnLoadPrinter

Func PTRobot_UnLoadDrive($num, $dwToLocation)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_UnLoadDrive", "hwnd", $RobotAddress, "hwnd", $DriveAddress[$num], "dword", $dwToLocation)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_UnLoadDrive

Func PTRobot_MoveDiscBetweenLocations($dwFromLocation, $dwToLocation)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_MoveDiscBetweenLocations", "hwnd", $RobotAddress, "dword", $dwFromLocation, "dword", $dwToLocation)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_MoveDiscBetweenLocations

Func PTRobot_OpenCloseDrive($num, $dwOpen)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_OpenCloseDrive", "hwnd", $DriveAddress[$num], "dword", $dwOpen)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_OpenCloseDrive

Func PTRobot_SystemAction($dwAction)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_SystemAction", "hwnd", $RobotAddress, "dword", $dwAction)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_SystemAction

Func PTRobot_KillSystemError($dwResetPrinter)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_KillSystemError", "hwnd", $RobotAddress, "dword", $dwResetPrinter)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_KillSystemError

Func PTRobot_GetManufactureInfo() ; WARNING: This function has not been thoroughly tested.
    Local $result
    Local $err = @error

    Local $dPTManufactureInfo = DllStructCreate("hwnd hRobot;char tszSerialNum[11];char tszManufactureDate[12];dword dwFiller[20]")

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetManufactureInfo", "hwnd", $RobotAddress, "ptr", DllStructGetPtr($dPTManufactureInfo))

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf

    MsgBox(0, "", "Serial Number: " & DllStructGetData($dPTManufactureInfo, "tszSerialNum") & @CRLF & _
            "Manufacture Date: " & DllStructGetData($dPTManufactureInfo, "tszManufactureDate") & @CRLF & _
            "Filler: " & DllStructGetData($dPTManufactureInfo, "dwFiller", 1))

    Return $result[0]
EndFunc   ;==>PTRobot_GetManufactureInfo

Func PTRobot_GetPrinterSettings()
    Local $result
    Local $err = @error

    Local $dPrinterSettings = DllStructCreate("dword dwPrintQuality;dword dwInnerDiam;dword dwOuterMargin")
    Local $PrintQuality

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetPrinterSettings", "hwnd", $RobotAddress, "ptr", DllStructGetPtr($dPrinterSettings))

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf

    Switch DllStructGetData($dPrinterSettings, "dwPrintQuality")
        Case $PQ_LOW
            $PrintQuality = "Low"
        Case $PQ_MED
            $PrintQuality = "Medium"
        Case $PQ_BETTER
            $PrintQuality = "Better" ; Default
        Case $PQ_HIGH
            $PrintQuality = "High"
        Case $PQ_BEST
            $PrintQuality = "Best"
        Case Else ; This shouldn't happen.
            $PrintQuality = "UNKNOWN"
    EndSwitch

    MsgBox(0, "", "Print Quality: " & $PrintQuality & @CRLF & _
            "Inner Diameter: " & DllStructGetData($dPrinterSettings, "dwInnerDiam") & @CRLF & _
            "Outer Margin: " & DllStructGetData($dPrinterSettings, "dwOuterMargin"))

EndFunc   ;==>PTRobot_GetPrinterSettings

Func PTRobot_ErrorMsgBox($ErrorNum)
    Local $ErrorMsg

    Switch $ErrorNum
        Case $SYSERR_PTR_TRAY
            $ErrorMsg = "Tray movement error.  Press the left button on the unit to try again."
        Case $SYSERR_CART_CODE
            $ErrorMsg = "There was a problem finding the ink cartridges.  Open the cover and press the left button. Make sure the color cartridge is installed on the left and the black is on the right.  Then close the cover."
        Case $SYSERR_INPUT_EMPTY
            $ErrorMsg = "The input bin is empty.  Open the cover and add more discs.  Then close the cover and push the left button on the unit."
        Case $SYSERR_PTR_COMM
            $ErrorMsg = "There was an internal printer communications error.  Press the left button on the unit to try again."
        Case $SYSERR_CLR_EMPTY
            $ErrorMsg = "WARNING:  The color cartridge is LOW on ink.  To replace the cartridge, open the cover on the unit and press the left button.  Then install the new cartridge and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_BLK_EMPTY
            $ErrorMsg = "WARNING:  The black cartridge is LOW on ink.  To replace the cartridge, open the cover on the unit and press the left button.  Then install the new cartridge and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_BOTH_EMPTY
            $ErrorMsg = "WARNING:  Both ink cartridges are LOW on ink.  To replace the cartridges, open the cover on the unit and press the left button.  Then install the new cartridges and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_PICK
            $ErrorMsg = "The disc was not picked.  Press the left button on the unit to try again."
        Case $SYSERR_ARM_MOVE
            $ErrorMsg = "There was an arm movement error.  Press the left button on the unit to try again."
        Case $SYSERR_CART_MOVE
            $ErrorMsg = "Arm picker error.  Press the left button on the unit to try again."
        Case $SYSERR_INTERNAL_SW
            $ErrorMsg = "There was an internal software error.  Please re-start the software."
        Case $SYSERR_NO_ROBODRIVES
            $ErrorMsg = "No external recorder drives were found.  Re-power the computer and unit, and then re-start the software."
        Case $SYSERR_OFFLINE
            $ErrorMsg = "The unit is offline.  Please ensure the unit is connected and powered on.  You may need to shut down and restart the software."
        Case $SYSERR_COVER_OPEN
            $ErrorMsg = "The unit’s cover is open.  Please close the cover."
        Case $SYSERR_PRINTER_PICK
            $ErrorMsg = "The disc was not picked from the printer.  Press the left button to retry."
        Case $SYSERR_MULTIPLE_PICK
            $ErrorMsg = "Multiple discs were picked up and moved.  Please manually remove any extra discs that were moved, keeping a single disc in place.  Then close the cover and press the left button."
        Case $SYSERR_MULTIPLEDISCS_IN_PRINTER
            $ErrorMsg = "Multiple discs were placed in the printer.  Please manually remove any extra discs from the printer, keeping a single disc in place.  Then close the cover and press the left button."
        Case $SYSERR_MULTIPLEDISCS_IN_RECORDER
            $ErrorMsg = "Multiple discs were placed in the recorder.  Please manually remove any extra discs from the recorder, keeping a single disc in place.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_RECORDER
            $ErrorMsg = "The disc was dropped while moving into the recorder.  Please manually place the disc into the recorder tray.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_BIN1
            $ErrorMsg = "The disc was dropped while moving into the right bin.  Please manually place the disc into the right bin.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_BIN2
            $ErrorMsg = "The disc was dropped while moving into the left bin.  Please manually place the disc into the left bin.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_PRINTER
            $ErrorMsg = "The disc was dropped while moving into the printer.  Please manually place the disc into the printer tray.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_REJECT
            $ErrorMsg = "The disc was dropped while moving to the reject area.  Please remove the dropped disc.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_UNKNOWN
            $ErrorMsg = "The disc was dropped. Please remove the dropped disc.  Then close the cover and press the left button."
        Case $SYSERR_ALIGNNEEDED
            $ErrorMsg = "The printer cartridges need to be aligned."
        Case $SYSERR_COLOR_INVALID
            $ErrorMsg = "The color cartridge is invalid.  Open the cover and press the left button.  Change the cartridge and close the cover."
        Case $SYSERR_BLACK_INVALID
            $ErrorMsg = "The black cartridge is invalid.  Open the cover and press the left button.  Change the cartridge and close the cover."
        Case $SYSERR_BOTH_INVALID
            $ErrorMsg = "Both cartridges are invalid.  Open the cover and press the left button.  Change the cartridges and close the cover."
        Case $SYSERR_NOCARTS
            $ErrorMsg = "No cartridges are installed.  Open the cover and press the left button.  Install the cartridges and close the cover."
        Case $SYSERR_K_IN_CMY
            $ErrorMsg = "The black cartridge is installed in the color position.  Open the cover and press the left button.  Change the cartridge and close the cover."
        Case $SYSERR_CMY_IN_K
            $ErrorMsg = "The color cartridge is installed in the black position.  Open the cover and press the left button.  Change the cartridge and close the cover."
        Case $SYSERR_SWAPPED
            $ErrorMsg = "The black and color cartridges are swapped.  Open the cover and press the left button.  Swap the cartridges and close the cover."
        Case $SYSERR_PIGONPRO
            $ErrorMsg = "This printer is not compatible with a pigment-based black cartridge.  Open the cover and press the left button.  Install a dye-based black cartridge and close the cover."
        Case $SYSERR_ALIGNFAILED
            $ErrorMsg = "The alignment print failed."
        Case $SYSERR_DROPPED_DISC_PRINTER_FATAL
            $ErrorMsg = "The disc was dropped while moving to/from the printer.  Please open the cover and manually remove and discard the disc.  Then place a new disc in the recorder, close the cover and press the left button."
        Case $SYSERR_MULTIPLEDISCS_IN_RIGHTBIN
            $ErrorMsg = "Multiple discs were placed in the right bin.  Please manually move any extra discs to the left bin, keeping a single disc in place.  Then close the cover and press the left button."
        Case $SYSERR_MULTIPLEDISCS_IN_LEFTBIN
            $ErrorMsg = "Multiple discs were placed in the left bin.  Please manually move any extra discs to the right bin, keeping a single disc in place.  Then close the cover and press the left button."
        Case $SYSERR_CLR_EMPTY_FINAL
            $ErrorMsg = "WARNING:  The color cartridge is Empty.  To replace the cartridge, open the cover on the unit and press the left button.  Then install the new cartridge and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_BLK_EMPTY_FINAL
            $ErrorMsg = "WARNING:  The black cartridge is Empty.  To replace the cartridge, open the cover on the unit and press the left button.  Then install the new cartridge and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_BOTH_EMPTY_FINAL
            $ErrorMsg = "WARNING:  Both cartridges are Empty.  To replace the cartridge, open the cover on the unit and press the left button.  Then install the new cartridge and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_WAITING_FOR_PRINTER
            $ErrorMsg = "The system timed out waiting for the printer to finish.  The disc may not have been printed on."
        Case $SYSERR_NO_DISC_IN_PRINTER
            $ErrorMsg = "No disc was found in the printer."
        Case $SYSERR_BUSY
            $ErrorMsg = "System Busy."
        Case $SYSERR_PURGE
            $ErrorMsg = "Purge."
        Case $SYSERR_DOCK_SENSOR
            $ErrorMsg = "Dock Sensor."
        Case $SYSERR_ALREADY_PRINTED
            $ErrorMsg = "Already printed."
        Case $SYSERR_UNKNOWN_HARDWARE
            $ErrorMsg = "Unknown hardware."
        Case $PTROBOT_FEATURE_NOT_IMPLEMENTED
            $ErrorMsg = "This feature is not implemented."
        Case $CUSTOM_INVALID_DRIVE_LETTER
            $ErrorMsg = "Could not find valid drive letter for burner."
        Case Else
            $ErrorMsg = "UNKNOWN ERROR! " & $ErrorNum
    EndSwitch

    WriteStatusError("Error: " & $ErrorMsg & @CRLF)
    MsgBox(0, "Error", $ErrorMsg)
EndFunc   ;==>PTRobot_ErrorMsgBox

Func _ValidateImageFile($sFilename)
    Local Const $sSTDHeader = "4D5600FF0C001200"
    Local Const $sPNGHeader = "89504E470D0A1A0A"
    Local Const $sJPGHeader = "FFD8FFE000104A46"
    Local Const $sGIF87Header = "474946383961"
    Local Const $sGIF89Header = "474946383761"
    Local Const $sBMPHeader = "424D"
    Local $sFileType, $bReturn = False

    Local $hFileHandle = FileOpen($sFilename, 16) ; Open file read only
    Local $bFileContents = FileRead($hFileHandle, 8) ; Read first 8 bytes
    Local $sFileExt = StringLower(StringRight($sFilename, 4)) ; Determine file extension

    If StringMid($bFileContents, 3, StringLen($sJPGHeader)) = $sJPGHeader And $sFileExt = ".jpg" Then
        $sFileType = "JPEG"
        $bReturn = True
    ElseIf StringMid($bFileContents, 3, StringLen($sSTDHeader)) = $sSTDHeader And $sFileExt = ".std" Then
        $sFileType = "SureThing Label"
        $bReturn = True
    ElseIf StringMid($bFileContents, 3, StringLen($sPNGHeader)) = $sPNGHeader And $sFileExt = ".png" Then
        $sFileType = "PNG"
        $bReturn = True
    ElseIf StringMid($bFileContents, 3, StringLen($sBMPHeader)) = $sBMPHeader And $sFileExt = ".bmp" Then
        $sFileType = "BMP"
        $bReturn = True
        ;ElseIf StringMid($bFileContents, 3, StringLen($sGIF87Header)) = $sGIF87Header And $sFileExt = ".gif" Then
        ;  $sFileType = "GIF87"
        ;  $bReturn = True
        ;ElseIf StringMid($bFileContents, 3, StringLen($sGIF89Header)) = $sGIF89Header And $sFileExt = ".gif" Then
        ;  $sFileType = "GIF89"
        ;  $bReturn = True
    Else
        $sFileType = "Unknown"
    EndIf

    ;MsgBox(0, "", $sFileType)
    Return $bReturn
EndFunc   ;==>_ValidateImageFile

Func _NoHaltMsgBox($code = 0, $Title = "", $text = "", $timeout = 0)
    Run(@AutoItExe & ' /AutoIt3ExecuteLine  "MsgBox(' & $code & ', ''' & $Title & ''', ''' & $text & ''',' & $timeout & ')"')
EndFunc   ;==>_NoHaltMsgBox

Func OpenPrompt($message, $Queue)

    Local $answer

    ; Get number of sets from the command-line
    $answer = getNumSets()

    ; If copies were specified on command line, then end function without prompting
    If $answer > 0 Then
        $NumSets = $answer
        Return $Queue
    EndIf

    Local $QueueSize = UBound($Queue, 1)

    Local $Form_Duplicator_GUI = GUICreate("IMGBurn AutoIt Script", 404, 260, 192, 124)
    Local $Button_Start = GUICtrlCreateButton("Start", 72, 210, 87, 33)
    Local $NumCopies = GUICtrlCreateInput($NumSets, 220, 170, 49, 21, $ES_CENTER)
    Local $Updown_NumCopies = GUICtrlCreateUpdown($NumCopies)
    GUICtrlSetLimit(-1, $MAX_NUMSETS, 1)
    Local $Button_Cancel = GUICtrlCreateButton("Cancel", 232, 210, 91, 33)
    Local $Label_NumCopies = GUICtrlCreateLabel("How many copies?", 116, 170, 94, 17, BitOR($SS_RIGHT, $SS_CENTERIMAGE))
    Local $Label_Message = GUICtrlCreateLabel("This script will run the disc burning software for the job" & @LF & $message & @LF & "Run?", 2, 8, 400, 57, BitOR($SS_NOPREFIX, $SS_CENTER))
    Local $List_Queue = GUICtrlCreateList("", 2, 60, 400, 100, BitOR($LBS_MULTIPLESEL, $WS_HSCROLL, $WS_VSCROLL, $WS_BORDER))
    GUICtrlSetLimit(-1, 200) ; Limit horizontal scrolling
    Local $DiscTitle
    For $i = 0 To $QueueSize - 1
        $DiscTitle = StringRegExp($Queue[$i][0], '\\([^\\]*)\.std$', 1)
        GUICtrlSetData(-1, $DiscTitle[0])
        ;_GUICtrlListBox_SetSel($List_Queue, $i) ; Preselect entry
        GUICtrlSendMsg(-1, $LB_SETSEL, True, $i) ; Preselect entry
    Next
    GUICtrlSendMsg(-1, $LB_SETTOPINDEX, 0, 0) ; Send scrollbar to top of list
    GUISetState(@SW_SHOW)

    Local $NewQueue

    While 1
        $answer = GUIGetMsg()
        $NewQueue = $Queue ; Set/Reset NewQueue to Queue

        Switch $answer
            Case $Button_Start
                If GUICtrlRead($NumCopies) < 1 Or GUICtrlRead($NumCopies) > $MAX_NUMSETS Then
                    MsgBox(0, "Error", "Number of copies must be between 1 and " & $MAX_NUMSETS & ".")
                Else
                    For $i = $QueueSize - 1 To 0 Step -1 ; Process array in reverse to keep index aligned
                        If _GUICtrlListBox_GetSel($List_Queue, $i) == False Then
                            _ArrayDelete($NewQueue, $i)
                        EndIf
                    Next

                    If UBound($NewQueue, 1) = 0 Then
                        MsgBox(0, "Error", "No discs selected.")
                    Else
                        ExitLoop ; Success!
                    EndIf
                EndIf
            Case $Button_Cancel
                MsgBox(0, "IMGBurn AutoIt Script", "OK.  Bye!")
                Exit
            Case $GUI_EVENT_CLOSE
                Exit
        EndSwitch
    WEnd

    $NumSets = GUICtrlRead($NumCopies)

    GUISetState(@SW_HIDE)

    Return $NewQueue
EndFunc   ;==>OpenPrompt

Func CalcDupeTime($iTimestampDiff)
    Local $iSeconds = Int($iTimestampDiff / 1000)
    Local $iHours, $iMins, $iSecs

    $iHours = Int($iSeconds / 3600)
    $iSeconds = Mod($iSeconds, 3600)
    $iMins = Int($iSeconds / 60)
    $iSecs = Mod($iSeconds, 60)

    If StringLen($iHours) = 1 Then $iHours = "0" & $iHours
    If StringLen($iMins) = 1 Then $iMins = "0" & $iMins
    If StringLen($iSecs) = 1 Then $iSecs = "0" & $iSecs

    Return $iHours & ":" & $iMins & ":" & $iSecs
EndFunc   ;==>CalcDupeTime

Func sendEmail($Title, $Queue, $NumSets, $DupeTime)
    Local $QueueSize = UBound($Queue, 1)

    Local $s_SmtpServer = "10.100.52.30"
    Local $s_FromName = "IMGBurn Disc Duplicator"
    Local $s_FromAddress = "ConfigurationManagementDistribution@aaccorp.com"
    Local $s_ToAddress = "ConfigurationManagementDistribution@aaccorp.com"
    Local $s_Subject = "[DUPLICATOR] - " & $Title & " - " & $NumSets & " set(s) in " & $DupeTime

    ; Old _INetSmtpMail code
    ;Local $as_Body[$QueueSize + 1]
    ;$as_Body[0] = "The job took " & $DupeTime & " to complete." & @CRLF

    Local $s_Body = "<body><p style=""font-family:Calibri, Arial, Helvetica, sans-serif;font-size:11pt;"">The job took " & $DupeTime & " to complete.</p><p></p>"
    $s_Body &= "<p style=""font-family:Calibri, Arial, Helvetica, sans-serif;font-size:11pt;"">"

    Local $DiscTitle

    ; Old _INetSmtpMail code
    ;For $i = 0 To $QueueSize - 1
    ;   $DiscTitle = StringRegExp($Queue[$i][0], '\\([^\\]*)\.std$', 1)
    ;   $as_Body[$i+1] = $DiscTitle[0] & @TAB ; We add a TAB at the end to stop Outlook from concatenating lines >= 40 chars
    ;Next

    For $i = 0 To $QueueSize - 1
        $DiscTitle = StringRegExp($Queue[$i][0], '\\([^\\]*)\.std$', 1)
        _HtmlEntities_Encode($DiscTitle[0])
        $s_Body &= $DiscTitle[0] & "<br>"
    Next

    $s_Body &= "</p></body>"

    ; Old _INetSmtpMail code
    ;Local $Response = _INetSmtpMail($s_SmtpServer, $s_FromName, $s_FromAddress, $s_ToAddress, $s_Subject, $as_Body, 1, -1)
    ;Local $err = @error
    ;
    ;If $Response = 1 Then
    ;   MsgBox(0, "Success!", "Mail sent")
    ;Else
    ;   MsgBox(0, "Error!", "Mail failed with error code " & $err)
    ;EndIf

    Local $oMyRet[2]
    Local $oMyError = ObjEvent("AutoIt.Error", "MyErrFunc")
    Local $Response = _INetSmtpMailCom($s_SmtpServer, $s_FromName, $s_FromAddress, $s_ToAddress, $s_Subject, $s_Body)
    If @error Then
        MsgBox(0, "Error sending message", "Error code:" & @error & "  Description:" & $Response)
    EndIf

EndFunc   ;==>sendEmail

Func toggleScreenSaver()
    Local $key = "HKEY_CURRENT_USER\Control Panel\Desktop"
    Local $value = "ScreenSaveActive"
    If Number(RegRead($key, $value)) Then
        RegWrite($key, $value, "REG_SZ", 0)
    Else
        RegWrite($key, $value, "REG_SZ", 1)
    EndIf
EndFunc   ;==>toggleScreenSaver

Func disableScreenSaver()
    Local $key = "HKEY_CURRENT_USER\Control Panel\Desktop"
    Local $value = "ScreenSaveActive"
    If Number(RegRead($key, $value)) Then
        RegWrite($key, $value, "REG_SZ", 0)
    EndIf
EndFunc   ;==>disableScreenSaver

Func enableScreenSaver()
    Local $key = "HKEY_CURRENT_USER\Control Panel\Desktop"
    Local $value = "ScreenSaveActive"
    If Number(RegRead($key, $value)) = 0 Then
        RegWrite($key, $value, "REG_SZ", 1)
    EndIf
EndFunc   ;==>enableScreenSaver

Func WriteStatus($LogMessage)
    ConsoleWrite($LogMessage)
    GUICtrlSetData($Label_Status, $LogMessage)
EndFunc   ;==>WriteStatus

Func WriteStatusError($LogMessage)
    ConsoleWriteError($LogMessage)
    GUICtrlSetData($Label_Status, $LogMessage)
EndFunc   ;==>WriteStatusError

Func _INetSmtpMailCom($s_SmtpServer, $s_FromName, $s_FromAddress, $s_ToAddress, $s_Subject = "", $as_Body = "", $s_AttachFiles = "", $s_CcAddress = "", $s_BccAddress = "", $s_Importance = "Normal", $s_Username = "", $s_Password = "", $IPPort = 25, $ssl = 0)
    Local $objEmail = ObjCreate("CDO.Message")
    $objEmail.From = '"' & $s_FromName & '" <' & $s_FromAddress & '>'
    $objEmail.To = $s_ToAddress
    Local $i_Error = 0
    Local $i_Error_desciption = ""
    If $s_CcAddress <> "" Then $objEmail.Cc = $s_CcAddress
    If $s_BccAddress <> "" Then $objEmail.Bcc = $s_BccAddress
    $objEmail.Subject = $s_Subject
    If StringInStr($as_Body, "<") And StringInStr($as_Body, ">") Then
        $objEmail.HTMLBody = $as_Body
    Else
        $objEmail.Textbody = $as_Body & @CRLF
    EndIf
    If $s_AttachFiles <> "" Then
        Local $S_Files2Attach = StringSplit($s_AttachFiles, ";")
        For $x = 1 To $S_Files2Attach[0]
            $S_Files2Attach[$x] = _PathFull($S_Files2Attach[$x])
;~          ConsoleWrite('@@ Debug : $S_Files2Attach[$x] = ' & $S_Files2Attach[$x] & @LF & '>Error code: ' & @error & @LF) ;### Debug Console
            If FileExists($S_Files2Attach[$x]) Then
                ConsoleWrite('+> File attachment added: ' & $S_Files2Attach[$x] & @LF)
                $objEmail.AddAttachment($S_Files2Attach[$x])
            Else
                ConsoleWrite('!> File not found to attach: ' & $S_Files2Attach[$x] & @LF)
                SetError(1)
                Return 0
            EndIf
        Next
    EndIf
    $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
    $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = $s_SmtpServer
    If Number($IPPort) = 0 Then $IPPort = 25
    $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = $IPPort
    ;Authenticated SMTP
    If $s_Username <> "" Then
        $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1
        $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = $s_Username
        $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = $s_Password
    EndIf
    If $ssl Then
        $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = True
    EndIf
    ;Update settings
    $objEmail.Configuration.Fields.Update
    ; Set Email Importance
    Switch $s_Importance
        Case "High"
            $objEmail.Fields.Item("urn:schemas:mailheader:Importance") = "High"
        Case "Normal"
            $objEmail.Fields.Item("urn:schemas:mailheader:Importance") = "Normal"
        Case "Low"
            $objEmail.Fields.Item("urn:schemas:mailheader:Importance") = "Low"
    EndSwitch
    $objEmail.Fields.Update
    ; Sent the Message
    $objEmail.Send
    If @error Then
        SetError(2)
        Return $oMyRet[1]
    EndIf
    $objEmail = ""
EndFunc   ;==>_INetSmtpMailCom
;
;
; Com Error Handler
Func MyErrFunc()
    Local $oMyError = ObjEvent("AutoIt.Error", "MyErrFunc")
    $HexNumber = Hex($oMyError.number, 8)
    $oMyRet[0] = $HexNumber
    $oMyRet[1] = StringStripWS($oMyError.description, 3)
    ConsoleWrite("### COM Error !  Number: " & $HexNumber & "   ScriptLine: " & $oMyError.scriptline & "   Description:" & $oMyRet[1] & @LF)
    SetError(1); something to check for when this function returns
    Return
EndFunc   ;==>MyErrFunc

Func _HtmlEntities_Encode(ByRef $sTxt)
    For $i = 0 To 245
        $sTxt = StringReplace($sTxt, ChrW($aisEntities[$i][0]), '&' & $aisEntities[$i][1] & ';', 0, 1)
    Next
EndFunc   ;==>_HtmlEntities_Encode

Func _HtmlEntities_Decode(ByRef $sTxt)
    For $i = 0 To 245
        $sTxt = StringReplace($sTxt, '&' & $aisEntities[$i][1] & ';', ChrW($aisEntities[$i][0]), 0, 1)
    Next
EndFunc   ;==>_HtmlEntities_Decode

Lots of small improvements. It now autodetects the burner's drive letter. Added a queue in an array: first column is the labels and second column is the disc image files (e.g., .ISO). It's much more interactive when problems occur. Leaving the label file blank in the queue array will skip the printing process. More error checking done. If your duplicator doesn't have a printer, it will never attempt to print even if you specify labels. Added some more functions.

EDIT: Added console messages and error output.

DOUBLE EDIT: Eject disc on abort before asking if you want to close the program.

TRIPLE EDIT: Added validation of label files since the DLLs will crash if an invalid image file is passed. It's a very rudimentary check.

Edited by GPinzone
Gerard J. Pinzonegpinzone AT yahoo.com
Link to comment
Share on other sites

  • 4 weeks later...

I am very interested in this code. I have never referenced a dll file. Is there any special way that the dll files must be registered? do the dll files need to be in the same directory as the autoit file? I have tried to use the code in this string and am getting an array errors. I suspect the dll call is not getting done.

Thanks for the hard work. I look forward to coding for my Primera Bravo II.

Link to comment
Share on other sites

  • 1 year later...

I updated the above code once more with some minor tweaks:

  • Fixed speed of IMGBurn to 6X since the throughput of the USB connection isn't going to allow much more than that for writing anyway. Feel free to try other speeds (or none at all).
  • Open drive tray on error.
  • Close drive tray on retry (after error).
EDIT:
  • Set speed to AUTO which invokes AWS. Check out the IMGBurn form for more info on AWS.
  • Settings chosen by default in IMG burn could accidentally be set during commad-line run. I added switches to trun off test writing, deleting image, and ejecting the disc.
Edited by GPinzone
Gerard J. Pinzonegpinzone AT yahoo.com
Link to comment
Share on other sites

  • 2 years later...
  • 3 years later...

Final update. I've move on to using PTBurn instead of rolling my own using PTRobot.

#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GuiListBox.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#include <ListboxConstants.au3>

#include <Array.au3>
#include <INet.au3>
#include <Misc.au3>
#include <File.au3>

If _Singleton("Primera", 1) = 0 Then
    ; We know the script is already running. Let the user know.
    MsgBox($MB_OK + $MB_ICONERROR, "Error: Multiple Copies", "This script is already running. Using multiple copies of this script at the same time is unsupported!")
    Exit
EndIf

Opt('MustDeclareVars', 1)

Global Const $aisEntities[246][2] = [[38, 'amp'], [34, 'quot'], [39, '#39'], [60, 'lt'], [62, 'gt'], [160, 'nbsp'], [161, 'iexcl'], [162, 'cent'], [163, 'pound'], [164, 'curren'], [165, 'yen'], [166, 'brvbar'], [167, 'sect'], [168, 'uml'], [169, 'copy'], [170, 'ordf'], [171, 'laquo'], [172, 'not'], [173, 'shy'], [174, 'reg'], [175, 'macr'], [176, 'deg'], [177, 'plusmn'], [180, 'acute'], [181, 'micro'], [182, 'para'], [183, 'middot'], [184, 'cedil'], [186, 'ordm'], [187, 'raquo'], [191, 'iquest'], [192, 'Agrave'], [193, 'Aacute'], [194, 'Acirc'], [195, 'Atilde'], [196, 'Auml'], [197, 'Aring'], [198, 'AElig'], [199, 'Ccedil'], [200, 'Egrave'], [201, 'Eacute'], [202, 'Ecirc'], [203, 'Euml'], [204, 'Igrave'], [205, 'Iacute'], [206, 'Icirc'], [207, 'Iuml'], [208, 'ETH'], [209, 'Ntilde'], [210, 'Ograve'], [211, 'Oacute'], [212, 'Ocirc'], [213, 'Otilde'], [214, 'Ouml'], [215, 'times'], [216, 'Oslash'], [217, 'Ugrave'], [218, 'Uacute'], [219, 'Ucirc'], [220, 'Uuml'], [221, 'Yacute'], [222, 'THORN'], [223, 'szlig'], [224, 'agrave'], [225, 'aacute'], [226, 'acirc'], [227, 'atilde'], [228, 'auml'], [229, 'aring'], [230, 'aelig'], [231, 'ccedil'], [232, 'egrave'], [233, 'eacute'], [234, 'ecirc'], [235, 'euml'], [236, 'igrave'], [237, 'iacute'], [238, 'icirc'], [239, 'iuml'], [240, 'eth'], [241, 'ntilde'], [242, 'ograve'], [243, 'oacute'], [244, 'ocirc'], [245, 'otilde'], [246, 'ouml'], [247, 'divide'], [248, 'oslash'], [249, 'ugrave'], [250, 'uacute'], [251, 'ucirc'], [252, 'uuml'], [253, 'yacute'], [254, 'thorn'], [255, 'yuml'], [338, 'OElig'], [339, 'oelig'], [352, 'Scaron'], [353, 'scaron'], [376, 'Yuml'], [402, 'fnof'], [710, 'circ'], [732, 'tilde'], [913, 'Alpha'], [914, 'Beta'], [915, 'Gamma'], [916, 'Delta'], [917, 'Epsilon'], [918, 'Zeta'], [919, 'Eta'], [920, 'Theta'], [921, 'Iota'], [922, 'Kappa'], [923, 'Lambda'], [924, 'Mu'], [925, 'Nu'], [926, 'Xi'], [927, 'Omicron'], [928, 'Pi'], [929, 'Rho'], [931, 'Sigma'], [932, 'Tau'], [933, 'Upsilon'], [934, 'Phi'], [935, 'Chi'], [936, 'Psi'], [937, 'Omega'], [945, 'alpha'], [946, 'beta'], [947, 'gamma'], [948, 'delta'], [949, 'epsilon'], [950, 'zeta'], [951, 'eta'], [952, 'theta'], [953, 'iota'], [954, 'kappa'], [955, 'lambda'], [956, 'mu'], [957, 'nu'], [958, 'xi'], [959, 'omicron'], [960, 'pi'], [961, 'rho'], [962, 'sigmaf'], [963, 'sigma'], [964, 'tau'], [965, 'upsilon'], [966, 'phi'], [967, 'chi'], [968, 'psi'], [969, 'omega'], [977, 'thetasym'], [978, 'upsih'], [982, 'piv'], [8194, 'ensp'], [8195, 'emsp'], [8201, 'thinsp'], [8204, 'zwnj'], [8205, 'zwj'], [8206, 'lrm'], [8207, 'rlm'], [8211, 'ndash'], [8212, 'mdash'], [8216, 'lsquo'], [8217, 'rsquo'], [8218, 'sbquo'], [8220, 'ldquo'], [8221, 'rdquo'], [8222, 'bdquo'], [8224, 'dagger'], [8225, 'Dagger'], [8226, 'bull'], [8230, 'hellip'], [8240, 'permil'], [8242, 'prime'], [8243, 'Prime'], [8249, 'lsaquo'], [8250, 'rsaquo'], [8254, 'oline'], [8260, 'frasl'], [8364, 'euro'], [8465, 'image'], [8472, 'weierp'], [8476, 'real'], [8482, 'trade'], [8501, 'alefsym'], [8592, 'larr'], [8593, 'uarr'], [8594, 'rarr'], [8595, 'darr'], [8596, 'harr'], [8629, 'crarr'], [8656, 'lArr'], [8657, 'uArr'], [8658, 'rArr'], [8659, 'dArr'], [8660, 'hArr'], [8704, 'forall'], [8706, 'part'], [8707, 'exist'], [8709, 'empty'], [8711, 'nabla'], [8712, 'isin'], [8713, 'notin'], [8715, 'ni'], [8719, 'prod'], [8721, 'sum'], [8722, 'minus'], [8727, 'lowast'], [8730, 'radic'], [8733, 'prop'], [8734, 'infin'], [8736, 'ang'], [8743, 'and'], [8744, 'or'], [8745, 'cap'], [8746, 'cup'], [8747, 'int'], [8764, 'sim'], [8773, 'cong'], [8776, 'asymp'], [8800, 'ne'], [8801, 'equiv'], [8804, 'le'], [8805, 'ge'], [8834, 'sub'], [8835, 'sup'], [8836, 'nsub'], [8838, 'sube'], [8839, 'supe'], [8853, 'oplus'], [8855, 'otimes'], [8869, 'perp'], [8901, 'sdot'], [8968, 'lceil'], [8969, 'rceil'], [8970, 'lfloor'], [8971, 'rfloor'], [9001, 'lang'], [9002, 'rang'], [9674, 'loz'], [9824, 'spades'], [9827, 'clubs'], [9829, 'hearts'], [9830, 'diams']]

Global $hPTROBOTDLL = DllOpen("U:\Macros\DLLs\PTRobot.dll")
Global $MacroLibPath = "U:\Macros\"
Global $LastUsedDriveNumFile = $MacroLibPath & "DriveNum.txt"
Global $RobotType
Global $RobotAddress
Global $NumRobots
Global $DriveAddress[10]
Global $NumDrives
Global $NumPrinters
Global $NumBins
Global $SystemState
Global $SystemError
Global $NumDiscsProcessed = 0
Global $DriveLetter[10]
Global $NumSets = 1
Global $NoDocDisc = False
Global $LastUsedDriveNum = 0

Global $Form_Status = GUICreate("IMGBurn AutoIt Script", 614, 163, 193, 124, BitOR($GUI_SS_DEFAULT_GUI, $WS_SIZEBOX, $WS_THICKFRAME))
Global $Label_Status = GUICtrlCreateLabel("", 16, 104, 580, 50, BitOR($SS_NOPREFIX, $SS_SUNKEN, $WS_BORDER))
Global $Label_Status_Title = GUICtrlCreateLabel("Status:", 16, 80, 37, 17, $SS_NOPREFIX)
Global $Label_NumSets_Title = GUICtrlCreateLabel("Total Number of Sets:", 424, 8, 107, 17, $SS_NOPREFIX)
Global $Label_NumSets = GUICtrlCreateLabel($NumSets, 568, 8, 26, 17, $SS_NOPREFIX)
Global $Label_Set_Title = GUICtrlCreateLabel("Current Set:", 424, 32, 60, 17, $SS_NOPREFIX)
Global $Label_Set = GUICtrlCreateLabel("1", 568, 32, 26, 17, $SS_NOPREFIX)
Global $Label_Title = GUICtrlCreateLabel("", 16, 24, 392, 17, $SS_NOPREFIX)
Global $Label_Job_Title = GUICtrlCreateLabel("Job:", 16, 8, 24, 17)
Global $Label_DiscsProcessed_Title = GUICtrlCreateLabel("Number of discs processed:", 424, 80, 135, 17, $SS_NOPREFIX)
Global $Label_DiscsProcessed = GUICtrlCreateLabel($NumDiscsProcessed, 568, 80, 26, 17)
Global $Label_Disc_Title = GUICtrlCreateLabel("Disc:", 424, 56, 28, 17, $SS_NOPREFIX)
Global $Label_Disc = GUICtrlCreateLabel("1", 456, 56, 28, 17, BitOR($SS_CENTER, $SS_NOPREFIX))
Global $Label_NumDiscss_Title = GUICtrlCreateLabel("of", 488, 56, 21, 17, BitOR($SS_CENTER, $SS_NOPREFIX))
Global $Label_NumDiscs = GUICtrlCreateLabel("", 512, 56, 39, 17, BitOR($SS_CENTER, $SS_NOPREFIX))

Global Const $MAX_NUMSETS = 50

Global Const $LOCATION_AUTO = 0
Global Const $LOCATION_RIGHT = 1
Global Const $LOCATION_LEFT = 2
Global Const $LOCATION_PRINTER = 100
Global Const $LOCATION_REJECT = 200

Global Const $CLEARDRIVE_NO = 0
Global Const $CLEARDRIVE_YES = 1

Global Const $DRIVE_OPEN = 0
Global Const $DRIVE_CLOSE = 1

; SET OUTPUT BINS HERE
Global Const $DVD_DL_BIN_INPUT = $LOCATION_LEFT
Global Const $DVD_BIN_INPUT = $LOCATION_RIGHT
Global Const $BIN_OUTPUT = $LOCATION_REJECT

Global Const $PQ_LOW = 0
Global Const $PQ_MED = 1
Global Const $PQ_BETTER = 2
Global Const $PQ_HIGH = 3
Global Const $PQ_BEST = 4

Global Const $PTACT_ALIGNPRINTER = 0x00000001
Global Const $PTACT_IGNOREINKLOW = 0x00000002
Global Const $PTACT_DISABLEPWRBUTTON = 0x00000004
Global Const $PTACT_REINIT_DRIVES = 0x00000008
Global Const $PTACT_IDENTIFY = 0x00000010
Global Const $PTACT_CANCELCMD = 0x00000020
Global Const $PTACT_ENABLEPWRBUTTON = 0x00000040
Global Const $PTACT_RESETSYSTEM = 0x00000080
Global Const $PTACT_CHECKDISCS = 0x00000100 ; Check number of discs in bins
Global Const $PTACT_CLEANCARTRIDGES = 0x00000200 ; Clean the cartridges
Global Const $PTACT_CALIBRATE_ONE_DISC = 0x00000400 ; SE, II, Pro: Calibrate for one disc (user must put one disc in each bin).
Global Const $PTACT_CHANGE_CARTRIDGE = 0x00000800 ; SE, II, Pro: Start the cartridge change procedure
Global Const $PTACT_END_CARTRIDGE_CHANGE = 0x00001000 ; SE: End the cartridge change (can close lid also)
Global Const $PTACT_SHIP_POSITION = 0x00002000 ; SE, II, Pro: Move the picker to the shipping position
Global Const $PTACT_RESET_LEFT_INK_LEVELS = 0x00004000 ; II: Clears the ink spits for the LEFT cartridge
Global Const $PTACT_RESET_RIGHT_INK_LEVELS = 0x00008000 ; II: Clears the ink spits for the RIGHT cartridge
Global Const $PTACT_ALLOW_NO_CARTRIDGES = 0x00010000 ; SE, II, Pro: Allows unit to operate non-printing robotics without a cartridge
Global Const $PTACT_XI_LIGHT_OFF = 0x00020000
Global Const $PTACT_XI_LIGHT_ON = 0x00040000
Global Const $PTACT_XI_LIGHT_FLASH = 0x00080000
Global Const $PTACT_UNHOOK_PICKER = 0x00100000
Global Const $PTACT_AUTOPRINTER_MODE = 0x00200000 ; DP4100: can perform a faster multiple copy print-only job by calling PTRobot_SetPrintCopies() prior to calling the print function (e.g. PTRobot_PrintFile()).
Global Const $PTACT_FAN_ON = 0x00400000 ; DP4100: turn on system fan
Global Const $PTACT_FAN_OFF = 0x00800000 ; DP4100: turn off system fan

Global Const $SYSERR_PTR_TRAY = 1
Global Const $SYSERR_CART_CODE = 2
Global Const $SYSERR_INPUT_EMPTY = 3
Global Const $SYSERR_PTR_COMM = 4
Global Const $SYSERR_CLR_EMPTY = 5
Global Const $SYSERR_BLK_EMPTY = 6
Global Const $SYSERR_BOTH_EMPTY = 7
Global Const $SYSERR_PICK = 8
Global Const $SYSERR_ARM_MOVE = 9
Global Const $SYSERR_CART_MOVE = 10
Global Const $SYSERR_INTERNAL_SW = 12
Global Const $SYSERR_NO_ROBODRIVES = 13
Global Const $SYSERR_OFFLINE = 14
Global Const $SYSERR_COVER_OPEN = 15
Global Const $SYSERR_PRINTER_PICK = 16
Global Const $SYSERR_MULTIPLE_PICK = 17
Global Const $SYSERR_MULTIPLEDISCS_IN_PRINTER = 18
Global Const $SYSERR_MULTIPLEDISCS_IN_RECORDER = 19
Global Const $SYSERR_DROPPED_DISC_RECORDER = 20
Global Const $SYSERR_DROPPED_DISC_BIN1 = 28
Global Const $SYSERR_DROPPED_DISC_BIN2 = 29
Global Const $SYSERR_DROPPED_DISC_PRINTER = 33
Global Const $SYSERR_DROPPED_DISC_REJECT = 34
Global Const $SYSERR_DROPPED_DISC_UNKNOWN = 35
Global Const $SYSERR_ALIGNNEEDED = 36
Global Const $SYSERR_COLOR_INVALID = 37
Global Const $SYSERR_BLACK_INVALID = 38
Global Const $SYSERR_BOTH_INVALID = 39
Global Const $SYSERR_NOCARTS = 40
Global Const $SYSERR_K_IN_CMY = 41
Global Const $SYSERR_CMY_IN_K = 42
Global Const $SYSERR_SWAPPED = 43
Global Const $SYSERR_PIGONPRO = 44
Global Const $SYSERR_ALIGNFAILED = 45
Global Const $SYSERR_DROPPED_DISC_PRINTER_FATAL = 46
Global Const $SYSERR_MULTIPLEDISCS_IN_RIGHTBIN = 47
Global Const $SYSERR_MULTIPLEDISCS_IN_LEFTBIN = 48
Global Const $SYSERR_CLR_EMPTY_FINAL = 49
Global Const $SYSERR_BLK_EMPTY_FINAL = 50
Global Const $SYSERR_BOTH_EMPTY_FINAL = 51
Global Const $SYSERR_WAITING_FOR_PRINTER = 52
Global Const $SYSERR_NO_DISC_IN_PRINTER = 53
Global Const $SYSERR_BUSY = 54
Global Const $SYSERR_PURGE = 55
Global Const $SYSERR_DOCK_SENSOR = 56
Global Const $SYSERR_ALREADY_PRINTED = 57
Global Const $SYSERR_UNKNOWN_HARDWARE = 58

Global Const $SYSSTATE_IDLE = 0
Global Const $SYSSTATE_BUSY = 1
Global Const $SYSSTATE_ERROR = 2

Global Const $ROBOT_DISCPUBLISHER = 0 ; Disc Publisher I
Global Const $ROBOT_DISCPUBLISHERII = 1 ; Disc Publisher II
Global Const $ROBOT_DISCPUBLISHERPRO = 2 ; Disc Publisher PRO
Global Const $ROBOT_COMPOSERMAX = 3 ; ComposerMAX
Global Const $ROBOT_RACKMOUNT_DPII = 4 ; Disc Publisher XR
Global Const $ROBOT_DISCPUBLISHER_XRP = 5 ; Disc Publisher XRP
Global Const $ROBOT_DISCPUBLISHER_SE = 6 ; Disc Publisher SE
Global Const $ROBOT_DISCPUBLISHERPRO_XI = 7 ; Disc Publisher Xi Series
Global Const $ROBOT_DISCPUBLISHER_4100 = 8 ; Disc Publisher 4100 Series

Global Const $PTROBOT_FEATURE_NOT_IMPLEMENTED = 520

Global Const $CUSTOM_FILE_NOT_FOUND = 901
Global Const $CUSTOM_FILE_FORMAT_WRONG = 902
Global Const $CUSTOM_INVALID_DRIVE_LETTER = 903

Func processJobs($Title, $Queue)

    Local $TimeStamp, $DupeTime, $DiscSet
    Local $QueueSize = UBound($Queue, 1)

    ; Store the time the process begins
    $TimeStamp = TimerInit()

    ; Exit script if ImgBurn is already running
    If ProcessExists("ImgBurn.exe") Then
        MsgBox($MB_OK + $MB_ICONWARNING, "IMGBurn AutoIt Script", "ImgBurn is already running.")
        Exit
    EndIf

    ; Configure GUI
    GUICtrlSetData($Label_Title, $Title)
    GUICtrlSetData($Label_NumSets, $NumSets)
    GUICtrlSetData($Label_NumDiscs, $QueueSize)

    GUISetState(@SW_SHOW, $Form_Status)

    ; Disable Screen Saver
    disableScreenSaver()

    PTRobot_Initialize()
    PTRobot_EnumRobots()
    PTRobot_EnumDrives()
    PTRobot_GetRobotInfo()

    ; Get the last drive used
    GetLastUsedDriveNum()

    ; Repeat process for each set of discs
    For $DiscSet = 1 To $NumSets

        ; Update GUI
        GUICtrlSetData($Label_Set, $DiscSet)
        ; Process the Queue
        PTRobot_ProcessQueue($Queue)

    Next

    PTRobot_Destroy()

    ; Store the last drive used
    StoreDriveNum()

    ; Enable Screen Saver
    enableScreenSaver()

    ; Calculate how long the process took
    $DupeTime = CalcDupeTime(TimerDiff($TimeStamp))

    sendEmail($Title, $Queue, $NumSets, $DupeTime)

EndFunc   ;==>processJobs

Func getNumSets()

    Local $nNumSets

    ; Check command line
    If $CmdLine[0] > 0 Then
        If $CmdLine[1] < 1 Or $CmdLine[1] > $MAX_NUMSETS Then
            MsgBox($MB_OK + $MB_ICONERROR, "IMGBurn AutoIt Script", "Number of jobs must be between 1 and " & $MAX_NUMSETS & ".")
            Exit
        Else
            $nNumSets = $CmdLine[1]
        EndIf
    Else
        ; Return -1 if no command line argument given
        $nNumSets = -1
    EndIf

    Return $nNumSets

EndFunc   ;==>getNumSets

Func PTRobot_ProcessQueue($Queue)
    Local $QueueSize = UBound($Queue, 1)
    Local $Disc, $SourceBin
    Local $MissingFiles, $WrongFileFormat
    Local $result
    Local Const $LABEL_COL = 0 ; Array column used for disc labels.
    Local Const $IMAGE_COL = 1 ; Array column used for disc image files.
    Local $DriveNumber

    ; Verify files exist.
    For $Disc = 0 To $QueueSize - 1
        If Not $Queue[$Disc][$LABEL_COL] = "" Then
            If Not FileExists($Queue[$Disc][$LABEL_COL]) Then
                $MissingFiles = $MissingFiles & $Queue[$Disc][$LABEL_COL] & @CRLF
            ElseIf Not _ValidateImageFile($Queue[$Disc][$LABEL_COL]) Then
                $WrongFileFormat = $WrongFileFormat & $Queue[$Disc][$LABEL_COL] & @CRLF
            EndIf
        EndIf
        If Not FileExists($Queue[$Disc][$IMAGE_COL]) Then
            $MissingFiles = $MissingFiles & $Queue[$Disc][$IMAGE_COL] & @CRLF
        EndIf
    Next

    If Not $MissingFiles = "" Then
        MsgBox($MB_OK + $MB_ICONERROR, "Queue Error", "Files not found:" & @CRLF & $MissingFiles)
        PTRobot_Destroy()
        Exit ($CUSTOM_FILE_NOT_FOUND)
    EndIf

    If Not $WrongFileFormat = "" Then
        MsgBox($MB_OK + $MB_ICONERROR, "Queue Error", "File format incorrect:" & @CRLF & $WrongFileFormat)
        PTRobot_Destroy()
        Exit ($CUSTOM_FILE_FORMAT_WRONG)
    EndIf

    PTRobot_Wait()

    For $Disc = 0 To $QueueSize - 1

        If StringInStr($Queue[$Disc][$LABEL_COL], "DVD DL", 1) Or StringInStr($Queue[$Disc][$IMAGE_COL], "DVD DL", 1) Then
            $SourceBin = $DVD_DL_BIN_INPUT
        Else
            $SourceBin = $DVD_BIN_INPUT
        EndIf

        GUICtrlSetData($Label_Disc, $Disc + 1) ; Update GUI

        $DriveNumber = Mod($NumDiscsProcessed + $LastUsedDriveNum, $NumDrives) ; Switch drives after each iteration
        ;TEMP PATCH BECAUSE OF TOP DRIVE PROBLEM
        ;$DriveNumber = 1

        ; Update last used drive number
        $LastUsedDriveNum = $DriveNumber

        WriteStatus("Loading drive..." & @CRLF)
        If $NumDiscsProcessed < $NumDrives Then ; First disc load in drive should check for disc in burner.
            PTRobot_LoadDrive($DriveNumber, $SourceBin, $CLEARDRIVE_YES) ; Load first disc from source bin.
        Else
            PTRobot_LoadDrive($DriveNumber, $SourceBin, $CLEARDRIVE_NO) ; Load subsequent disc from source bin.
        EndIf

        WriteStatus("Starting disc writer..." & @CRLF)

        Do
            PTRobot_Wait()

            $result = PTRobot_BurnImage($DriveNumber, $Queue[$Disc][$IMAGE_COL]) ; Burn image file to disc.

            ; If Retry, unload disc, load a new one and try again.
            If $result = 2 Then
                WriteStatus("Sending disc to reject bin..." & @CRLF)
                PTRobot_Wait()
                PTRobot_UnLoadDrive($DriveNumber, $LOCATION_REJECT) ; Send to reject pile.
                PTRobot_Wait()
                WriteStatus("Replacing blank disc..." & @CRLF)
                PTRobot_LoadDrive($DriveNumber, $SourceBin, $CLEARDRIVE_NO) ; Replace disc from source bin.
                PTRobot_Wait()
                PTRobot_OpenCloseDrive($DriveNumber, $DRIVE_CLOSE)
                WriteStatus("Retrying " & $Queue[$Disc][$IMAGE_COL] & "..." & @CRLF)
            EndIf
        Until $result <> 2

        ; If Abort, unload disc and exit program.
        If $result = 1 Then
            WriteStatus("Sending disc to reject bin..." & @CRLF)
            PTRobot_Wait()
            PTRobot_UnLoadDrive($DriveNumber, $LOCATION_REJECT) ; Send to reject pile.
            PTRobot_Wait()
            PTRobot_Destroy()
            Exit ($result)
        ; If Ignore, unload disc and move to the next one.
        ElseIf $result = 3 Then
            WriteStatus("Sending disc to reject bin..." & @CRLF)
            PTRobot_Wait()
            PTRobot_UnLoadDrive($DriveNumber, $LOCATION_REJECT) ; Send to reject pile.
        ; Success ($result = 0)
        Else
            WriteStatus("File: " & $Queue[$Disc][$IMAGE_COL] & " successfully written." & @CRLF)

            PTRobot_Wait()

            If $Queue[$Disc][$LABEL_COL] = "" Or $NumPrinters < 1 Then
                WriteStatus("Send to output bin..." & @CRLF)
                PTRobot_UnLoadDrive($DriveNumber, $BIN_OUTPUT) ; Unload disc to output bin.
                PTRobot_Wait()
            Else
                WriteStatus("Loading printer..." & @CRLF)
                PTRobot_LoadPrinterFromDrive($DriveNumber) ; Send disc to printer.

                PTRobot_Wait()

                WriteStatus("Printing label..." & @CRLF)
                PTRobot_PrintFile($Queue[$Disc][$LABEL_COL]) ; Print label.

                PTRobot_Wait()

                WriteStatus("Send to output bin..." & @CRLF)
                PTRobot_UnLoadPrinter($BIN_OUTPUT) ; Unload disc to output bin.
            EndIf
        EndIf

        $NumDiscsProcessed = $NumDiscsProcessed + 1 ; Increment global counter.
        GUICtrlSetData($Label_DiscsProcessed, $NumDiscsProcessed)

        PTRobot_Wait()
    Next
EndFunc   ;==>PTRobot_ProcessQueue

Func PTRobot_BurnImage($num, $Filename)
    Local $ErrorLevel
    Local $result = 0

    $ErrorLevel = RunWait('"C:\Program Files (x86)\ImgBurn\ImgBurn.exe" /MODE WRITE /SRC "' & $Filename & '" /DEST ' & $DriveLetter[$num] & ': /COPIES 1 /VERIFY NO /SPEED AUTO /TESTMODE NO /DELETEIMAGE NO /EJECT YES /WAITFORMEDIA /START /CLOSE /NOSAVESETTINGS')

    If $ErrorLevel Then
        WriteStatusError("Error: Disc write failed! - Exit Code: " & $ErrorLevel & @CRLF)
        WriteStatusError("Error: " & $Filename & " not successfully written!" & @CRLF)
        $result = MsgBox($MB_ABORTRETRYIGNORE + $MB_ICONERROR, "Burn Error", "Disc write failed!" & @CRLF & "Exit Code: " & $ErrorLevel)
        ; Abort
        If $result = $IDABORT Then
            $result = 1 ; Return value for aborted error.
            ; Retry
        ElseIf $result = $IDRETRY Then
            $result = 2 ; Return value for retry error.
            ; Ignore
        ElseIf $result = $IDIGNORE Then
            $result = 3 ; Return value for ignored error.
        EndIf
        ; Success
    EndIf

    PTRobot_OpenCloseDrive($num, $DRIVE_OPEN)

    Return $result
EndFunc   ;==>PTRobot_BurnImage

Func PTRobot_Wait()
    Local $ErrorMsg

    Sleep(1000)
    While Not PTRobot_GetRobotStatus()
        If $SystemError Then
            PTRobot_ErrorMsgBox($SystemError)
        ElseIf Not $SystemState Then
            ExitLoop
        Else
            Sleep(1000)
        EndIf
    WEnd

    Sleep(1000)
    While Not PTRobot_GetRobotStatus()
        If $SystemError Then
            If $SystemError = $SYSERR_CLR_EMPTY Then
                $ErrorMsg = "WARNING:  The color cartridge is LOW on ink."
                WriteStatusError("Error: " & $ErrorMsg & @CRLF)
                PTRobot_KillSystemError(0)
                Sleep(1000)
            ElseIf $SystemError = $SYSERR_BLK_EMPTY Then
                $ErrorMsg = "WARNING:  The black cartridge is LOW on ink."
                WriteStatusError("Error: " & $ErrorMsg & @CRLF)
                PTRobot_KillSystemError(0)
                Sleep(1000)
            ElseIf $SystemError = $SYSERR_BOTH_EMPTY Then
                $ErrorMsg = "WARNING:  Both ink cartridges are LOW on ink."
                WriteStatusError("Error: " & $ErrorMsg & @CRLF)
                PTRobot_KillSystemError(0)
                Sleep(1000)
            Else
                PTRobot_ErrorMsgBox($SystemError)
            EndIf
        ElseIf Not $SystemState Then
            ExitLoop
        Else
            Sleep(1000)
        EndIf
    WEnd




EndFunc   ;==>PTRobot_Wait

Func PTRobot_GetErrorString()
    Local $result
    Local $err = @error

    Local $dwErrorNum
    Local $wszErrorString
    Local $dwMaxLength
    Local $dwLanguage

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetErrorString", "hwnd", $RobotAddress, "dword", $dwErrorNum, "wstr", $wszErrorString, "dword", $dwMaxLength, "dword", $dwLanguage)

    $dwErrorNum = $result[2]
    $wszErrorString = $result[3]
    $dwMaxLength = $result[4]
    $dwLanguage = $result[5]

    MsgBox($MB_OK + $MB_ICONWARNING + $MB_ICONERROR, "", "Status: " & $result[0] & @CRLF & _
            "System Number Error: " & $dwErrorNum & @CRLF & _
            "Error String: " & $wszErrorString & @CRLF & _
            "Max Length: " & $dwMaxLength & @CRLF & _
            "Language: " & $dwLanguage)

    Return $result[0]
EndFunc   ;==>PTRobot_GetErrorString

Func PTRobot_Initialize()
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_Initialize")

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf

EndFunc   ;==>PTRobot_Initialize

Func PTRobot_Destroy()
    Local $result
    Local $err = @error

    PTRobot_Wait()
    DllCall($hPTROBOTDLL, "int", "PTRobot_SystemAction", "hwnd", $RobotAddress, "dword", $PTACT_XI_LIGHT_OFF)
    PTRobot_Wait()

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_Destroy")

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        Exit ($result[0])
    EndIf

EndFunc   ;==>PTRobot_Destroy

Func PTRobot_EnumRobots()
    Local $result
    Local $err = @error

    Local $hRobots = DllStructCreate("hwnd[10]")
    Local $dwNumRobots = DllStructCreate("dword")
    DllStructSetData($dwNumRobots, 1, 10) ; Set input to be the size of the array

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_EnumRobots", "ptr", DllStructGetPtr($hRobots), "ptr", DllStructGetPtr($dwNumRobots))

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf

    ; Set Global Variables
    $RobotAddress = DllStructGetData($hRobots, 1, 1)
    $NumRobots = DllStructGetData($dwNumRobots, 1)
EndFunc   ;==>PTRobot_EnumRobots

Func PTRobot_EnumDrives()
    Local $result
    Local $err = @error

    Local $hDrives = DllStructCreate("hwnd[10]")
    Local $dwNumDrives = DllStructCreate("dword")
    DllStructSetData($dwNumDrives, 1, 10) ; Set input to be the size of the array

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_EnumDrives", "hwnd", $RobotAddress, "ptr", DllStructGetPtr($hDrives), "ptr", DllStructGetPtr($dwNumDrives))

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf

    ; Set Global Variables
    $NumDrives = DllStructGetData($dwNumDrives, 1)
    ReDim $DriveAddress[$NumDrives]
    ReDim $DriveLetter[$NumDrives]
    For $i = 0 To $NumDrives - 1
        $DriveAddress[$i] = DllStructGetData($hDrives, 1, $i + 1)
        $DriveLetter[$i] = Chr(Dec(StringRight($DriveAddress[$i], 2)))
        ;MsgBox($MB_OK, "Debug", "Drive " & $i & ": " & $DriveLetter[$i])
        If Not StringRegExp($DriveLetter[$i], "^[A-Z]$") Then
            PTRobot_ErrorMsgBox($CUSTOM_INVALID_DRIVE_LETTER)
            PTRobot_Destroy()
            Exit ($CUSTOM_INVALID_DRIVE_LETTER)
        EndIf
    Next

EndFunc   ;==>PTRobot_EnumDrives

Func PTRobot_GetRobotStatus()
    Local $result
    Local $err = @error

    Local $dRobotStatus = DllStructCreate("dword dwSystemState;dword dwSystemError;dword dwCurrColorSpits;dword dwCurrBlackSpits;dword dwFullColorSpits;dword dwFullBlackSpits")

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetRobotStatus", "hwnd", $RobotAddress, "ptr", DllStructGetPtr($dRobotStatus))

    ;MsgBox($MB_OK, "", "Status: "         & $result[0]                                          & @CRLF & _
    ;              "System State: "   & DllStructGetData($dRobotStatus, "dwSystemState")    & @CRLF & _
    ;              "System Error: "   & DllStructGetData($dRobotStatus, "dwSystemError")    & @CRLF & _
    ;              "CurrColorSpits: " & DllStructGetData($dRobotStatus, "dwCurrColorSpits") & @CRLF & _
    ;              "CurrBlackSpits: " & DllStructGetData($dRobotStatus, "dwCurrBlackSpits") & @CRLF & _
    ;              "FullColorSpits: " & DllStructGetData($dRobotStatus, "dwFullColorSpits") & @CRLF & _
    ;              "FullBlackSpits: " & DllStructGetData($dRobotStatus, "dwFullBlackSpits"));

    ; Set Global Variables
    $SystemState = DllStructGetData($dRobotStatus, "dwSystemState")
    $SystemError = DllStructGetData($dRobotStatus, "dwSystemError")

    Return $result[0]
EndFunc   ;==>PTRobot_GetRobotStatus

Func PTRobot_GetRobotInfo()
    Local $result
    Local $err = @error

    Local $dRobotInfo = DllStructCreate("hwnd hRobot;char tszRobotDesc[100];dword dwRobotType;dword dwNumDrives;dword dwNumPrinters;dword dwNumBins;dword dwDriveColumns;dword dwDriveRows;char tszRobotFirmware[20];dword dwOptions;dword dwAction;hwnd hDrives[10];dword dwDriveBusType")

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetRobotInfo", "hwnd", $RobotAddress, "ptr", DllStructGetPtr($dRobotInfo))

    ;MsgBox($MB_OK, "", "Status: "                  & $result[0]                                        & @CRLF & _
    ;              "Robot: "                   & DllStructGetData($dRobotInfo, "hRobot")           & @CRLF & _
    ;              "Robot Type: "              & DllStructGetData($dRobotInfo, "tszRobotDesc")     & @CRLF & _
    ;              "Number of Drives: "        & DllStructGetData($dRobotInfo, "dwNumDrives")      & @CRLF & _
    ;              "Number of Printers: "      & DllStructGetData($dRobotInfo, "dwNumPrinters")    & @CRLF & _
    ;              "Number of Bins: "          & DllStructGetData($dRobotInfo, "dwNumBins")        & @CRLF & _
    ;              "Number of Drive Columns: " & DllStructGetData($dRobotInfo, "dwDriveColumns")   & @CRLF & _
    ;              "Number of Drive Rows: "    & DllStructGetData($dRobotInfo, "dwDriveRows")      & @CRLF & _
    ;              "Robot FW Version: "        & DllStructGetData($dRobotInfo, "tszRobotFirmware") & @CRLF & _
    ;              "Robot Options: "           & DllStructGetData($dRobotInfo, "dwOptions")        & @CRLF & _
    ;              "Robot Actions: "           & DllStructGetData($dRobotInfo, "dwAction")         & @CRLF & _
    ;              "Drive Bus Type: "          & DllStructGetData($dRobotInfo, "dwDriveBusType"))

    $RobotType = DllStructGetData($dRobotInfo, "tszRobotDesc")
    $NumDrives = DllStructGetData($dRobotInfo, "dwNumDrives")
    $NumPrinters = DllStructGetData($dRobotInfo, "dwNumPrinters")
    $NumBins = DllStructGetData($dRobotInfo, "dwNumBins")

    Return $result[0]
EndFunc   ;==>PTRobot_GetRobotInfo

Func PTRobot_GetDriveInfo($num)
    Local $result
    Local $err = @error

    Local $dDrvInfo = DllStructCreate("hwnd hDrive;char tszDriveName[132];char tszFirmwareVer[40];char tszSerialNum[40];hwnd hRobot;dword dwDriveColumn;dword dwDriveRow")

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetDriveInfo", "hwnd", $DriveAddress[$num], "ptr", DllStructGetPtr($dDrvInfo))

    ;MsgBox($MB_OK, "", "Status: "           & $result[0] & @CRLF & _
    ;              "Drive: "            & DllStructGetData($dDrvInfo, "hDrive")         & @CRLF & _
    ;              "Drive Name: "       & DllStructGetData($dDrvInfo, "tszDriveName")   & @CRLF & _
    ;              "Firmware Version: " & DllStructGetData($dDrvInfo, "tszFirmwareVer") & @CRLF & _
    ;              "Serial Number: "    & DllStructGetData($dDrvInfo, "tszSerialNum")   & @CRLF & _
    ;              "Robot: "            & DllStructGetData($dDrvInfo, "hRobot")         & @CRLF & _
    ;              "Drive Column: "     & DllStructGetData($dDrvInfo, "dwDriveColumn")  & @CRLF & _
    ;              "Drive Row: "        & DllStructGetData($dDrvInfo, "dwDriveRow"))

    Return $result[0]
EndFunc   ;==>PTRobot_GetDriveInfo

Func PTRobot_LoadDrive($num, $dwFromLocation, $dwFirstDiscLoad)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_LoadDrive", "hwnd", $RobotAddress, "hwnd", $DriveAddress[$num], "dword", $dwFromLocation, "dword", $dwFirstDiscLoad)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_LoadDrive

Func PTRobot_LoadPrinter($dwFromLocation)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_LoadPrinter", "hwnd", $RobotAddress, "dword", $dwFromLocation)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_LoadPrinter

Func PTRobot_LoadPrinterFromDrive($num)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_LoadPrinterFromDrive", "hwnd", $RobotAddress, "hwnd", $DriveAddress[$num])

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_LoadPrinterFromDrive

Func PTRobot_PrintFile($strFilename)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_PrintFile", "hwnd", $RobotAddress, "str", $strFilename, "dword", 0)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit
    EndIf
EndFunc   ;==>PTRobot_PrintFile

Func PTRobot_UnLoadPrinter($dwToLocation)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_UnLoadPrinter", "hwnd", $RobotAddress, "dword", $dwToLocation)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_UnLoadPrinter

Func PTRobot_UnLoadDrive($num, $dwToLocation)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_UnLoadDrive", "hwnd", $RobotAddress, "hwnd", $DriveAddress[$num], "dword", $dwToLocation)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_UnLoadDrive

Func PTRobot_MoveDiscBetweenLocations($dwFromLocation, $dwToLocation)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_MoveDiscBetweenLocations", "hwnd", $RobotAddress, "dword", $dwFromLocation, "dword", $dwToLocation)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_MoveDiscBetweenLocations

Func PTRobot_OpenCloseDrive($num, $dwOpen)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_OpenCloseDrive", "hwnd", $DriveAddress[$num], "dword", $dwOpen)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_OpenCloseDrive

Func PTRobot_SystemAction($dwAction)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_SystemAction", "hwnd", $RobotAddress, "dword", $dwAction)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_SystemAction

Func PTRobot_KillSystemError($dwResetPrinter)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_KillSystemError", "hwnd", $RobotAddress, "dword", $dwResetPrinter)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_KillSystemError

Func PTRobot_GetManufactureInfo() ; WARNING: This function has not been thoroughly tested.
    Local $result
    Local $err = @error

    Local $dPTManufactureInfo = DllStructCreate("hwnd hRobot;char tszSerialNum[11];char tszManufactureDate[12];dword dwFiller[20]")

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetManufactureInfo", "hwnd", $RobotAddress, "ptr", DllStructGetPtr($dPTManufactureInfo))

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf

    MsgBox($MB_OK + $MB_ICONINFORMATION, "", "Serial Number: " & DllStructGetData($dPTManufactureInfo, "tszSerialNum") & @CRLF & _
            "Manufacture Date: " & DllStructGetData($dPTManufactureInfo, "tszManufactureDate") & @CRLF & _
            "Filler: " & DllStructGetData($dPTManufactureInfo, "dwFiller", 1))

    Return $result[0]
EndFunc   ;==>PTRobot_GetManufactureInfo

Func PTRobot_GetPrinterSettings()
    Local $result
    Local $err = @error

    Local $dPrinterSettings = DllStructCreate("dword dwPrintQuality;dword dwInnerDiam;dword dwOuterMargin")
    Local $PrintQuality

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetPrinterSettings", "hwnd", $RobotAddress, "ptr", DllStructGetPtr($dPrinterSettings))

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf

    Switch DllStructGetData($dPrinterSettings, "dwPrintQuality")
        Case $PQ_LOW
            $PrintQuality = "Low"
        Case $PQ_MED
            $PrintQuality = "Medium"
        Case $PQ_BETTER
            $PrintQuality = "Better" ; Default
        Case $PQ_HIGH
            $PrintQuality = "High"
        Case $PQ_BEST
            $PrintQuality = "Best"
        Case Else ; This shouldn't happen.
            $PrintQuality = "UNKNOWN"
    EndSwitch

    MsgBox($MB_OK + $MB_ICONINFORMATION, "", "Print Quality: " & $PrintQuality & @CRLF & _
            "Inner Diameter: " & DllStructGetData($dPrinterSettings, "dwInnerDiam") & @CRLF & _
            "Outer Margin: " & DllStructGetData($dPrinterSettings, "dwOuterMargin"))

EndFunc   ;==>PTRobot_GetPrinterSettings

Func PTRobot_ErrorMsgBox($ErrorNum)
    Local $ErrorMsg

    Switch $ErrorNum
        Case $SYSERR_PTR_TRAY
            $ErrorMsg = "Tray movement error.  Press the left button on the unit to try again."
        Case $SYSERR_CART_CODE
            $ErrorMsg = "There was a problem finding the ink cartridges.  Open the cover and press the left button. Make sure the color cartridge is installed on the left and the black is on the right.  Then close the cover."
        Case $SYSERR_INPUT_EMPTY
            $ErrorMsg = "The input bin is empty.  Open the cover and add more discs.  Then close the cover and push the left button on the unit."
        Case $SYSERR_PTR_COMM
            $ErrorMsg = "There was an internal printer communications error.  Press the left button on the unit to try again."
        Case $SYSERR_CLR_EMPTY
            $ErrorMsg = "WARNING:  The color cartridge is LOW on ink.  To replace the cartridge, open the cover on the unit and press the left button.  Then install the new cartridge and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_BLK_EMPTY
            $ErrorMsg = "WARNING:  The black cartridge is LOW on ink.  To replace the cartridge, open the cover on the unit and press the left button.  Then install the new cartridge and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_BOTH_EMPTY
            $ErrorMsg = "WARNING:  Both ink cartridges are LOW on ink.  To replace the cartridges, open the cover on the unit and press the left button.  Then install the new cartridges and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_PICK
            $ErrorMsg = "The disc was not picked.  Press the left button on the unit to try again."
        Case $SYSERR_ARM_MOVE
            $ErrorMsg = "There was an arm movement error.  Press the left button on the unit to try again."
        Case $SYSERR_CART_MOVE
            $ErrorMsg = "Arm picker error.  Press the left button on the unit to try again."
        Case $SYSERR_INTERNAL_SW
            $ErrorMsg = "There was an internal software error.  Please re-start the software."
        Case $SYSERR_NO_ROBODRIVES
            $ErrorMsg = "No external recorder drives were found.  Re-power the computer and unit, and then re-start the software."
        Case $SYSERR_OFFLINE
            $ErrorMsg = "The unit is offline.  Please ensure the unit is connected and powered on.  You may need to shut down and restart the software."
        Case $SYSERR_COVER_OPEN
            $ErrorMsg = "The unit’s cover is open.  Please close the cover."
        Case $SYSERR_PRINTER_PICK
            $ErrorMsg = "The disc was not picked from the printer.  Press the left button to retry."
        Case $SYSERR_MULTIPLE_PICK
            $ErrorMsg = "Multiple discs were picked up and moved.  Please manually remove any extra discs that were moved, keeping a single disc in place.  Then close the cover and press the left button."
        Case $SYSERR_MULTIPLEDISCS_IN_PRINTER
            $ErrorMsg = "Multiple discs were placed in the printer.  Please manually remove any extra discs from the printer, keeping a single disc in place.  Then close the cover and press the left button."
        Case $SYSERR_MULTIPLEDISCS_IN_RECORDER
            $ErrorMsg = "Multiple discs were placed in the recorder.  Please manually remove any extra discs from the recorder, keeping a single disc in place.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_RECORDER
            $ErrorMsg = "The disc was dropped while moving into the recorder.  Please manually place the disc into the recorder tray.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_BIN1
            $ErrorMsg = "The disc was dropped while moving into the right bin.  Please manually place the disc into the right bin.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_BIN2
            $ErrorMsg = "The disc was dropped while moving into the left bin.  Please manually place the disc into the left bin.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_PRINTER
            $ErrorMsg = "The disc was dropped while moving into the printer.  Please manually place the disc into the printer tray.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_REJECT
            $ErrorMsg = "The disc was dropped while moving to the reject area.  Please remove the dropped disc.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_UNKNOWN
            $ErrorMsg = "The disc was dropped. Please remove the dropped disc.  Then close the cover and press the left button."
        Case $SYSERR_ALIGNNEEDED
            $ErrorMsg = "The printer cartridges need to be aligned."
        Case $SYSERR_COLOR_INVALID
            $ErrorMsg = "The color cartridge is invalid.  Open the cover and press the left button.  Change the cartridge and close the cover."
        Case $SYSERR_BLACK_INVALID
            $ErrorMsg = "The black cartridge is invalid.  Open the cover and press the left button.  Change the cartridge and close the cover."
        Case $SYSERR_BOTH_INVALID
            $ErrorMsg = "Both cartridges are invalid.  Open the cover and press the left button.  Change the cartridges and close the cover."
        Case $SYSERR_NOCARTS
            $ErrorMsg = "No cartridges are installed.  Open the cover and press the left button.  Install the cartridges and close the cover."
        Case $SYSERR_K_IN_CMY
            $ErrorMsg = "The black cartridge is installed in the color position.  Open the cover and press the left button.  Change the cartridge and close the cover."
        Case $SYSERR_CMY_IN_K
            $ErrorMsg = "The color cartridge is installed in the black position.  Open the cover and press the left button.  Change the cartridge and close the cover."
        Case $SYSERR_SWAPPED
            $ErrorMsg = "The black and color cartridges are swapped.  Open the cover and press the left button.  Swap the cartridges and close the cover."
        Case $SYSERR_PIGONPRO
            $ErrorMsg = "This printer is not compatible with a pigment-based black cartridge.  Open the cover and press the left button.  Install a dye-based black cartridge and close the cover."
        Case $SYSERR_ALIGNFAILED
            $ErrorMsg = "The alignment print failed."
        Case $SYSERR_DROPPED_DISC_PRINTER_FATAL
            $ErrorMsg = "The disc was dropped while moving to/from the printer.  Please open the cover and manually remove and discard the disc.  Then place a new disc in the recorder, close the cover and press the left button."
        Case $SYSERR_MULTIPLEDISCS_IN_RIGHTBIN
            $ErrorMsg = "Multiple discs were placed in the right bin.  Please manually move any extra discs to the left bin, keeping a single disc in place.  Then close the cover and press the left button."
        Case $SYSERR_MULTIPLEDISCS_IN_LEFTBIN
            $ErrorMsg = "Multiple discs were placed in the left bin.  Please manually move any extra discs to the right bin, keeping a single disc in place.  Then close the cover and press the left button."
        Case $SYSERR_CLR_EMPTY_FINAL
            $ErrorMsg = "WARNING:  The color cartridge is Empty.  To replace the cartridge, open the cover on the unit and press the left button.  Then install the new cartridge and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_BLK_EMPTY_FINAL
            $ErrorMsg = "WARNING:  The black cartridge is Empty.  To replace the cartridge, open the cover on the unit and press the left button.  Then install the new cartridge and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_BOTH_EMPTY_FINAL
            $ErrorMsg = "WARNING:  Both cartridges are Empty.  To replace the cartridge, open the cover on the unit and press the left button.  Then install the new cartridge and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_WAITING_FOR_PRINTER
            $ErrorMsg = "The system timed out waiting for the printer to finish.  The disc may not have been printed on."
        Case $SYSERR_NO_DISC_IN_PRINTER
            $ErrorMsg = "No disc was found in the printer."
        Case $SYSERR_BUSY
            $ErrorMsg = "System Busy."
        Case $SYSERR_PURGE
            $ErrorMsg = "Purge."
        Case $SYSERR_DOCK_SENSOR
            $ErrorMsg = "Dock Sensor."
        Case $SYSERR_ALREADY_PRINTED
            $ErrorMsg = "Already printed."
        Case $SYSERR_UNKNOWN_HARDWARE
            $ErrorMsg = "Unknown hardware."
        Case $PTROBOT_FEATURE_NOT_IMPLEMENTED
            $ErrorMsg = "This feature is not implemented."
        Case $CUSTOM_INVALID_DRIVE_LETTER
            $ErrorMsg = "Could not find valid drive letter for burner."
        Case Else
            $ErrorMsg = "UNKNOWN ERROR! " & $ErrorNum
    EndSwitch

    WriteStatusError("Error: " & $ErrorMsg & @CRLF)
    MsgBox($MB_OK + $MB_ICONERROR, "Error", $ErrorMsg)
EndFunc   ;==>PTRobot_ErrorMsgBox

Func _ValidateImageFile($sFilename)
    Local Const $sSTDHeader = "4D5600FF0C001200"
    Local Const $sPNGHeader = "89504E470D0A1A0A"
    Local Const $sJPGHeader = "FFD8FFE000104A46"
    Local Const $sGIF87Header = "474946383961"
    Local Const $sGIF89Header = "474946383761"
    Local Const $sBMPHeader = "424D"
    Local $sFileType, $bReturn = False

    Local $hFileHandle = FileOpen($sFilename, 16) ; Open file read only
    Local $bFileContents = FileRead($hFileHandle, 8) ; Read first 8 bytes
    Local $sFileExt = StringLower(StringRight($sFilename, 4)) ; Determine file extension

    If StringMid($bFileContents, 3, StringLen($sJPGHeader)) = $sJPGHeader And $sFileExt = ".jpg" Then
        $sFileType = "JPEG"
        $bReturn = True
    ElseIf StringMid($bFileContents, 3, StringLen($sSTDHeader)) = $sSTDHeader And $sFileExt = ".std" Then
        $sFileType = "SureThing Label"
        $bReturn = True
    ElseIf StringMid($bFileContents, 3, StringLen($sPNGHeader)) = $sPNGHeader And $sFileExt = ".png" Then
        $sFileType = "PNG"
        $bReturn = True
    ElseIf StringMid($bFileContents, 3, StringLen($sBMPHeader)) = $sBMPHeader And $sFileExt = ".bmp" Then
        $sFileType = "BMP"
        $bReturn = True
        ;ElseIf StringMid($bFileContents, 3, StringLen($sGIF87Header)) = $sGIF87Header And $sFileExt = ".gif" Then
        ;  $sFileType = "GIF87"
        ;  $bReturn = True
        ;ElseIf StringMid($bFileContents, 3, StringLen($sGIF89Header)) = $sGIF89Header And $sFileExt = ".gif" Then
        ;  $sFileType = "GIF89"
        ;  $bReturn = True
    Else
        $sFileType = "Unknown"
    EndIf

    ;MsgBox($MB_OK, "", $sFileType)
    Return $bReturn
EndFunc   ;==>_ValidateImageFile

Func _NoHaltMsgBox($code = 0, $Title = "", $text = "", $timeout = 0)
    Run(@AutoItExe & ' /AutoIt3ExecuteLine  "MsgBox(' & $code & ', ''' & $Title & ''', ''' & $text & ''',' & $timeout & ')"')
EndFunc   ;==>_NoHaltMsgBox

Func OpenPrompt($message, $Queue)

    Local $answer

    ; Get number of sets from the command-line
    $answer = getNumSets()

    ; If copies were specified on command line, then end function without prompting
    If $answer > 0 Then
        $NumSets = $answer
        Return $Queue
    EndIf

    Local $QueueSize = UBound($Queue, 1)

    Local $Form_Duplicator_GUI = GUICreate("IMGBurn AutoIt Script", 404, 260, 192, 124)
    Local $Button_Start = GUICtrlCreateButton("Start", 72, 210, 87, 33)
    Local $NumCopies = GUICtrlCreateInput($NumSets, 220, 170, 49, 21, $ES_CENTER)
    Local $Updown_NumCopies = GUICtrlCreateUpdown($NumCopies)
    GUICtrlSetLimit(-1, $MAX_NUMSETS, 1)
    Local $Button_Cancel = GUICtrlCreateButton("Cancel", 232, 210, 91, 33)
    Local $Label_NumCopies = GUICtrlCreateLabel("How many copies?", 116, 170, 94, 17, BitOR($SS_RIGHT, $SS_CENTERIMAGE))
    Local $Label_Message = GUICtrlCreateLabel("This script will run the disc burning software for the job" & @LF & $message & @LF & "Run?", 2, 8, 400, 57, BitOR($SS_NOPREFIX, $SS_CENTER))
    Local $List_Queue = GUICtrlCreateList("", 2, 60, 400, 100, BitOR($LBS_MULTIPLESEL, $WS_HSCROLL, $WS_VSCROLL, $WS_BORDER))
    GUICtrlSetLimit(-1, 200) ; Limit horizontal scrolling
    Local $DiscTitle
    For $i = 0 To $QueueSize - 1
        $DiscTitle = StringRegExp($Queue[$i][0], '\\([^\\]*)\.std$', 1)
        GUICtrlSetData(-1, $DiscTitle[0])
        ;_GUICtrlListBox_SetSel($List_Queue, $i) ; Preselect entry
        GUICtrlSendMsg(-1, $LB_SETSEL, True, $i) ; Preselect entry
    Next
    GUICtrlSendMsg(-1, $LB_SETTOPINDEX, 0, 0) ; Send scrollbar to top of list
    GUISetState(@SW_SHOW)

    Local $NewQueue

    While 1
        $answer = GUIGetMsg()
        $NewQueue = $Queue ; Set/Reset NewQueue to Queue

        Switch $answer
            Case $Button_Start
                If GUICtrlRead($NumCopies) < 1 Or GUICtrlRead($NumCopies) > $MAX_NUMSETS Then
                    MsgBox($MB_OK + $MB_ICONERROR, "Error", "Number of copies must be between 1 and " & $MAX_NUMSETS & ".")
                Else
                    For $i = $QueueSize - 1 To 0 Step -1 ; Process array in reverse to keep index aligned
                        If _GUICtrlListBox_GetSel($List_Queue, $i) == False Then
                            _ArrayDelete($NewQueue, $i)
                        EndIf
                    Next

                    If UBound($NewQueue, 1) = 0 Then
                        MsgBox($MB_OK + $MB_ICONERROR, "Error", "No discs selected.")
                    Else
                        ExitLoop ; Success!
                    EndIf
                EndIf
            Case $Button_Cancel
                MsgBox($MB_OK, "IMGBurn AutoIt Script", "OK.  Bye!")
                Exit
            Case $GUI_EVENT_CLOSE
                Exit
        EndSwitch
    WEnd

    $NumSets = GUICtrlRead($NumCopies)

    GUISetState(@SW_HIDE)

    Return $NewQueue
EndFunc   ;==>OpenPrompt

Func CalcDupeTime($iTimestampDiff)
    Local $iSeconds = Int($iTimestampDiff / 1000)
    Local $iHours, $iMins, $iSecs

    $iHours = Int($iSeconds / 3600)
    $iSeconds = Mod($iSeconds, 3600)
    $iMins = Int($iSeconds / 60)
    $iSecs = Mod($iSeconds, 60)

    If StringLen($iHours) = 1 Then $iHours = "0" & $iHours
    If StringLen($iMins) = 1 Then $iMins = "0" & $iMins
    If StringLen($iSecs) = 1 Then $iSecs = "0" & $iSecs

    Return $iHours & ":" & $iMins & ":" & $iSecs
EndFunc   ;==>CalcDupeTime

Func sendEmail($Title, $Queue, $NumSets, $DupeTime)
    Local $QueueSize = UBound($Queue, 1)

    Local $s_SmtpServer = "nyex.us.corp.aacisd.com"
    Local $s_FromName = "IMGBurn Disc Duplicator"
    Local $s_FromAddress = "ConfigurationManagementDistribution@aaccorp.com"
    Local $s_ToAddress = "ConfigurationManagementDistribution@aaccorp.com"
    Local $s_Subject = "[DUPLICATOR] - " & $Title & " - " & $NumSets & " set(s) in " & $DupeTime

    ; Old _INetSmtpMail code
    ;Local $as_Body[$QueueSize + 1]
    ;$as_Body[0] = "The job took " & $DupeTime & " to complete." & @CRLF

    Local $s_Body = "<body><p style=""font-family:Calibri, Arial, Helvetica, sans-serif;font-size:11pt;"">The job took " & $DupeTime & " to complete.</p><p></p>"
    $s_Body &= "<p style=""font-family:Calibri, Arial, Helvetica, sans-serif;font-size:11pt;"">"

    Local $DiscTitle

    ; Old _INetSmtpMail code
    ;For $i = 0 To $QueueSize - 1
    ;   $DiscTitle = StringRegExp($Queue[$i][0], '\\([^\\]*)\.std$', 1)
    ;   $as_Body[$i+1] = $DiscTitle[0] & @TAB ; We add a TAB at the end to stop Outlook from concatenating lines >= 40 chars
    ;Next

    For $i = 0 To $QueueSize - 1
        $DiscTitle = StringRegExp($Queue[$i][0], '\\([^\\]*)\.std$', 1)
        _HtmlEntities_Encode($DiscTitle[0])
        $s_Body &= $DiscTitle[0] & "<br>"
    Next

    $s_Body &= "</p></body>"

    ; Old _INetSmtpMail code
    ;Local $Response = _INetSmtpMail($s_SmtpServer, $s_FromName, $s_FromAddress, $s_ToAddress, $s_Subject, $as_Body, 1, -1)
    ;Local $err = @error
    ;
    ;If $Response = 1 Then
    ;   MsgBox($MB_OK, "Success!", "Mail sent")
    ;Else
    ;   MsgBox($MB_OK + $MB_ICONERROR, "Error!", "Mail failed with error code " & $err)
    ;EndIf

    Local $oMyRet[2]
    Local $oMyError = ObjEvent("AutoIt.Error", "MyErrFunc")
    Local $Response = _INetSmtpMailCom($s_SmtpServer, $s_FromName, $s_FromAddress, $s_ToAddress, $s_Subject, $s_Body)
    If @error Then
        MsgBox($MB_OK + $MB_ICONERROR, "Error sending message", "Error code:" & @error & "  Description:" & $Response)
    EndIf

EndFunc   ;==>sendEmail

Func toggleScreenSaver()
    Local $key = "HKEY_CURRENT_USER\Control Panel\Desktop"
    Local $value = "ScreenSaveActive"
    If Number(RegRead($key, $value)) Then
        RegWrite($key, $value, "REG_SZ", 0)
    Else
        RegWrite($key, $value, "REG_SZ", 1)
    EndIf
EndFunc   ;==>toggleScreenSaver

Func disableScreenSaver()
    Local $key = "HKEY_CURRENT_USER\Control Panel\Desktop"
    Local $value = "ScreenSaveActive"
    If Number(RegRead($key, $value)) Then
        RegWrite($key, $value, "REG_SZ", 0)
    EndIf
EndFunc   ;==>disableScreenSaver

Func enableScreenSaver()
    Local $key = "HKEY_CURRENT_USER\Control Panel\Desktop"
    Local $value = "ScreenSaveActive"
    If Number(RegRead($key, $value)) = 0 Then
        RegWrite($key, $value, "REG_SZ", 1)
    EndIf
EndFunc   ;==>enableScreenSaver

Func WriteStatus($LogMessage)
    ConsoleWrite($LogMessage)
    GUICtrlSetData($Label_Status, $LogMessage)
EndFunc   ;==>WriteStatus

Func WriteStatusError($LogMessage)
    ConsoleWriteError($LogMessage)
    GUICtrlSetData($Label_Status, $LogMessage)
EndFunc   ;==>WriteStatusError

Func _INetSmtpMailCom($s_SmtpServer, $s_FromName, $s_FromAddress, $s_ToAddress, $s_Subject = "", $as_Body = "", $s_AttachFiles = "", $s_CcAddress = "", $s_BccAddress = "", $s_Importance = "Normal", $s_Username = "", $s_Password = "", $IPPort = 25, $ssl = 0)
    Local $objEmail = ObjCreate("CDO.Message")
    $objEmail.From = '"' & $s_FromName & '" <' & $s_FromAddress & '>'
    $objEmail.To = $s_ToAddress
    Local $i_Error = 0
    Local $i_Error_desciption = ""
    Local $oMyRet[2]
    If $s_CcAddress <> "" Then $objEmail.Cc = $s_CcAddress
    If $s_BccAddress <> "" Then $objEmail.Bcc = $s_BccAddress
    $objEmail.Subject = $s_Subject
    If StringInStr($as_Body, "<") And StringInStr($as_Body, ">") Then
        $objEmail.HTMLBody = $as_Body
    Else
        $objEmail.Textbody = $as_Body & @CRLF
    EndIf
    If $s_AttachFiles <> "" Then
        Local $S_Files2Attach = StringSplit($s_AttachFiles, ";")
        For $x = 1 To $S_Files2Attach[0]
            $S_Files2Attach[$x] = _PathFull($S_Files2Attach[$x])
;~          ConsoleWrite('@@ Debug : $S_Files2Attach[$x] = ' & $S_Files2Attach[$x] & @LF & '>Error code: ' & @error & @LF) ;### Debug Console
            If FileExists($S_Files2Attach[$x]) Then
                ConsoleWrite('+> File attachment added: ' & $S_Files2Attach[$x] & @LF)
                $objEmail.AddAttachment($S_Files2Attach[$x])
            Else
                ConsoleWrite('!> File not found to attach: ' & $S_Files2Attach[$x] & @LF)
                SetError(1)
                Return 0
            EndIf
        Next
    EndIf
    $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
    $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = $s_SmtpServer
    If Number($IPPort) = 0 Then $IPPort = 25
    $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = $IPPort
    ;Authenticated SMTP
    If $s_Username <> "" Then
        $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1
        $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = $s_Username
        $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = $s_Password
    EndIf
    If $ssl Then
        $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = True
    EndIf
    ;Update settings
    $objEmail.Configuration.Fields.Update
    ; Set Email Importance
    Switch $s_Importance
        Case "High"
            $objEmail.Fields.Item("urn:schemas:mailheader:Importance") = "High"
        Case "Normal"
            $objEmail.Fields.Item("urn:schemas:mailheader:Importance") = "Normal"
        Case "Low"
            $objEmail.Fields.Item("urn:schemas:mailheader:Importance") = "Low"
    EndSwitch
    $objEmail.Fields.Update
    ; Sent the Message
    $objEmail.Send
    If @error Then
        SetError(2)
        Return $oMyRet[1]
    EndIf
    $objEmail = ""
EndFunc   ;==>_INetSmtpMailCom
;
;
; Com Error Handler
Func MyErrFunc()
    Local $oMyError = ObjEvent("AutoIt.Error", "MyErrFunc")
    Local $HexNumber = Hex($oMyError.number, 8)
    Local $oMyRet[2]
    $oMyRet[0] = $HexNumber
    $oMyRet[1] = StringStripWS($oMyError.description, 3)
    ConsoleWrite("### COM Error !  Number: " & $HexNumber & "   ScriptLine: " & $oMyError.scriptline & "   Description:" & $oMyRet[1] & @LF)
    SetError(1) ; something to check for when this function returns
    Return
EndFunc   ;==>MyErrFunc

Func _HtmlEntities_Encode(ByRef $sTxt)
    For $i = 0 To 245
        $sTxt = StringReplace($sTxt, ChrW($aisEntities[$i][0]), '&' & $aisEntities[$i][1] & ';', 0, 1)
    Next
EndFunc   ;==>_HtmlEntities_Encode

Func _HtmlEntities_Decode(ByRef $sTxt)
    For $i = 0 To 245
        $sTxt = StringReplace($sTxt, '&' & $aisEntities[$i][1] & ';', ChrW($aisEntities[$i][0]), 0, 1)
    Next
EndFunc   ;==>_HtmlEntities_Decode

Func StoreDriveNum()

    ; Open the file for writing (append to the end of a file) and store the handle to a variable.
    Local $hFileOpen = FileOpen($LastUsedDriveNumFile, $FO_OVERWRITE)
    If $hFileOpen = -1 Then
        MsgBox($MB_OK + $MB_ICONERROR + $MB_SYSTEMMODAL, "", "An error occurred whilst writing " & $LastUsedDriveNumFile & ".")
        Return False
    EndIf

    FileWrite($hFileOpen, $LastUsedDriveNum)

    ; Close the handle returned by FileOpen.
    FileClose($hFileOpen)

    Return True
EndFunc   ;==>StoreDriveNum

Func GetLastUsedDriveNum()

    ; Open the file for reading and store the handle to a variable.
    Local $hFileOpen = FileOpen($LastUsedDriveNumFile, $FO_READ)
    If $hFileOpen = -1 Then
        MsgBox($MB_OK + $MB_ICONERROR + $MB_SYSTEMMODAL, "", "An error occurred when reading the file " & $LastUsedDriveNumFile & ".")
        Return False
    EndIf

    ; Read the contents of the file using the handle returned by FileOpen.
    Local $iFileRead = Number(FileRead($hFileOpen))

    ; Close the handle returned by FileOpen.
    FileClose($hFileOpen)

    ; Validate the file contents
    If $iFileRead >= 0 And $iFileRead < $NumDrives Then
        ; Set LastUsedDriveNum.
        $LastUsedDriveNum = $iFileRead
    Else
        MsgBox($MB_OK + $MB_ICONERROR + $MB_SYSTEMMODAL, "", "Contents of " & $LastUsedDriveNumFile & " are invalid.")
        ; Set LastUsedDriveNum to default.
        $LastUsedDriveNum = 0
        Return False
    EndIf

    Return True
EndFunc   ;==>GetLastUsedDriveNum

 

Gerard J. Pinzonegpinzone AT yahoo.com
Link to comment
Share on other sites

  • 10 months later...

Hey there,

thank you for sharing the script! I was looking into AutoIt for controlling my Primera SE. Finding your script, I was hoping to get it working for my Project. Being new to AutoIt, I am having a bit of trouble getting your script started "as it is". What changes do I have to make, other than specifying the location on the DLLs?

Greetings,

Link to comment
Share on other sites

16 minutes ago, Enoch23 said:

I am having a bit of trouble getting your script started "as it is". What changes do I have to make

You'll have to get "ImgBurn.exe" in place. If that is your trouble. What is your trouble ?

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

I first tried the script in post #3. When executing from the SciTE editor nothing happened.

I was wondering if there is some other way this script is to be executed?

 

So, I tried the script in the last post (now post #7).

When executing from the SciTE editor I get the following output:

>"C:\Program Files (x86)\AutoIt3\SciTE\..\AutoIt3.exe" "C:\Program Files (x86)\AutoIt3\SciTE\AutoIt3Wrapper\AutoIt3Wrapper.au3" /run /prod /ErrorStdOut /in "C:\Users\User\Desktop\Primera.au3" /UserParams    
+>13:17:49 Starting AutoIt3Wrapper v.19.102.1901.0 SciTE v.4.1.2.0   Keyboard:00000407  OS:WIN_7/Service Pack 1  CPU:X64 OS:X64  Environment(Language:0409)  CodePage:0  utf8.auto.check:4
+>         SciTEDir => C:\Program Files (x86)\AutoIt3\SciTE   UserDir => C:\Users\User\AppData\Local\AutoIt v3\SciTE\AutoIt3Wrapper   SCITE_USERHOME => C:\Users\User\AppData\Local\AutoIt v3\SciTE 
>Running AU3Check (3.3.14.5)  from:C:\Program Files (x86)\AutoIt3  input:C:\Users\User\Desktop\Primera.au3
"C:\Users\User\Desktop\Primera.au3"(1199,26) : warning: $oMyRet: possibly used before declaration.
        Return $oMyRet[1]
~~~~~~~~~~~~~~~~~~~~~~~~~^
"C:\Users\User\Desktop\Primera.au3"(1208,42) : warning: $HexNumber possibly not declared/created yet
    $HexNumber = Hex($oMyError.number, 8)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
"C:\Users\User\Desktop\Primera.au3"(1158,64) : error: _PathFull(): undefined function.
            $S_Files2Attach[$x] = _PathFull($S_Files2Attach[$x])
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
C:\Users\User\Desktop\Primera.au3 - 1 error(s), 2 warning(s)
!>13:17:49 AU3Check ended. Press F4 to jump to next error.rc:2
+>13:17:49 AutoIt3Wrapper Finished.
>Exit code: 2    Time: 1.854

The first and last error are from the Function _INetSmtpMailCom. I'm guessing I have to configure something for that?

ImgBurn is installed.

Link to comment
Share on other sites

9 hours ago, Enoch23 said:

error: _PathFull(): undefined function.

That is not so had to fix

#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GuiListBox.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#include <ListboxConstants.au3>

#include <Array.au3>
#include <INet.au3>
#include <Misc.au3>
#include <File.au3>

If _Singleton("Primera", 1) = 0 Then
    ; We know the script is already running. Let the user know.
    MsgBox(0, "Error: Multiple Copies", "This script is already running. Using multiple copies of this script at the same time is unsupported!")
    Exit
EndIf

Opt('MustDeclareVars', 1)

Global $oMyRet, $oMyError = ObjEvent("AutoIt.Error", "MyErrFunc")

Global Const $aisEntities[246][2] = [[34, 'quot'],[38, 'amp'],[39, 'apos'],[60, 'lt'],[62, 'gt'],[160, 'nbsp'],[161, 'iexcl'],[162, 'cent'],[163, 'pound'],[164, 'curren'],[165, 'yen'],[166, 'brvbar'],[167, 'sect'],[168, 'uml'],[169, 'copy'],[170, 'ordf'],[171, 'laquo'],[172, 'not'],[173, 'shy'],[174, 'reg'],[175, 'macr'],[176, 'deg'],[177, 'plusmn'],[180, 'acute'],[181, 'micro'],[182, 'para'],[183, 'middot'],[184, 'cedil'],[186, 'ordm'],[187, 'raquo'],[191, 'iquest'],[192, 'Agrave'],[193, 'Aacute'],[194, 'Acirc'],[195, 'Atilde'],[196, 'Auml'],[197, 'Aring'],[198, 'AElig'],[199, 'Ccedil'],[200, 'Egrave'],[201, 'Eacute'],[202, 'Ecirc'],[203, 'Euml'],[204, 'Igrave'],[205, 'Iacute'],[206, 'Icirc'],[207, 'Iuml'],[208, 'ETH'],[209, 'Ntilde'],[210, 'Ograve'],[211, 'Oacute'],[212, 'Ocirc'],[213, 'Otilde'],[214, 'Ouml'],[215, 'times'],[216, 'Oslash'],[217, 'Ugrave'],[218, 'Uacute'],[219, 'Ucirc'],[220, 'Uuml'],[221, 'Yacute'],[222, 'THORN'],[223, 'szlig'],[224, 'agrave'],[225, 'aacute'],[226, 'acirc'],[227, 'atilde'],[228, 'auml'],[229, 'aring'],[230, 'aelig'],[231, 'ccedil'],[232, 'egrave'],[233, 'eacute'],[234, 'ecirc'],[235, 'euml'],[236, 'igrave'],[237, 'iacute'],[238, 'icirc'],[239, 'iuml'],[240, 'eth'],[241, 'ntilde'],[242, 'ograve'],[243, 'oacute'],[244, 'ocirc'],[245, 'otilde'],[246, 'ouml'],[247, 'divide'],[248, 'oslash'],[249, 'ugrave'],[250, 'uacute'],[251, 'ucirc'],[252, 'uuml'],[253, 'yacute'],[254, 'thorn'],[255, 'yuml'],[338, 'OElig'],[339, 'oelig'],[352, 'Scaron'],[353, 'scaron'],[376, 'Yuml'],[402, 'fnof'],[710, 'circ'],[732, 'tilde'],[913, 'Alpha'],[914, 'Beta'],[915, 'Gamma'],[916, 'Delta'],[917, 'Epsilon'],[918, 'Zeta'],[919, 'Eta'],[920, 'Theta'],[921, 'Iota'],[922, 'Kappa'],[923, 'Lambda'],[924, 'Mu'],[925, 'Nu'],[926, 'Xi'],[927, 'Omicron'],[928, 'Pi'],[929, 'Rho'],[931, 'Sigma'],[932, 'Tau'],[933, 'Upsilon'],[934, 'Phi'],[935, 'Chi'],[936, 'Psi'],[937, 'Omega'],[945, 'alpha'],[946, 'beta'],[947, 'gamma'],[948, 'delta'],[949, 'epsilon'],[950, 'zeta'],[951, 'eta'],[952, 'theta'],[953, 'iota'],[954, 'kappa'],[955, 'lambda'],[956, 'mu'],[957, 'nu'],[958, 'xi'],[959, 'omicron'],[960, 'pi'],[961, 'rho'],[962, 'sigmaf'],[963, 'sigma'],[964, 'tau'],[965, 'upsilon'],[966, 'phi'],[967, 'chi'],[968, 'psi'],[969, 'omega'],[977, 'thetasym'],[978, 'upsih'],[982, 'piv'],[8194, 'ensp'],[8195, 'emsp'],[8201, 'thinsp'],[8204, 'zwnj'],[8205, 'zwj'],[8206, 'lrm'],[8207, 'rlm'],[8211, 'ndash'],[8212, 'mdash'],[8216, 'lsquo'],[8217, 'rsquo'],[8218, 'sbquo'],[8220, 'ldquo'],[8221, 'rdquo'],[8222, 'bdquo'],[8224, 'dagger'],[8225, 'Dagger'],[8226, 'bull'],[8230, 'hellip'],[8240, 'permil'],[8242, 'prime'],[8243, 'Prime'],[8249, 'lsaquo'],[8250, 'rsaquo'],[8254, 'oline'],[8260, 'frasl'],[8364, 'euro'],[8465, 'image'],[8472, 'weierp'],[8476, 'real'],[8482, 'trade'],[8501, 'alefsym'],[8592, 'larr'],[8593, 'uarr'],[8594, 'rarr'],[8595, 'darr'],[8596, 'harr'],[8629, 'crarr'],[8656, 'lArr'],[8657, 'uArr'],[8658, 'rArr'],[8659, 'dArr'],[8660, 'hArr'],[8704, 'forall'],[8706, 'part'],[8707, 'exist'],[8709, 'empty'],[8711, 'nabla'],[8712, 'isin'],[8713, 'notin'],[8715, 'ni'],[8719, 'prod'],[8721, 'sum'],[8722, 'minus'],[8727, 'lowast'],[8730, 'radic'],[8733, 'prop'],[8734, 'infin'],[8736, 'ang'],[8743, 'and'],[8744, 'or'],[8745, 'cap'],[8746, 'cup'],[8747, 'int'],[8764, 'sim'],[8773, 'cong'],[8776, 'asymp'],[8800, 'ne'],[8801, 'equiv'],[8804, 'le'],[8805, 'ge'],[8834, 'sub'],[8835, 'sup'],[8836, 'nsub'],[8838, 'sube'],[8839, 'supe'],[8853, 'oplus'],[8855, 'otimes'],[8869, 'perp'],[8901, 'sdot'],[8968, 'lceil'],[8969, 'rceil'],[8970, 'lfloor'],[8971, 'rfloor'],[9001, 'lang'],[9002, 'rang'],[9674, 'loz'],[9824, 'spades'],[9827, 'clubs'],[9829, 'hearts'],[9830, 'diams']]

Global $hPTROBOTDLL = DllOpen("U:\Macros\PTRobot.dll")
Global $RobotType
Global $RobotAddress
Global $NumRobots
Global $DriveAddress[10]
Global $NumDrives
Global $NumPrinters
Global $NumBins
Global $SystemState
Global $SystemError
Global $NumDiscsProcessed = 0
Global $DriveLetter[10]
Global $NumSets = 1
Global $NoDocDisc = False

Global $Form_Status = GUICreate("IMGBurn AutoIt Script", 614, 163, 193, 124, BitOR($GUI_SS_DEFAULT_GUI, $WS_SIZEBOX, $WS_THICKFRAME))
Global $Label_Status = GUICtrlCreateLabel("", 16, 104, 580, 50, BitOR($SS_NOPREFIX, $SS_SUNKEN, $WS_BORDER))
Global $Label_Status_Title = GUICtrlCreateLabel("Status:", 16, 80, 37, 17, $SS_NOPREFIX)
Global $Label_NumSets_Title = GUICtrlCreateLabel("Total Number of Sets:", 424, 8, 107, 17, $SS_NOPREFIX)
Global $Label_NumSets = GUICtrlCreateLabel($NumSets, 568, 8, 26, 17, $SS_NOPREFIX)
Global $Label_Set_Title = GUICtrlCreateLabel("Current Set:", 424, 32, 60, 17, $SS_NOPREFIX)
Global $Label_Set = GUICtrlCreateLabel("1", 568, 32, 26, 17, $SS_NOPREFIX)
Global $Label_Title = GUICtrlCreateLabel("", 16, 24, 392, 17, $SS_NOPREFIX)
Global $Label_Job_Title = GUICtrlCreateLabel("Job:", 16, 8, 24, 17)
Global $Label_DiscsProcessed_Title = GUICtrlCreateLabel("Number of discs processed:", 424, 80, 135, 17, $SS_NOPREFIX)
Global $Label_DiscsProcessed = GUICtrlCreateLabel($NumDiscsProcessed, 568, 80, 26, 17)
Global $Label_Disc_Title = GUICtrlCreateLabel("Disc:", 424, 56, 28, 17, $SS_NOPREFIX)
Global $Label_Disc = GUICtrlCreateLabel("1", 456, 56, 28, 17, BitOR($SS_CENTER, $SS_NOPREFIX))
Global $Label_NumDiscss_Title = GUICtrlCreateLabel("of", 488, 56, 21, 17, BitOR($SS_CENTER, $SS_NOPREFIX))
Global $Label_NumDiscs = GUICtrlCreateLabel("", 512, 56, 39, 17, BitOR($SS_CENTER, $SS_NOPREFIX))

Global Const $MAX_NUMSETS = 50

Global Const $LOCATION_AUTO = 0
Global Const $LOCATION_RIGHT = 1
Global Const $LOCATION_LEFT = 2
Global Const $LOCATION_PRINTER = 100
Global Const $LOCATION_REJECT = 200

Global Const $CLEARDRIVE_NO = 0
Global Const $CLEARDRIVE_YES = 1

Global Const $DRIVE_OPEN = 0
Global Const $DRIVE_CLOSE = 1

; SET OUTPUT BINS HERE
Global Const $CD_BIN_INPUT = $LOCATION_LEFT
Global Const $DVD_BIN_INPUT = $LOCATION_RIGHT
Global Const $BIN_OUTPUT = $LOCATION_REJECT

Global Const $PQ_LOW = 0
Global Const $PQ_MED = 1
Global Const $PQ_BETTER = 2
Global Const $PQ_HIGH = 3
Global Const $PQ_BEST = 4

Global Const $PTACT_ALIGNPRINTER = 0x00000001
Global Const $PTACT_IGNOREINKLOW = 0x00000002
Global Const $PTACT_DISABLEPWRBUTTON = 0x00000004
Global Const $PTACT_REINIT_DRIVES = 0x00000008
Global Const $PTACT_IDENTIFY = 0x00000010
Global Const $PTACT_CANCELCMD = 0x00000020
Global Const $PTACT_ENABLEPWRBUTTON = 0x00000040
Global Const $PTACT_RESETSYSTEM = 0x00000080
Global Const $PTACT_CHECKDISCS = 0x00000100 ; Check number of discs in bins
Global Const $PTACT_CLEANCARTRIDGES = 0x00000200 ; Clean the cartridges
Global Const $PTACT_CALIBRATE_ONE_DISC = 0x00000400 ; SE, II, Pro: Calibrate for one disc (user must put one disc in each bin).
Global Const $PTACT_CHANGE_CARTRIDGE = 0x00000800 ; SE, II, Pro: Start the cartridge change procedure
Global Const $PTACT_END_CARTRIDGE_CHANGE = 0x00001000 ; SE: End the cartridge change (can close lid also)
Global Const $PTACT_SHIP_POSITION = 0x00002000 ; SE, II, Pro: Move the picker to the shipping position
Global Const $PTACT_RESET_LEFT_INK_LEVELS = 0x00004000 ; II: Clears the ink spits for the LEFT cartridge
Global Const $PTACT_RESET_RIGHT_INK_LEVELS = 0x00008000 ; II: Clears the ink spits for the RIGHT cartridge
Global Const $PTACT_ALLOW_NO_CARTRIDGES = 0x00010000 ; SE, II, Pro: Allows unit to operate non-printing robotics without a cartridge
Global Const $PTACT_XI_LIGHT_OFF = 0x00020000
Global Const $PTACT_XI_LIGHT_ON = 0x00040000
Global Const $PTACT_XI_LIGHT_FLASH = 0x00080000
Global Const $PTACT_UNHOOK_PICKER = 0x00100000
Global Const $PTACT_AUTOPRINTER_MODE = 0x00200000 ; DP4100: can perform a faster multiple copy print-only job by calling PTRobot_SetPrintCopies() prior to calling the print function (e.g. PTRobot_PrintFile()).
Global Const $PTACT_FAN_ON = 0x00400000 ; DP4100: turn on system fan
Global Const $PTACT_FAN_OFF = 0x00800000 ; DP4100: turn off system fan

Global Const $SYSERR_PTR_TRAY = 1
Global Const $SYSERR_CART_CODE = 2
Global Const $SYSERR_INPUT_EMPTY = 3
Global Const $SYSERR_PTR_COMM = 4
Global Const $SYSERR_CLR_EMPTY = 5
Global Const $SYSERR_BLK_EMPTY = 6
Global Const $SYSERR_BOTH_EMPTY = 7
Global Const $SYSERR_PICK = 8
Global Const $SYSERR_ARM_MOVE = 9
Global Const $SYSERR_CART_MOVE = 10
Global Const $SYSERR_INTERNAL_SW = 12
Global Const $SYSERR_NO_ROBODRIVES = 13
Global Const $SYSERR_OFFLINE = 14
Global Const $SYSERR_COVER_OPEN = 15
Global Const $SYSERR_PRINTER_PICK = 16
Global Const $SYSERR_MULTIPLE_PICK = 17
Global Const $SYSERR_MULTIPLEDISCS_IN_PRINTER = 18
Global Const $SYSERR_MULTIPLEDISCS_IN_RECORDER = 19
Global Const $SYSERR_DROPPED_DISC_RECORDER = 20
Global Const $SYSERR_DROPPED_DISC_BIN1 = 28
Global Const $SYSERR_DROPPED_DISC_BIN2 = 29
Global Const $SYSERR_DROPPED_DISC_PRINTER = 33
Global Const $SYSERR_DROPPED_DISC_REJECT = 34
Global Const $SYSERR_DROPPED_DISC_UNKNOWN = 35
Global Const $SYSERR_ALIGNNEEDED = 36
Global Const $SYSERR_COLOR_INVALID = 37
Global Const $SYSERR_BLACK_INVALID = 38
Global Const $SYSERR_BOTH_INVALID = 39
Global Const $SYSERR_NOCARTS = 40
Global Const $SYSERR_K_IN_CMY = 41
Global Const $SYSERR_CMY_IN_K = 42
Global Const $SYSERR_SWAPPED = 43
Global Const $SYSERR_PIGONPRO = 44
Global Const $SYSERR_ALIGNFAILED = 45
Global Const $SYSERR_DROPPED_DISC_PRINTER_FATAL = 46
Global Const $SYSERR_MULTIPLEDISCS_IN_RIGHTBIN = 47
Global Const $SYSERR_MULTIPLEDISCS_IN_LEFTBIN = 48
Global Const $SYSERR_CLR_EMPTY_FINAL = 49
Global Const $SYSERR_BLK_EMPTY_FINAL = 50
Global Const $SYSERR_BOTH_EMPTY_FINAL = 51
Global Const $SYSERR_WAITING_FOR_PRINTER = 52
Global Const $SYSERR_NO_DISC_IN_PRINTER = 53
Global Const $SYSERR_BUSY = 54
Global Const $SYSERR_PURGE = 55
Global Const $SYSERR_DOCK_SENSOR = 56
Global Const $SYSERR_ALREADY_PRINTED = 57
Global Const $SYSERR_UNKNOWN_HARDWARE = 58

Global Const $SYSSTATE_IDLE = 0
Global Const $SYSSTATE_BUSY = 1
Global Const $SYSSTATE_ERROR = 2

Global Const $ROBOT_DISCPUBLISHER = 0 ; Disc Publisher I
Global Const $ROBOT_DISCPUBLISHERII = 1 ; Disc Publisher II
Global Const $ROBOT_DISCPUBLISHERPRO = 2 ; Disc Publisher PRO
Global Const $ROBOT_COMPOSERMAX = 3 ; ComposerMAX
Global Const $ROBOT_RACKMOUNT_DPII = 4 ; Disc Publisher XR
Global Const $ROBOT_DISCPUBLISHER_XRP = 5 ; Disc Publisher XRP
Global Const $ROBOT_DISCPUBLISHER_SE = 6 ; Disc Publisher SE
Global Const $ROBOT_DISCPUBLISHERPRO_XI = 7 ; Disc Publisher Xi Series
Global Const $ROBOT_DISCPUBLISHER_4100 = 8 ; Disc Publisher 4100 Series

Global Const $PTROBOT_FEATURE_NOT_IMPLEMENTED = 520

Global Const $CUSTOM_FILE_NOT_FOUND = 901
Global Const $CUSTOM_FILE_FORMAT_WRONG = 902
Global Const $CUSTOM_INVALID_DRIVE_LETTER = 903

Func processJobs($Title, $Queue)

    Local $TimeStamp, $DupeTime, $DiscSet
    Local $QueueSize = UBound($Queue, 1)

    ; Store the time the process begins
    $TimeStamp = TimerInit()

    ; Exit script if ImgBurn is already running
    If ProcessExists("ImgBurn.exe") Then
        MsgBox(0, "IMGBurn AutoIt Script", "ImgBurn is already running.")
        Exit
    EndIf

    ; Configure GUI
    GUICtrlSetData($Label_Title, $Title)
    GUICtrlSetData($Label_NumSets, $NumSets)
    GUICtrlSetData($Label_NumDiscs, $QueueSize)

    GUISetState(@SW_SHOW, $Form_Status)

    ; Disable Screen Saver
    disableScreenSaver()

    PTRobot_Initialize()
    PTRobot_EnumRobots()
    PTRobot_EnumDrives()
    PTRobot_GetRobotInfo()

    ; Repeat process for each set of discs
    For $DiscSet = 1 To $NumSets

        ; Process the Queue
        PTRobot_ProcessQueue($Queue)
        ; Update GUI
        GUICtrlSetData($Label_Set, $DiscSet)

    Next

    PTRobot_Destroy()

    ; Enable Screen Saver
    enableScreenSaver()

    ; Calculate how long the process took
    $DupeTime = CalcDupeTime(TimerDiff($TimeStamp))

    sendEmail($Title, $Queue, $NumSets, $DupeTime)

EndFunc   ;==>processJobs

Func getNumSets()

    Local $nNumSets

    ; Check command line
    If $CmdLine[0] > 0 Then
        If $CmdLine[1] < 1 Or $CmdLine[1] > $MAX_NUMSETS Then
            MsgBox(0, "IMGBurn AutoIt Script", "Number of jobs must be between 1 and " & $MAX_NUMSETS & ".")
            Exit
        Else
            $nNumSets = $CmdLine[1]
        EndIf
    Else
        ; Return -1 if no command line argument given
        $nNumSets = -1
    EndIf

    Return $nNumSets

EndFunc   ;==>getNumSets

Func PTRobot_ProcessQueue($Queue)
    Local $QueueSize = UBound($Queue, 1)
    Local $Disc, $SourceBin
    Local $MissingFiles, $WrongFileFormat
    Local $result
    Local Const $LABEL_COL = 0 ; Array column used for disc labels.
    Local Const $IMAGE_COL = 1 ; Array column used for disc image files.

    ; Verify files exist.
    For $Disc = 0 To $QueueSize - 1
        If Not $Queue[$Disc][$LABEL_COL] = "" Then
            If Not FileExists($Queue[$Disc][$LABEL_COL]) Then
                $MissingFiles = $MissingFiles & $Queue[$Disc][$LABEL_COL] & @CRLF
            ElseIf Not _ValidateImageFile($Queue[$Disc][$LABEL_COL]) Then
                $WrongFileFormat = $WrongFileFormat & $Queue[$Disc][$LABEL_COL] & @CRLF
            EndIf
        EndIf
        If Not FileExists($Queue[$Disc][$IMAGE_COL]) Then
            $MissingFiles = $MissingFiles & $Queue[$Disc][$IMAGE_COL] & @CRLF
        EndIf
    Next

    If Not $MissingFiles = "" Then
        MsgBox(48, "Queue Error", "Files not found:" & @CRLF & $MissingFiles)
        PTRobot_Destroy()
        Exit ($CUSTOM_FILE_NOT_FOUND)
    EndIf

    If Not $WrongFileFormat = "" Then
        MsgBox(48, "Queue Error", "File format incorrect:" & @CRLF & $WrongFileFormat)
        PTRobot_Destroy()
        Exit ($CUSTOM_FILE_FORMAT_WRONG)
    EndIf

    PTRobot_Wait()

    For $Disc = 0 To $QueueSize - 1

        If StringInStr($Queue[$Disc][$LABEL_COL], "DVD", 1) Or StringInStr($Queue[$Disc][$IMAGE_COL], "DVD", 1) Then
            $SourceBin = $DVD_BIN_INPUT
        Else
            $SourceBin = $CD_BIN_INPUT
        EndIf

        GUICtrlSetData($Label_Disc, $Disc + 1) ; Update GUI

        WriteStatus("Loading drive..." & @CRLF)
        If $NumDiscsProcessed < $NumDrives Then ; First disc load in drive should check for disc in burner.
            PTRobot_LoadDrive(Mod($NumDiscsProcessed, $NumDrives), $SourceBin, $CLEARDRIVE_YES) ; Load first disc from source bin.
        Else
            PTRobot_LoadDrive(Mod($NumDiscsProcessed, $NumDrives), $SourceBin, $CLEARDRIVE_NO) ; Load subsequent disc from source bin.
        EndIf

        PTRobot_Wait()

        WriteStatus("Starting disc writer..." & @CRLF)
        $result = PTRobot_BurnImage(Mod($NumDiscsProcessed, $NumDrives), $Queue[$Disc][$IMAGE_COL]) ; Burn image file to disc.

        ; If disc was aborted, skip to next disc.
        If $result = 1 Then
            WriteStatus("Skipping " & $Queue[$Disc][$IMAGE_COL] & "..." & @CRLF)
            $NumDiscsProcessed = $NumDiscsProcessed + 1 ; Increment global counter.
            GUICtrlSetData($Label_DiscsProcessed, $NumDiscsProcessed)

            ContinueLoop
        EndIf

        PTRobot_Wait()

        If $Queue[$Disc][$LABEL_COL] = "" Or $NumPrinters < 1 Then
            WriteStatus("Unloading drive..." & @CRLF)
            PTRobot_UnLoadDrive(Mod($NumDiscsProcessed, $NumDrives), $BIN_OUTPUT) ; Unload disc to output bin.
        Else
            ; Check if burn was ignored.
            If $result = 2 Then
                $result = MsgBox(36, "Print...", "Print disc?" & @CRLF)
                ; No
                If $result = 7 Then
                    PTRobot_UnLoadDrive(Mod($NumDiscsProcessed, $NumDrives), $BIN_OUTPUT) ; Unload disc to output bin.
                    PTRobot_Wait()
                    $NumDiscsProcessed = $NumDiscsProcessed + 1 ; Increment global counter.
                    GUICtrlSetData($Label_DiscsProcessed, $NumDiscsProcessed)
                    ContinueLoop
                EndIf
            EndIf

            WriteStatus("Loading printer..." & @CRLF)
            PTRobot_LoadPrinterFromDrive(Mod($NumDiscsProcessed, $NumDrives)) ; Send disc to printer.

            PTRobot_Wait()

            WriteStatus("Printing label..." & @CRLF)
            PTRobot_PrintFile($Queue[$Disc][$LABEL_COL]) ; Print label.

            PTRobot_Wait()

            WriteStatus("Unloading printer..." & @CRLF)
            PTRobot_UnLoadPrinter($BIN_OUTPUT) ; Unload disc to output bin.
        EndIf

        $NumDiscsProcessed = $NumDiscsProcessed + 1 ; Increment global counter.
        GUICtrlSetData($Label_DiscsProcessed, $NumDiscsProcessed)

        PTRobot_Wait()
    Next
EndFunc   ;==>PTRobot_ProcessQueue

Func PTRobot_BurnImage($num, $Filename)
    Local $ErrorLevel
    Local $result = 0

    While 1 ; Infinte loop for "Retry" on failure.
        $ErrorLevel = RunWait('"C:\Program Files (x86)\ImgBurn\ImgBurn.exe" /MODE WRITE /SRC "' & $Filename & '" /DEST ' & $DriveLetter[$num] & ': /COPIES 1 /VERIFY NO /SPEED AUTO /TESTMODE NO /DELETEIMAGE NO /EJECT YES /WAITFORMEDIA /START /CLOSE /NOSAVESETTINGS')

        If $ErrorLevel Then
            PTRobot_OpenCloseDrive($num, $DRIVE_OPEN)
            WriteStatusError("Error: Disc write failed! - Exit Code: " & $ErrorLevel & @CRLF)
            WriteStatusError("Error: " & $Filename & " not successfully written!" & @CRLF)
            $result = MsgBox(50, "Burn Error", "Disc write failed!" & @CRLF & "Exit Code: " & $ErrorLevel)
            ; Abort
            If $result = 3 Then
                WriteStatus("Rejecting disc..." & @CRLF)
                PTRobot_UnLoadDrive($num, $LOCATION_REJECT) ; Send to reject pile.
                $result = MsgBox(36, "Burn Error", "Close program?" & @CRLF)
                PTRobot_Wait()
                ; Yes
                If $result = 6 Then
                    PTRobot_Destroy()
                    Exit ($ErrorLevel)
                    ; No
                Else
                    $result = 1 ; Return value for aborted error.
                    ExitLoop
                EndIf
                ; Retry
            ElseIf $result = 4 Then
                PTRobot_OpenCloseDrive($num, $DRIVE_CLOSE)
                ; Ignore
            ElseIf $result = 5 Then
                $result = 2 ; Return value for ignored error.
                ExitLoop
            EndIf
            ; Success
        Else
            WriteStatus("File: " & $Filename & " successfully written." & @CRLF)
            ExitLoop
        EndIf
    WEnd

    Return $result
EndFunc   ;==>PTRobot_BurnImage

Func PTRobot_Wait()
    Local $ErrorMsg

    Sleep(1000)
    While Not PTRobot_GetRobotStatus()
        If $SystemError Then
            PTRobot_ErrorMsgBox($SystemError)
        ElseIf Not $SystemState Then
            ExitLoop
        Else
            Sleep(1000)
        EndIf
    WEnd

    Sleep(1000)
    While Not PTRobot_GetRobotStatus()
        If $SystemError Then
            If $SystemError = $SYSERR_CLR_EMPTY Then
                $ErrorMsg = "WARNING:  The color cartridge is LOW on ink."
                WriteStatusError("Error: " & $ErrorMsg & @CRLF)
                PTRobot_KillSystemError(0)
                Sleep(1000)
            ElseIf $SystemError = $SYSERR_BLK_EMPTY Then
                $ErrorMsg = "WARNING:  The black cartridge is LOW on ink."
                WriteStatusError("Error: " & $ErrorMsg & @CRLF)
                PTRobot_KillSystemError(0)
                Sleep(1000)
            ElseIf $SystemError = $SYSERR_BOTH_EMPTY Then
                $ErrorMsg = "WARNING:  Both ink cartridges are LOW on ink."
                WriteStatusError("Error: " & $ErrorMsg & @CRLF)
                PTRobot_KillSystemError(0)
                Sleep(1000)
            Else
                PTRobot_ErrorMsgBox($SystemError)
            EndIf
        ElseIf Not $SystemState Then
            ExitLoop
        Else
            Sleep(1000)
        EndIf
    WEnd




EndFunc   ;==>PTRobot_Wait

Func PTRobot_GetErrorString()
    Local $result
    Local $err = @error

    Local $dwErrorNum
    Local $wszErrorString
    Local $dwMaxLength
    Local $dwLanguage

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetErrorString", "hwnd", $RobotAddress, "dword", $dwErrorNum, "wstr", $wszErrorString, "dword", $dwMaxLength, "dword", $dwLanguage)

    $dwErrorNum = $result[2]
    $wszErrorString = $result[3]
    $dwMaxLength = $result[4]
    $dwLanguage = $result[5]

    MsgBox(0, "", "Status: " & $result[0] & @CRLF & _
            "System Number Error: " & $dwErrorNum & @CRLF & _
            "Error String: " & $wszErrorString & @CRLF & _
            "Max Length: " & $dwMaxLength & @CRLF & _
            "Language: " & $dwLanguage)

    Return $result[0]
EndFunc   ;==>PTRobot_GetErrorString

Func PTRobot_Initialize()
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_Initialize")

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf

EndFunc   ;==>PTRobot_Initialize

Func PTRobot_Destroy()
    Local $result
    Local $err = @error

    PTRobot_Wait()
    DllCall($hPTROBOTDLL, "int", "PTRobot_SystemAction", "hwnd", $RobotAddress, "dword", $PTACT_XI_LIGHT_OFF)
    PTRobot_Wait()

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_Destroy")

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        Exit ($result[0])
    EndIf

EndFunc   ;==>PTRobot_Destroy

Func PTRobot_EnumRobots()
    Local $result
    Local $err = @error

    Local $hRobots = DllStructCreate("hwnd[10]")
    Local $dwNumRobots = DllStructCreate("dword")
    DllStructSetData($dwNumRobots, 1, 10) ; Set input to be the size of the array

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_EnumRobots", "ptr", DllStructGetPtr($hRobots), "ptr", DllStructGetPtr($dwNumRobots))

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf

    ; Set Global Variables
    $RobotAddress = DllStructGetData($hRobots, 1, 1)
    $NumRobots = DllStructGetData($dwNumRobots, 1)
EndFunc   ;==>PTRobot_EnumRobots

Func PTRobot_EnumDrives()
    Local $result
    Local $err = @error

    Local $hDrives = DllStructCreate("hwnd[10]")
    Local $dwNumDrives = DllStructCreate("dword")
    DllStructSetData($dwNumDrives, 1, 10) ; Set input to be the size of the array

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_EnumDrives", "hwnd", $RobotAddress, "ptr", DllStructGetPtr($hDrives), "ptr", DllStructGetPtr($dwNumDrives))

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf

    ; Set Global Variables
    $NumDrives = DllStructGetData($dwNumDrives, 1)
    ReDim $DriveAddress[$NumDrives]
    ReDim $DriveLetter[$NumDrives]
    For $i = 0 To $NumDrives - 1
        $DriveAddress[$i] = DllStructGetData($hDrives, 1, $i + 1)
        $DriveLetter[$i] = Chr(Dec(StringRight($DriveAddress[$i], 2)))
        ;MsgBox(0, "Debug", "Drive " & $i & ": " & $DriveLetter[$i])
        If Not StringRegExp($DriveLetter[$i], "^[A-Z]$") Then
            PTRobot_ErrorMsgBox($CUSTOM_INVALID_DRIVE_LETTER)
            PTRobot_Destroy()
            Exit ($CUSTOM_INVALID_DRIVE_LETTER)
        EndIf
    Next

EndFunc   ;==>PTRobot_EnumDrives

Func PTRobot_GetRobotStatus()
    Local $result
    Local $err = @error

    Local $dRobotStatus = DllStructCreate("dword dwSystemState;dword dwSystemError;dword dwCurrColorSpits;dword dwCurrBlackSpits;dword dwFullColorSpits;dword dwFullBlackSpits")

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetRobotStatus", "hwnd", $RobotAddress, "ptr", DllStructGetPtr($dRobotStatus))

    ;MsgBox(0, "", "Status: "         & $result[0]                                          & @CRLF & _
    ;              "System State: "   & DllStructGetData($dRobotStatus, "dwSystemState")    & @CRLF & _
    ;              "System Error: "   & DllStructGetData($dRobotStatus, "dwSystemError")    & @CRLF & _
    ;              "CurrColorSpits: " & DllStructGetData($dRobotStatus, "dwCurrColorSpits") & @CRLF & _
    ;              "CurrBlackSpits: " & DllStructGetData($dRobotStatus, "dwCurrBlackSpits") & @CRLF & _
    ;              "FullColorSpits: " & DllStructGetData($dRobotStatus, "dwFullColorSpits") & @CRLF & _
    ;              "FullBlackSpits: " & DllStructGetData($dRobotStatus, "dwFullBlackSpits"));

    ; Set Global Variables
    $SystemState = DllStructGetData($dRobotStatus, "dwSystemState")
    $SystemError = DllStructGetData($dRobotStatus, "dwSystemError")

    Return $result[0]
EndFunc   ;==>PTRobot_GetRobotStatus

Func PTRobot_GetRobotInfo()
    Local $result
    Local $err = @error

    Local $dRobotInfo = DllStructCreate("hwnd hRobot;char tszRobotDesc[100];dword dwRobotType;dword dwNumDrives;dword dwNumPrinters;dword dwNumBins;dword dwDriveColumns;dword dwDriveRows;char tszRobotFirmware[20];dword dwOptions;dword dwAction;hwnd hDrives[10];dword dwDriveBusType")

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetRobotInfo", "hwnd", $RobotAddress, "ptr", DllStructGetPtr($dRobotInfo))

    ;MsgBox(0, "", "Status: "                  & $result[0]                                        & @CRLF & _
    ;              "Robot: "                   & DllStructGetData($dRobotInfo, "hRobot")           & @CRLF & _
    ;              "Robot Type: "              & DllStructGetData($dRobotInfo, "tszRobotDesc")     & @CRLF & _
    ;              "Number of Drives: "        & DllStructGetData($dRobotInfo, "dwNumDrives")      & @CRLF & _
    ;              "Number of Printers: "      & DllStructGetData($dRobotInfo, "dwNumPrinters")    & @CRLF & _
    ;              "Number of Bins: "          & DllStructGetData($dRobotInfo, "dwNumBins")        & @CRLF & _
    ;              "Number of Drive Columns: " & DllStructGetData($dRobotInfo, "dwDriveColumns")   & @CRLF & _
    ;              "Number of Drive Rows: "    & DllStructGetData($dRobotInfo, "dwDriveRows")      & @CRLF & _
    ;              "Robot FW Version: "        & DllStructGetData($dRobotInfo, "tszRobotFirmware") & @CRLF & _
    ;              "Robot Options: "           & DllStructGetData($dRobotInfo, "dwOptions")        & @CRLF & _
    ;              "Robot Actions: "           & DllStructGetData($dRobotInfo, "dwAction")         & @CRLF & _
    ;              "Drive Bus Type: "          & DllStructGetData($dRobotInfo, "dwDriveBusType"))

    $RobotType = DllStructGetData($dRobotInfo, "tszRobotDesc")
    $NumDrives = DllStructGetData($dRobotInfo, "dwNumDrives")
    $NumPrinters = DllStructGetData($dRobotInfo, "dwNumPrinters")
    $NumBins = DllStructGetData($dRobotInfo, "dwNumBins")

    Return $result[0]
EndFunc   ;==>PTRobot_GetRobotInfo

Func PTRobot_GetDriveInfo($num)
    Local $result
    Local $err = @error

    Local $dDrvInfo = DllStructCreate("hwnd hDrive;char tszDriveName[132];char tszFirmwareVer[40];char tszSerialNum[40];hwnd hRobot;dword dwDriveColumn;dword dwDriveRow")

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetDriveInfo", "hwnd", $DriveAddress[$num], "ptr", DllStructGetPtr($dDrvInfo))

    ;MsgBox(0, "", "Status: "           & $result[0] & @CRLF & _
    ;              "Drive: "            & DllStructGetData($dDrvInfo, "hDrive")         & @CRLF & _
    ;              "Drive Name: "       & DllStructGetData($dDrvInfo, "tszDriveName")   & @CRLF & _
    ;              "Firmware Version: " & DllStructGetData($dDrvInfo, "tszFirmwareVer") & @CRLF & _
    ;              "Serial Number: "    & DllStructGetData($dDrvInfo, "tszSerialNum")   & @CRLF & _
    ;              "Robot: "            & DllStructGetData($dDrvInfo, "hRobot")         & @CRLF & _
    ;              "Drive Column: "     & DllStructGetData($dDrvInfo, "dwDriveColumn")  & @CRLF & _
    ;              "Drive Row: "        & DllStructGetData($dDrvInfo, "dwDriveRow"))

    Return $result[0]
EndFunc   ;==>PTRobot_GetDriveInfo

Func PTRobot_LoadDrive($num, $dwFromLocation, $dwFirstDiscLoad)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_LoadDrive", "hwnd", $RobotAddress, "hwnd", $DriveAddress[$num], "dword", $dwFromLocation, "dword", $dwFirstDiscLoad)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_LoadDrive

Func PTRobot_LoadPrinter($dwFromLocation)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_LoadPrinter", "hwnd", $RobotAddress, "dword", $dwFromLocation)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_LoadPrinter

Func PTRobot_LoadPrinterFromDrive($num)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_LoadPrinterFromDrive", "hwnd", $RobotAddress, "hwnd", $DriveAddress[$num])

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_LoadPrinterFromDrive

Func PTRobot_PrintFile($strFilename)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_PrintFile", "hwnd", $RobotAddress, "str", $strFilename, "dword", 0)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit
    EndIf
EndFunc   ;==>PTRobot_PrintFile

Func PTRobot_UnLoadPrinter($dwToLocation)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_UnLoadPrinter", "hwnd", $RobotAddress, "dword", $dwToLocation)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_UnLoadPrinter

Func PTRobot_UnLoadDrive($num, $dwToLocation)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_UnLoadDrive", "hwnd", $RobotAddress, "hwnd", $DriveAddress[$num], "dword", $dwToLocation)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_UnLoadDrive

Func PTRobot_MoveDiscBetweenLocations($dwFromLocation, $dwToLocation)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_MoveDiscBetweenLocations", "hwnd", $RobotAddress, "dword", $dwFromLocation, "dword", $dwToLocation)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_MoveDiscBetweenLocations

Func PTRobot_OpenCloseDrive($num, $dwOpen)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_OpenCloseDrive", "hwnd", $DriveAddress[$num], "dword", $dwOpen)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_OpenCloseDrive

Func PTRobot_SystemAction($dwAction)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_SystemAction", "hwnd", $RobotAddress, "dword", $dwAction)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_SystemAction

Func PTRobot_KillSystemError($dwResetPrinter)
    Local $result
    Local $err = @error

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_KillSystemError", "hwnd", $RobotAddress, "dword", $dwResetPrinter)

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf
EndFunc   ;==>PTRobot_KillSystemError

Func PTRobot_GetManufactureInfo() ; WARNING: This function has not been thoroughly tested.
    Local $result
    Local $err = @error

    Local $dPTManufactureInfo = DllStructCreate("hwnd hRobot;char tszSerialNum[11];char tszManufactureDate[12];dword dwFiller[20]")

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetManufactureInfo", "hwnd", $RobotAddress, "ptr", DllStructGetPtr($dPTManufactureInfo))

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf

    MsgBox(0, "", "Serial Number: " & DllStructGetData($dPTManufactureInfo, "tszSerialNum") & @CRLF & _
            "Manufacture Date: " & DllStructGetData($dPTManufactureInfo, "tszManufactureDate") & @CRLF & _
            "Filler: " & DllStructGetData($dPTManufactureInfo, "dwFiller", 1))

    Return $result[0]
EndFunc   ;==>PTRobot_GetManufactureInfo

Func PTRobot_GetPrinterSettings()
    Local $result
    Local $err = @error

    Local $dPrinterSettings = DllStructCreate("dword dwPrintQuality;dword dwInnerDiam;dword dwOuterMargin")
    Local $PrintQuality

    $result = DllCall($hPTROBOTDLL, "int", "PTRobot_GetPrinterSettings", "hwnd", $RobotAddress, "ptr", DllStructGetPtr($dPrinterSettings))

    If $result[0] Then
        PTRobot_ErrorMsgBox($result[0])
        PTRobot_Destroy()
        Exit ($result[0])
    EndIf

    Switch DllStructGetData($dPrinterSettings, "dwPrintQuality")
        Case $PQ_LOW
            $PrintQuality = "Low"
        Case $PQ_MED
            $PrintQuality = "Medium"
        Case $PQ_BETTER
            $PrintQuality = "Better" ; Default
        Case $PQ_HIGH
            $PrintQuality = "High"
        Case $PQ_BEST
            $PrintQuality = "Best"
        Case Else ; This shouldn't happen.
            $PrintQuality = "UNKNOWN"
    EndSwitch

    MsgBox(0, "", "Print Quality: " & $PrintQuality & @CRLF & _
            "Inner Diameter: " & DllStructGetData($dPrinterSettings, "dwInnerDiam") & @CRLF & _
            "Outer Margin: " & DllStructGetData($dPrinterSettings, "dwOuterMargin"))

EndFunc   ;==>PTRobot_GetPrinterSettings

Func PTRobot_ErrorMsgBox($ErrorNum)
    Local $ErrorMsg

    Switch $ErrorNum
        Case $SYSERR_PTR_TRAY
            $ErrorMsg = "Tray movement error.  Press the left button on the unit to try again."
        Case $SYSERR_CART_CODE
            $ErrorMsg = "There was a problem finding the ink cartridges.  Open the cover and press the left button. Make sure the color cartridge is installed on the left and the black is on the right.  Then close the cover."
        Case $SYSERR_INPUT_EMPTY
            $ErrorMsg = "The input bin is empty.  Open the cover and add more discs.  Then close the cover and push the left button on the unit."
        Case $SYSERR_PTR_COMM
            $ErrorMsg = "There was an internal printer communications error.  Press the left button on the unit to try again."
        Case $SYSERR_CLR_EMPTY
            $ErrorMsg = "WARNING:  The color cartridge is LOW on ink.  To replace the cartridge, open the cover on the unit and press the left button.  Then install the new cartridge and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_BLK_EMPTY
            $ErrorMsg = "WARNING:  The black cartridge is LOW on ink.  To replace the cartridge, open the cover on the unit and press the left button.  Then install the new cartridge and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_BOTH_EMPTY
            $ErrorMsg = "WARNING:  Both ink cartridges are LOW on ink.  To replace the cartridges, open the cover on the unit and press the left button.  Then install the new cartridges and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_PICK
            $ErrorMsg = "The disc was not picked.  Press the left button on the unit to try again."
        Case $SYSERR_ARM_MOVE
            $ErrorMsg = "There was an arm movement error.  Press the left button on the unit to try again."
        Case $SYSERR_CART_MOVE
            $ErrorMsg = "Arm picker error.  Press the left button on the unit to try again."
        Case $SYSERR_INTERNAL_SW
            $ErrorMsg = "There was an internal software error.  Please re-start the software."
        Case $SYSERR_NO_ROBODRIVES
            $ErrorMsg = "No external recorder drives were found.  Re-power the computer and unit, and then re-start the software."
        Case $SYSERR_OFFLINE
            $ErrorMsg = "The unit is offline.  Please ensure the unit is connected and powered on.  You may need to shut down and restart the software."
        Case $SYSERR_COVER_OPEN
            $ErrorMsg = "The unit’s cover is open.  Please close the cover."
        Case $SYSERR_PRINTER_PICK
            $ErrorMsg = "The disc was not picked from the printer.  Press the left button to retry."
        Case $SYSERR_MULTIPLE_PICK
            $ErrorMsg = "Multiple discs were picked up and moved.  Please manually remove any extra discs that were moved, keeping a single disc in place.  Then close the cover and press the left button."
        Case $SYSERR_MULTIPLEDISCS_IN_PRINTER
            $ErrorMsg = "Multiple discs were placed in the printer.  Please manually remove any extra discs from the printer, keeping a single disc in place.  Then close the cover and press the left button."
        Case $SYSERR_MULTIPLEDISCS_IN_RECORDER
            $ErrorMsg = "Multiple discs were placed in the recorder.  Please manually remove any extra discs from the recorder, keeping a single disc in place.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_RECORDER
            $ErrorMsg = "The disc was dropped while moving into the recorder.  Please manually place the disc into the recorder tray.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_BIN1
            $ErrorMsg = "The disc was dropped while moving into the right bin.  Please manually place the disc into the right bin.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_BIN2
            $ErrorMsg = "The disc was dropped while moving into the left bin.  Please manually place the disc into the left bin.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_PRINTER
            $ErrorMsg = "The disc was dropped while moving into the printer.  Please manually place the disc into the printer tray.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_REJECT
            $ErrorMsg = "The disc was dropped while moving to the reject area.  Please remove the dropped disc.  Then close the cover and press the left button."
        Case $SYSERR_DROPPED_DISC_UNKNOWN
            $ErrorMsg = "The disc was dropped. Please remove the dropped disc.  Then close the cover and press the left button."
        Case $SYSERR_ALIGNNEEDED
            $ErrorMsg = "The printer cartridges need to be aligned."
        Case $SYSERR_COLOR_INVALID
            $ErrorMsg = "The color cartridge is invalid.  Open the cover and press the left button.  Change the cartridge and close the cover."
        Case $SYSERR_BLACK_INVALID
            $ErrorMsg = "The black cartridge is invalid.  Open the cover and press the left button.  Change the cartridge and close the cover."
        Case $SYSERR_BOTH_INVALID
            $ErrorMsg = "Both cartridges are invalid.  Open the cover and press the left button.  Change the cartridges and close the cover."
        Case $SYSERR_NOCARTS
            $ErrorMsg = "No cartridges are installed.  Open the cover and press the left button.  Install the cartridges and close the cover."
        Case $SYSERR_K_IN_CMY
            $ErrorMsg = "The black cartridge is installed in the color position.  Open the cover and press the left button.  Change the cartridge and close the cover."
        Case $SYSERR_CMY_IN_K
            $ErrorMsg = "The color cartridge is installed in the black position.  Open the cover and press the left button.  Change the cartridge and close the cover."
        Case $SYSERR_SWAPPED
            $ErrorMsg = "The black and color cartridges are swapped.  Open the cover and press the left button.  Swap the cartridges and close the cover."
        Case $SYSERR_PIGONPRO
            $ErrorMsg = "This printer is not compatible with a pigment-based black cartridge.  Open the cover and press the left button.  Install a dye-based black cartridge and close the cover."
        Case $SYSERR_ALIGNFAILED
            $ErrorMsg = "The alignment print failed."
        Case $SYSERR_DROPPED_DISC_PRINTER_FATAL
            $ErrorMsg = "The disc was dropped while moving to/from the printer.  Please open the cover and manually remove and discard the disc.  Then place a new disc in the recorder, close the cover and press the left button."
        Case $SYSERR_MULTIPLEDISCS_IN_RIGHTBIN
            $ErrorMsg = "Multiple discs were placed in the right bin.  Please manually move any extra discs to the left bin, keeping a single disc in place.  Then close the cover and press the left button."
        Case $SYSERR_MULTIPLEDISCS_IN_LEFTBIN
            $ErrorMsg = "Multiple discs were placed in the left bin.  Please manually move any extra discs to the right bin, keeping a single disc in place.  Then close the cover and press the left button."
        Case $SYSERR_CLR_EMPTY_FINAL
            $ErrorMsg = "WARNING:  The color cartridge is Empty.  To replace the cartridge, open the cover on the unit and press the left button.  Then install the new cartridge and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_BLK_EMPTY_FINAL
            $ErrorMsg = "WARNING:  The black cartridge is Empty.  To replace the cartridge, open the cover on the unit and press the left button.  Then install the new cartridge and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_BOTH_EMPTY_FINAL
            $ErrorMsg = "WARNING:  Both cartridges are Empty.  To replace the cartridge, open the cover on the unit and press the left button.  Then install the new cartridge and close the cover.  To ignore the warning, press the left button."
        Case $SYSERR_WAITING_FOR_PRINTER
            $ErrorMsg = "The system timed out waiting for the printer to finish.  The disc may not have been printed on."
        Case $SYSERR_NO_DISC_IN_PRINTER
            $ErrorMsg = "No disc was found in the printer."
        Case $SYSERR_BUSY
            $ErrorMsg = "System Busy."
        Case $SYSERR_PURGE
            $ErrorMsg = "Purge."
        Case $SYSERR_DOCK_SENSOR
            $ErrorMsg = "Dock Sensor."
        Case $SYSERR_ALREADY_PRINTED
            $ErrorMsg = "Already printed."
        Case $SYSERR_UNKNOWN_HARDWARE
            $ErrorMsg = "Unknown hardware."
        Case $PTROBOT_FEATURE_NOT_IMPLEMENTED
            $ErrorMsg = "This feature is not implemented."
        Case $CUSTOM_INVALID_DRIVE_LETTER
            $ErrorMsg = "Could not find valid drive letter for burner."
        Case Else
            $ErrorMsg = "UNKNOWN ERROR! " & $ErrorNum
    EndSwitch

    WriteStatusError("Error: " & $ErrorMsg & @CRLF)
    MsgBox(0, "Error", $ErrorMsg)
EndFunc   ;==>PTRobot_ErrorMsgBox

Func _ValidateImageFile($sFilename)
    Local Const $sSTDHeader = "4D5600FF0C001200"
    Local Const $sPNGHeader = "89504E470D0A1A0A"
    Local Const $sJPGHeader = "FFD8FFE000104A46"
    Local Const $sGIF87Header = "474946383961"
    Local Const $sGIF89Header = "474946383761"
    Local Const $sBMPHeader = "424D"
    Local $sFileType, $bReturn = False

    Local $hFileHandle = FileOpen($sFilename, 16) ; Open file read only
    Local $bFileContents = FileRead($hFileHandle, 8) ; Read first 8 bytes
    Local $sFileExt = StringLower(StringRight($sFilename, 4)) ; Determine file extension

    If StringMid($bFileContents, 3, StringLen($sJPGHeader)) = $sJPGHeader And $sFileExt = ".jpg" Then
        $sFileType = "JPEG"
        $bReturn = True
    ElseIf StringMid($bFileContents, 3, StringLen($sSTDHeader)) = $sSTDHeader And $sFileExt = ".std" Then
        $sFileType = "SureThing Label"
        $bReturn = True
    ElseIf StringMid($bFileContents, 3, StringLen($sPNGHeader)) = $sPNGHeader And $sFileExt = ".png" Then
        $sFileType = "PNG"
        $bReturn = True
    ElseIf StringMid($bFileContents, 3, StringLen($sBMPHeader)) = $sBMPHeader And $sFileExt = ".bmp" Then
        $sFileType = "BMP"
        $bReturn = True
        ;ElseIf StringMid($bFileContents, 3, StringLen($sGIF87Header)) = $sGIF87Header And $sFileExt = ".gif" Then
        ;  $sFileType = "GIF87"
        ;  $bReturn = True
        ;ElseIf StringMid($bFileContents, 3, StringLen($sGIF89Header)) = $sGIF89Header And $sFileExt = ".gif" Then
        ;  $sFileType = "GIF89"
        ;  $bReturn = True
    Else
        $sFileType = "Unknown"
    EndIf

    ;MsgBox(0, "", $sFileType)
    Return $bReturn
EndFunc   ;==>_ValidateImageFile

Func _NoHaltMsgBox($code = 0, $Title = "", $text = "", $timeout = 0)
    Run(@AutoItExe & ' /AutoIt3ExecuteLine  "MsgBox(' & $code & ', ''' & $Title & ''', ''' & $text & ''',' & $timeout & ')"')
EndFunc   ;==>_NoHaltMsgBox

Func OpenPrompt($message, $Queue)

    Local $answer

    ; Get number of sets from the command-line
    $answer = getNumSets()

    ; If copies were specified on command line, then end function without prompting
    If $answer > 0 Then
        $NumSets = $answer
        Return $Queue
    EndIf

    Local $QueueSize = UBound($Queue, 1)

    Local $Form_Duplicator_GUI = GUICreate("IMGBurn AutoIt Script", 404, 260, 192, 124)
    Local $Button_Start = GUICtrlCreateButton("Start", 72, 210, 87, 33)
    Local $NumCopies = GUICtrlCreateInput($NumSets, 220, 170, 49, 21, $ES_CENTER)
    Local $Updown_NumCopies = GUICtrlCreateUpdown($NumCopies)
    GUICtrlSetLimit(-1, $MAX_NUMSETS, 1)
    Local $Button_Cancel = GUICtrlCreateButton("Cancel", 232, 210, 91, 33)
    Local $Label_NumCopies = GUICtrlCreateLabel("How many copies?", 116, 170, 94, 17, BitOR($SS_RIGHT, $SS_CENTERIMAGE))
    Local $Label_Message = GUICtrlCreateLabel("This script will run the disc burning software for the job" & @LF & $message & @LF & "Run?", 2, 8, 400, 57, BitOR($SS_NOPREFIX, $SS_CENTER))
    Local $List_Queue = GUICtrlCreateList("", 2, 60, 400, 100, BitOR($LBS_MULTIPLESEL, $WS_HSCROLL, $WS_VSCROLL, $WS_BORDER))
    GUICtrlSetLimit(-1, 200) ; Limit horizontal scrolling
    Local $DiscTitle
    For $i = 0 To $QueueSize - 1
        $DiscTitle = StringRegExp($Queue[$i][0], '\\([^\\]*)\.std$', 1)
        GUICtrlSetData(-1, $DiscTitle[0])
        ;_GUICtrlListBox_SetSel($List_Queue, $i) ; Preselect entry
        GUICtrlSendMsg(-1, $LB_SETSEL, True, $i) ; Preselect entry
    Next
    GUICtrlSendMsg(-1, $LB_SETTOPINDEX, 0, 0) ; Send scrollbar to top of list
    GUISetState(@SW_SHOW)

    Local $NewQueue

    While 1
        $answer = GUIGetMsg()
        $NewQueue = $Queue ; Set/Reset NewQueue to Queue

        Switch $answer
            Case $Button_Start
                If GUICtrlRead($NumCopies) < 1 Or GUICtrlRead($NumCopies) > $MAX_NUMSETS Then
                    MsgBox(0, "Error", "Number of copies must be between 1 and " & $MAX_NUMSETS & ".")
                Else
                    For $i = $QueueSize - 1 To 0 Step -1 ; Process array in reverse to keep index aligned
                        If _GUICtrlListBox_GetSel($List_Queue, $i) == False Then
                            _ArrayDelete($NewQueue, $i)
                        EndIf
                    Next

                    If UBound($NewQueue, 1) = 0 Then
                        MsgBox(0, "Error", "No discs selected.")
                    Else
                        ExitLoop ; Success!
                    EndIf
                EndIf
            Case $Button_Cancel
                MsgBox(0, "IMGBurn AutoIt Script", "OK.  Bye!")
                Exit
            Case $GUI_EVENT_CLOSE
                Exit
        EndSwitch
    WEnd

    $NumSets = GUICtrlRead($NumCopies)

    GUISetState(@SW_HIDE)

    Return $NewQueue
EndFunc   ;==>OpenPrompt

Func CalcDupeTime($iTimestampDiff)
    Local $iSeconds = Int($iTimestampDiff / 1000)
    Local $iHours, $iMins, $iSecs

    $iHours = Int($iSeconds / 3600)
    $iSeconds = Mod($iSeconds, 3600)
    $iMins = Int($iSeconds / 60)
    $iSecs = Mod($iSeconds, 60)

    If StringLen($iHours) = 1 Then $iHours = "0" & $iHours
    If StringLen($iMins) = 1 Then $iMins = "0" & $iMins
    If StringLen($iSecs) = 1 Then $iSecs = "0" & $iSecs

    Return $iHours & ":" & $iMins & ":" & $iSecs
EndFunc   ;==>CalcDupeTime

Func sendEmail($Title, $Queue, $NumSets, $DupeTime)
    Local $QueueSize = UBound($Queue, 1)

    Local $s_SmtpServer = "10.100.52.30"
    Local $s_FromName = "IMGBurn Disc Duplicator"
    Local $s_FromAddress = "ConfigurationManagementDistribution@aaccorp.com"
    Local $s_ToAddress = "ConfigurationManagementDistribution@aaccorp.com"
    Local $s_Subject = "[DUPLICATOR] - " & $Title & " - " & $NumSets & " set(s) in " & $DupeTime

    ; Old _INetSmtpMail code
    ;Local $as_Body[$QueueSize + 1]
    ;$as_Body[0] = "The job took " & $DupeTime & " to complete." & @CRLF

    Local $s_Body = "<body><p style=""font-family:Calibri, Arial, Helvetica, sans-serif;font-size:11pt;"">The job took " & $DupeTime & " to complete.</p><p></p>"
    $s_Body &= "<p style=""font-family:Calibri, Arial, Helvetica, sans-serif;font-size:11pt;"">"

    Local $DiscTitle

    ; Old _INetSmtpMail code
    ;For $i = 0 To $QueueSize - 1
    ;   $DiscTitle = StringRegExp($Queue[$i][0], '\\([^\\]*)\.std$', 1)
    ;   $as_Body[$i+1] = $DiscTitle[0] & @TAB ; We add a TAB at the end to stop Outlook from concatenating lines >= 40 chars
    ;Next

    For $i = 0 To $QueueSize - 1
        $DiscTitle = StringRegExp($Queue[$i][0], '\\([^\\]*)\.std$', 1)
        _HtmlEntities_Encode($DiscTitle[0])
        $s_Body &= $DiscTitle[0] & "<br>"
    Next

    $s_Body &= "</p></body>"

    ; Old _INetSmtpMail code
    ;Local $Response = _INetSmtpMail($s_SmtpServer, $s_FromName, $s_FromAddress, $s_ToAddress, $s_Subject, $as_Body, 1, -1)
    ;Local $err = @error
    ;
    ;If $Response = 1 Then
    ;   MsgBox(0, "Success!", "Mail sent")
    ;Else
    ;   MsgBox(0, "Error!", "Mail failed with error code " & $err)
    ;EndIf

    Local $oMyRet[2]
    Local $oMyError = ObjEvent("AutoIt.Error", "MyErrFunc")
    Local $Response = _INetSmtpMailCom($s_SmtpServer, $s_FromName, $s_FromAddress, $s_ToAddress, $s_Subject, $s_Body)
    If @error Then
        MsgBox(0, "Error sending message", "Error code:" & @error & "  Description:" & $Response)
    EndIf

EndFunc   ;==>sendEmail

Func toggleScreenSaver()
    Local $key = "HKEY_CURRENT_USER\Control Panel\Desktop"
    Local $value = "ScreenSaveActive"
    If Number(RegRead($key, $value)) Then
        RegWrite($key, $value, "REG_SZ", 0)
    Else
        RegWrite($key, $value, "REG_SZ", 1)
    EndIf
EndFunc   ;==>toggleScreenSaver

Func disableScreenSaver()
    Local $key = "HKEY_CURRENT_USER\Control Panel\Desktop"
    Local $value = "ScreenSaveActive"
    If Number(RegRead($key, $value)) Then
        RegWrite($key, $value, "REG_SZ", 0)
    EndIf
EndFunc   ;==>disableScreenSaver

Func enableScreenSaver()
    Local $key = "HKEY_CURRENT_USER\Control Panel\Desktop"
    Local $value = "ScreenSaveActive"
    If Number(RegRead($key, $value)) = 0 Then
        RegWrite($key, $value, "REG_SZ", 1)
    EndIf
EndFunc   ;==>enableScreenSaver

Func WriteStatus($LogMessage)
    ConsoleWrite($LogMessage)
    GUICtrlSetData($Label_Status, $LogMessage)
EndFunc   ;==>WriteStatus

Func WriteStatusError($LogMessage)
    ConsoleWriteError($LogMessage)
    GUICtrlSetData($Label_Status, $LogMessage)
EndFunc   ;==>WriteStatusError

Func _INetSmtpMailCom($s_SmtpServer, $s_FromName, $s_FromAddress, $s_ToAddress, $s_Subject = "", $as_Body = "", $s_AttachFiles = "", $s_CcAddress = "", $s_BccAddress = "", $s_Importance = "Normal", $s_Username = "", $s_Password = "", $IPPort = 25, $ssl = 0)
    Local $objEmail = ObjCreate("CDO.Message")
    $objEmail.From = '"' & $s_FromName & '" <' & $s_FromAddress & '>'
    $objEmail.To = $s_ToAddress
    Local $i_Error = 0
    Local $i_Error_desciption = ""
    If $s_CcAddress <> "" Then $objEmail.Cc = $s_CcAddress
    If $s_BccAddress <> "" Then $objEmail.Bcc = $s_BccAddress
    $objEmail.Subject = $s_Subject
    If StringInStr($as_Body, "<") And StringInStr($as_Body, ">") Then
        $objEmail.HTMLBody = $as_Body
    Else
        $objEmail.Textbody = $as_Body & @CRLF
    EndIf
    If $s_AttachFiles <> "" Then
        Local $S_Files2Attach = StringSplit($s_AttachFiles, ";")
        For $x = 1 To $S_Files2Attach[0]
            $S_Files2Attach[$x] = _PathFull($S_Files2Attach[$x])
;~          ConsoleWrite('@@ Debug : $S_Files2Attach[$x] = ' & $S_Files2Attach[$x] & @LF & '>Error code: ' & @error & @LF) ;### Debug Console
            If FileExists($S_Files2Attach[$x]) Then
                ConsoleWrite('+> File attachment added: ' & $S_Files2Attach[$x] & @LF)
                $objEmail.AddAttachment($S_Files2Attach[$x])
            Else
                ConsoleWrite('!> File not found to attach: ' & $S_Files2Attach[$x] & @LF)
                SetError(1)
                Return 0
            EndIf
        Next
    EndIf
    $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
    $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = $s_SmtpServer
    If Number($IPPort) = 0 Then $IPPort = 25
    $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = $IPPort
    ;Authenticated SMTP
    If $s_Username <> "" Then
        $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1
        $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = $s_Username
        $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = $s_Password
    EndIf
    If $ssl Then
        $objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = True
    EndIf
    ;Update settings
    $objEmail.Configuration.Fields.Update
    ; Set Email Importance
    Switch $s_Importance
        Case "High"
            $objEmail.Fields.Item("urn:schemas:mailheader:Importance") = "High"
        Case "Normal"
            $objEmail.Fields.Item("urn:schemas:mailheader:Importance") = "Normal"
        Case "Low"
            $objEmail.Fields.Item("urn:schemas:mailheader:Importance") = "Low"
    EndSwitch
    $objEmail.Fields.Update
    ; Sent the Message
    $objEmail.Send
    If @error Then
        SetError(2)
        Return $oMyRet[1]
    EndIf
    $objEmail = ""
EndFunc   ;==>_INetSmtpMailCom
;
;
; Com Error Handler
Func MyErrFunc($oMyError)
    Local $HexNumber = Hex($oMyError.number, 8)
    $oMyRet[0] = $HexNumber
    $oMyRet[1] = StringStripWS($oMyError.description, 3)
    ConsoleWrite("### COM Error !  Number: " & $HexNumber & "   ScriptLine: " & $oMyError.scriptline & "   Description:" & $oMyRet[1] & @LF)
    SetError(1); something to check for when this function returns
    Return
EndFunc   ;==>MyErrFunc

Func _HtmlEntities_Encode(ByRef $sTxt)
    For $i = 0 To 245
        $sTxt = StringReplace($sTxt, ChrW($aisEntities[$i][0]), '&' & $aisEntities[$i][1] & ';', 0, 1)
    Next
EndFunc   ;==>_HtmlEntities_Encode

Func _HtmlEntities_Decode(ByRef $sTxt)
    For $i = 0 To 245
        $sTxt = StringReplace($sTxt, '&' & $aisEntities[$i][1] & ';', ChrW($aisEntities[$i][0]), 0, 1)
    Next
EndFunc   ;==>_HtmlEntities_Decode

..but I do not have this Primera device, so, ..good luck.

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

@Enoch23, I can no longer aid you due to the simple fact, that what I did to avoid the error, was beyond simple. Do get acquainted with AutoIt. Try example scripts, etc. It takes time. Be patient with .. everything really. Shooting from the heap takes experience. Kindly post the next questions in the help and support forum.

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

Hello.

I am new to AutoIt. I don't expect people to do the work for me. However, I would find it helpful to be pointed in the right direction as to what to read up, etc. Being new to something, I don't have all the terminology down, which makes it difficult to even do a simple search for my answers.

That being said:

I am trying to get the script posted from GPinzone to work for me.

I have a Primera device, the DLL's, and Imgburn. I understand the script to the extent of what the functions do and the DLL calls. What I don't understand about the code, is how you get it to run. Sounds kinda stupid, to be honest, but I just don't get it.

Link to comment
Share on other sites

  • Developers

Why are you posting this here instead continuing in the place this belongs and where you already posted?

It makes no sense so merged back into that thread.

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 comment
Share on other sites

On 4/19/2019 at 3:21 PM, Enoch23 said:

Hello.

I am new to AutoIt. I don't expect people to do the work for me. However, I would find it helpful to be pointed in the right direction as to what to read up, etc. Being new to something, I don't have all the terminology down, which makes it difficult to even do a simple search for my answers.

That being said:

I am trying to get the script posted from GPinzone to work for me.

I have a Primera device, the DLL's, and Imgburn. I understand the script to the extent of what the functions do and the DLL calls. What I don't understand about the code, is how you get it to run. Sounds kinda stupid, to be honest, but I just don't get it.

Primera released a set of DLLs so you could manipulate the robot through C. I wrote the script to provide wrappers for the DLLs so you can write your own code in AutoIt instead. The DLLs provide very fine-grained control of the duplicator. This is no function in the DLL for "Burn a disc, label it, and eject it. That's why I also wrote the "processJobs" function.

Create another .au3 file like this:

;
; AutoIt Version: 3.0
; Language:       English
; Platform:       XP Professional
; Author:         Gerard Pinzone (gpinzone@aaccorp.com)
;
; Script Function:
;   Runs multiple jobs for PrimoDVD.
;

#include "C:\Macros\Disc Duplicator Function Libraries.au3"

; Title of job
Local $Title = "CentOS-6.0-x86_64-bin-DVD (2 discs)"

; Set up array of labels and images
Local $Queue [32][2] = [["C:\Disc Labels\Misc\CentOS-6.0-x86_64-bin-DVD\CentOS-6.0-x86_64-bin-DVD1.std", "C:\Disc Images\Misc\CentOS-6.0-x86_64-bin-DVD\CentOS-6.0-x86_64-bin-DVD1.iso"], _
                        ["C:\Disc Labels\Misc\CentOS-6.0-x86_64-bin-DVD\CentOS-6.0-x86_64-bin-DVD2.std", "C:\Disc Images\Misc\CentOS-6.0-x86_64-bin-DVD\CentOS-6.0-x86_64-bin-DVD2.iso"]]

Local $i

; Trim the Queue array down to actual size
For $i = 0 To UBound( $Queue, 1) - 1
  If $Queue[$i][0] = "" Then
    ReDim $Queue[$i][2]
    ExitLoop
  EndIf
Next

; Display selection prompt
$Queue = OpenPrompt($Title, $Queue)

; Run main function
processJobs($Title, $Queue)

Obviously, the paths will be different for you.

When you run that, it will burn those ISO files and print the labels as shown. Is that what you're trying to accomplish or something else?

Gerard J. Pinzonegpinzone AT yahoo.com
Link to comment
Share on other sites

Great! Thank you! Now I understand.

My plan was to rip a large DVD collection using MakeMKV, using the programs command line interface.

A couple of questions:

what do I set the PTACT_ALLOW_NO_CARTRIDGES to, so that the use without a cartridge is allowed? At the moment I get the corresponding error. Or, maybe, I misunderstand the way this is supposed to work. 🙄

whats with the DriveNum.txt file? Is this created automatically? At the moment I am getting an error that it can't be found. (I guess, cause it's not there :D )

 

In the future I would also like to be able to burn and print, so I figured it would be a good idea, to get your script working for me "as is", and take it from there for my batch ripping.

 

Thanks again for the help so far!

Link to comment
Share on other sites

The PTACT_ALLOW_NO_CARTRIDGES  is a constant to be used with the PTRobot_SystemAction command.

I haven't tried it, but I assume all you'd have to do is run these commands:

PTRobot_Wait()
PTRobot_SystemAction( PTACT_ALLOW_NO_CARTRIDGES )
PTRobot_Wait()

to enable that mode.

Gerard J. Pinzonegpinzone AT yahoo.com
Link to comment
Share on other sites

  • 2 weeks later...
  • 5 months later...

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
 Share

  • Recently Browsing   0 members

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