② Utility Functions

These are the shared helper functions used throughout the whole script. They are called dozens (sometimes hundreds) of times during the configuration process. You don't need to change these unless there is a bug.

CreateLogFile

Sub CreateLogFile
What it does:
Creates a new script called "LogFile" inside the Starter project's Scripts section. If the log file already exists, it reuses it. Clears any old content. This is the start of the audit trail — every parameter value changed later is recorded here.
Sub CreateLogFile
    Dim ConfigLogFile          ' This will hold a reference to the LogFile script object in Starter
    Dim ConfigLogFileText       ' (Reserved, not actually used in the body)

    If ConfigOption02_Logfile Then   ' Only create the log if logging is enabled in config (see file 01)
        On Error Resume Next            ' Don't crash if the script already exists — just handle it gracefully

        Set ConfigLogFile = PROJ.Scripts.Item(ConfigLogFileName)  ' Try to get the existing "LogFile" script

        If (Err.Number<>0) Then          ' If it didn't exist (error occurred)...
            APP.PrintToLog "Log file Doesn't exist"     ' ...log this fact to Starter's internal log
            PROJ.Scripts.Add(ConfigLogFileName)              ' ...create a brand-new script named "LogFile"
            APP.PrintToLog "Log file created"
            Set ConfigLogFile = PROJ.Scripts.Item(ConfigLogFileName)  ' ...and get a reference to it
        End If

        ' Write a header line to the log so it's easy to see where one session starts
        ConfigLogFile.Code="' "&"-------------------- LogFile created -------------------"&vbCrLf
        ' vbCrLf = a newline character (carriage return + line feed)
    End If
End Sub

PrintToLogFile

Sub PrintToLogFile( logText )
What it does:
Appends one line of text to the "LogFile" script in Starter. Called by SetParam, SetSym, and PrintHeader each time a parameter or symbol is changed. The logged text is prefixed with ' so that the LogFile reads as valid VBScript comments.
Sub PrintToLogFile(logText)
    Dim ConfigLogFile
    If ConfigOption02_Logfile Then     ' Only log if logging is enabled
        Set ConfigLogFile = PROJ.Scripts.Item(ConfigLogFileName)    ' Get the LogFile script
        ConfigLogFile.Code = ConfigLogFile.Code & "'" & logText & vbCrLf
        ' AppendS the new line: ConfigLogFile.Code is the existing content,
        ' plus "'" (makes it a VBScript comment), plus the new text, plus a newline
    End If
End Sub

SetParam

Sub SetParam( driveTO, parameter, index, newValue )
What it does:
This is the most important function in the entire script. It writes a value to a Siemens drive parameter and records what changed in the log file.

Arguments:
Sub SetParam(driveTO, parameter, index, newValue)
    oldValue = driveTO.Parameters(parameter, index)   ' Read the current value BEFORE changing it
    driveTO.Parameters(parameter, index) = newValue   ' Write the new value to the drive
    finalValue = driveTO.Parameters(parameter, index) ' Read back the value to verify it was accepted
    ' NOTE: Sometimes drives reject a write if the value is out of range.
    '       finalValue lets us see what the drive actually stored.

    ' ── Build the log line ──────────────────────────────────────────────────────────
    ' Each column is padded to LogfilePadLength (15 chars) for aligned CSV output
    DriveObjectName = Padding_Text(driveTo.Name, LogfilePadLength, " ")  ' e.g. "Unwind         "
    ParameterType   = Padding_Text("Parameter",  LogfilePadLength, " ")  ' always "Parameter      "
    Parameter       = Padding_Text(parameter,   LogfilePadLength, " ")  ' e.g. "210            "
    index           = Padding_Text(index,        LogfilePadLength, " ")  ' e.g. "0              "
    OldValue        = Padding_Text(oldValue,     LogfilePadLength, " ")
    NewValue        = Padding_Text(newValue,     LogfilePadLength, " ")
    FinalValue      = Padding_Text(finalValue,   LogfilePadLength, " ")

    If ParameterEchoToScreen Then  ' If debug echo is on, also print to HTML status bar
        EchoToScreen(DriveObjectName & "," & ParameterType & "," & Parameter & "," & Index & "," & oldValue & "," & NewValue & "," & FinalValue)
    End If

    PrintToLogFile(DriveObjectName & "," & ParameterType & "," & Parameter & "," & Index & "," & OldValue & "," & NewValue & "," & FinalValue)
    ' Log line example: "Unwind         ,Parameter      ,210            ,0              ,0              ,400            ,400            "
End Sub

Padding_Text

Function Padding_Text( StringToPad, FinalStringLen, PadCharacter )
What it does:
Takes a string and adds spaces (or any other character) to make it exactly FinalStringLen characters long. This makes the log file columns line up neatly. If CompressedCSVLogfile = True, it just returns the original string unchanged.
Function Padding_Text(StringToPad, FinalStringLen, PadCharacter)
    PaddingLength = FinalStringLen - Len(StringtoPad)  ' How many spaces do we need to add?
    PaddedStringOut = StringToPad                       ' Start with the original string

    For i=1 To PaddingLength                           ' Add spaces one at a time until correct length
        PaddedStringOut = PaddedStringOut & PadCharacter
    Next

    If CompressedCSVLogfile = True Then
        Padding_Text = StringToPad       ' Compressed mode: return unpadded original
    Else
        Padding_Text = PaddedStringOut   ' Normal mode: return padded string
    End If
End Function

SetSym

Sub SetSym( driveTO, sym, newValue )
What it does:
Similar to SetParam, but writes to a Siemens drive Symbol (a named alias for a parameter bit) instead of a raw parameter number. Symbols look like p2100[0].1 — meaning parameter 2100, array index 0, bit 1.

The function also parses the symbol string to extract the parameter number and index so they can be logged in the same format as SetParam.
Sub SetSym(driveTO, sym, newValue)
    oldValue  = driveTO.Symbols(sym)    ' Read current symbol value
    driveTO.Symbols(sym) = newValue     ' Write new value
    finalValue = driveTO.Symbols(sym)   ' Read back to verify

    ' ── Parse the symbol string to extract the parameter name and index ──────────
    PeriodPosn = instr(sym, ".")        ' Find position of the "." — e.g. "p2100[0].1" → period is at position 9
    IndexText = mid(sym, PeriodPosn+1)  ' Everything after the period = bit index (e.g. "1")

    IndexStartPosn = instr(sym, "[")    ' Find position of "[" for array index
    IndexEndPosn   = instr(sym, "]")    ' Find position of "]"

    ' Extract the array digit(s) — could be single digit (0-9) or double digit (10-99)
    If IndexStartPosn <> 0 And IndexEndPosn <> 0 Then
        If (IndexEndPosn - IndexStartPosn) = 2 Then
            StringText1 = mid(sym, IndexStartPosn+1, 1)   ' Single digit: e.g. "[0]" → "0"
        Else
            StringText1 = mid(sym, IndexStartPosn+1, 2)   ' Double digit: e.g. "[10]" → "10"
        End If
    End If

    ' Extract just the parameter number (strip the leading "p" character)
    StringText = Left(sym, PeriodPosn-1)  ' e.g. "p2100[0]"
    SymbolText = mid(StringText, 2)       ' Remove leading "p" → "2100[0]"

    ' ── Build and log the output line ──────────────────────────────────────────────
    SymbolType = Padding_Text("Symbol", LogfilePadLength, " ")
    ' ... (pad all values, then call PrintToLogFile — same pattern as SetParam)
End Sub

ConfirmWithUser

Function ConfirmWithUser( prompt )
What it does:
Shows a Yes/No popup dialog to the engineer and returns True if they clicked Yes, False if they clicked No or pressed Cancel.
Function ConfirmWithUser(prompt)
    returnValue = MsgBox(prompt, 4, "Confirm:")
    ' MsgBox with parameter 4 = Yes/No buttons
    ' Return value 6 = Yes was clicked, anything else = No
    ConfirmWithUser = (returnValue = 6)   ' Returns True only if Yes was clicked
End Function

GoOnline

Function GoOnline( TargetStateOnline )
What it does:
Puts the Starter project online (connected to physical drives) or offline (disconnected). Returns True if the target state was achieved.

It always goes offline first before going online — this prevents communication conflicts.
⚠️ Why go offline first? Siemens Starter can sometimes get "stuck" if you try to go online while already online. Going offline first resets the connection state cleanly.
Function GoOnline(TargertStateOnline)    ' Returns True if the requested state was reached
    ThisProject.Online = False          ' ALWAYS go offline first (resets connection state)
    On Error Resume Next                  ' Don't crash if going online fails

    If TargertStateOnline Then
        ThisProject.Online = True        ' Try to go online (connect to real hardware)
    Else
        ThisProject.Online = False       ' Explicitly stay offline
    End If

    GoOnline = ThisProject.Online         ' Return the actual current state
                                          ' (may be False even if True was requested, if connection failed)
    On Error GoTo 0
End Function

CheckExists

Function CheckExists( DeviceName, ThisName, ThisType, Alert )
What it does:
Checks whether a given device, drive object, or sub-object exists in the Starter project. Returns True if found, False if not. Optionally shows an error popup if it's missing (Alert = True).
ThisTypeChecks for
0A Device (e.g., a CU320)
1A Technology Object inside a device (e.g., the Unwind drive)
2A SubObject inside a device (e.g., an infeed module)
Function CheckExists(DeviceName, ThisName, ThisType, Alert)
    On Error Resume Next
    If ThisType=0 Then Set ThisObject = PROJ.Devices(DeviceName)               ' Try to get device
    If ThisType=1 Then Set ThisObject = PROJ.Devices(DeviceName).TOs(ThisName)   ' Try to get drive TO
    If ThisType=2 Then Set ThisObject = PROJ.Devices(DeviceName).SubObjects(ThisName)

    If Err.Number Then           ' If any of the above failed (object not found)...
        CheckExists = False       ' ...return False
        If Alert ... Then MsgBox(...)  ' ...and optionally show an error popup
    Else
        CheckExists = True        ' Object was found OK
    End If
    On Error GoTo 0
End Function

EchoToScreen, StatusLine, StatusLineEcho

What they do:
These three functions update the HTML UI to show progress and status messages.
Sub EchoToScreen(TextLine)
    APP.PrintToLog LineString    ' Print a divider line to Starter's internal log (for readability)
    MainPage.StatusText = LineString
    APP.PrintToLog TextLine      ' Print the actual message
    MainPage.StatusText = TextLine   ' Display in the browser window's status bar
End Sub

Sub StatusLine(LineNumber, Colour, StatusText)
    ' Sets the text INSIDE the status cell next to button number LineNumber
    ' e.g. StatusLine(1, "Green", "Complete") → step 1's status cell shows "Complete" in green
    MainPage.Document.GetElementbyid("out_" & CStr(LineNumber)).innerHTML = StatusText
    MainPage.Document.GetElementbyid("out_" & CStr(LineNumber)).style.Color = Colour
End Sub

Sub StatusLineEcho(LineNumber, Colour, StatusText)
    MainPage.StatusText = StatusText    ' Update browser status bar
    StatusLine LineNumber, Colour, StatusText  ' Also update the button's status cell
End Sub

Continue to ③ Drive Configuration.