'***************************************************************************************
'
' File name: MappTrace.vb
' Location: See Matrox Example Launcher in the MIL Control Center
' 
'
' Synopsis:  This example shows how to explicitly control and generate a trace for 
'            MIL functions and how to visualize it using the Matrox Profiler utility. 
'            To generate a trace, you must open Matrox Profiler (accessible from the 
'            MIL Control Center) and select 'Generate New Trace' from the 'File' menu 
'            before to run your MIL application.
' 
' Note:      By default, all MIL applications are traceable without code modifications.
'            You can try this using Matrox Profiler with any MIL example (Ex: MappStart).
'
' Copyright (C) Matrox Electronic Systems Ltd., 1992-2020.
' All Rights Reserved
'
Imports System.Runtime.InteropServices
Imports Matrox.MatroxImagingLibrary

Class HookDataStruct
    Public MilImageDisp As MIL_ID
    Public MilImageTemp1 As MIL_ID
    Public MilImageTemp2 As MIL_ID
    Public ProcessedImageCount As MIL_INT
    Public DoneEvent As MIL_ID
End Class

Module MappTrace

    ' Trace related constants
    Private Const TRACE_TAG_HOOK_START As Integer = 1
    Private Const TRACE_TAG_PROCESSING As Integer = 2
    Private Const TRACE_TAG_PREPROCESSING As Integer = 3

    ' General constants.
    Private ReadOnly COLOR_BROWN As Integer = MIL.M_RGB888(100, 65, 50)

    Private Const BUFFERING_SIZE_MAX As Integer = 3
    Private Const NUMBER_OF_FRAMES_TO_PROCESS As Integer = 10

    Sub Main()
        Dim MilApplication As MIL_ID = MIL.M_NULL
        Dim MilSystem As MIL_ID = MIL.M_NULL
        Dim MilDisplay As MIL_ID = MIL.M_NULL
        Dim MilDigitizer As MIL_ID = MIL.M_NULL
        Dim MilGrabBuf(BUFFERING_SIZE_MAX - 1) As MIL_ID
        Dim MilDummyBuffer As MIL_ID = MIL.M_NULL

        Dim TracesActivated As MIL_INT = MIL.M_NO
        Dim NbGrabBuf As MIL_INT = 0
        Dim SizeX As MIL_INT = 0, SizeY As MIL_INT = 0

        Dim UserHookData As New HookDataStruct()

        Console.WriteLine()
        Console.WriteLine("MIL PROGRAM TRACING AND PROFILING:")
        Console.WriteLine("----------------------------------")
        Console.WriteLine()
        Console.WriteLine("This example shows how to generate a trace for the execution")
        Console.WriteLine("of the MIL functions, and to visualize it using")
        Console.WriteLine("the Matrox Profiler utility.")
        Console.WriteLine()
        Console.WriteLine("ACTION REQUIRED:")
        Console.WriteLine()
        Console.WriteLine("Open 'Matrox Profiler' from the 'MIL Control Center' and")
        Console.WriteLine("select 'Generate New Trace' from the 'File' menu.")
        Console.WriteLine()
        Console.WriteLine("Press <Enter> to continue.")
        Console.WriteLine()
        Console.ReadKey()

        '*************** Untraceable code section ***************

        ' The following code will not be visible in the trace.

        ' MIL application allocation. 
        ' At MIL Application allocation time, M_TRACE_LOG_DISABLE can be used to ensures that 
        ' an application will not be traceable regardless of Matrox Profiler or MilConfig requests
        ' unless traces are explicitly enabled in the program using an MappControl command.
        '
        MIL.MappAlloc("M_DEFAULT", MIL.M_TRACE_LOG_DISABLE, MilApplication)

        ' Dummy MIL calls that will be invisible in the trace.
        MIL.MsysAlloc(MIL.M_DEFAULT, MIL.M_SYSTEM_HOST, MIL.M_DEFAULT, MIL.M_DEFAULT, MilSystem)
        MIL.MbufAllocColor(MilSystem, 3, 128, 128, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE, MilDummyBuffer)
        MIL.MbufClear(MilDummyBuffer, 0L)
        MIL.MbufFree(MilDummyBuffer)
        MIL.MsysFree(MilSystem)

        '********************************************************

        ' Explicitly allow trace logging after a certain point if Matrox Profiler has
        ' requested a trace. Note that M_TRACE = M_ENABLE can be used to force the log 
        ' of a trace even if Profiler is not opened; M_TRACE = M_DISABLE can prevent 
        ' logging of code section.
        '
        MIL.MappControl(MIL.M_DEFAULT, MIL.M_TRACE, MIL.M_DEFAULT)

        ' Inquire if the traces are active (i.e. Profiler is open and waiting for a trace).
        MIL.MappInquire(MIL.M_DEFAULT, MIL.M_TRACE_ACTIVE, TracesActivated)

        If TracesActivated = MIL.M_YES Then
            ' Create custom trace markers: setting custom names and colors.

            ' Initialize a custom Tag for the grab callback function with a unique color (blue).
            MIL.MappTrace(MIL.M_DEFAULT, MIL.M_TRACE_SET_TAG_INFORMATION, TRACE_TAG_HOOK_START, MIL.M_COLOR_BLUE, "Grab Callback Marker")

            ' Initialize the custom Tag for the processing section.
            MIL.MappTrace(MIL.M_DEFAULT, MIL.M_TRACE_SET_TAG_INFORMATION, TRACE_TAG_PROCESSING, MIL.M_DEFAULT, "Processing Section")

            ' Initialize the custom Tag for the preprocessing with a unique color (brown).
            MIL.MappTrace(MIL.M_DEFAULT, MIL.M_TRACE_SET_TAG_INFORMATION, TRACE_TAG_PREPROCESSING, COLOR_BROWN, "Preprocessing Marker")

        End If

        ' Allocate MIL objects.
        MIL.MsysAlloc(MIL.M_DEFAULT, MIL.M_SYSTEM_HOST, MIL.M_DEFAULT, MIL.M_DEFAULT, MilSystem)
        MIL.MdigAlloc(MilSystem, MIL.M_DEFAULT, "M_DEFAULT", MIL.M_DEFAULT, MilDigitizer)
        MIL.MdispAlloc(MilSystem, MIL.M_DEFAULT, "M_DEFAULT", MIL.M_DEFAULT, MilDisplay)

        SizeX = MIL.MdigInquire(MilDigitizer, MIL.M_SIZE_X, MIL.M_NULL)
        SizeY = MIL.MdigInquire(MilDigitizer, MIL.M_SIZE_Y, MIL.M_NULL)

        MIL.MbufAllocColor(MilSystem, 3, SizeX, SizeY, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, UserHookData.MilImageDisp)

        MIL.MdispSelect(MilDisplay, UserHookData.MilImageDisp)

        ' Allocate the processing temporary buffers.
        MIL.MbufAlloc2d(MilSystem, SizeX, SizeY, 8 + MIL.M_UNSIGNED, MIL.M_PROC + MIL.M_IMAGE, _
           UserHookData.MilImageTemp1)
        MIL.MbufAlloc2d(MilSystem, SizeX, SizeY, 8 + MIL.M_UNSIGNED, MIL.M_PROC + MIL.M_IMAGE, _
           UserHookData.MilImageTemp2)

        ' Allocate the grab buffers.
        For NbGrabBuf = 0 To BUFFERING_SIZE_MAX - 1
            MIL.MbufAllocColor(MilSystem, 3, SizeX, SizeY, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC, MilGrabBuf(NbGrabBuf))
        Next NbGrabBuf

        ' Initialize the user's processing function data structure.
        UserHookData.ProcessedImageCount = 0
        MIL.MthrAlloc(MilSystem, MIL.M_EVENT, MIL.M_NOT_SIGNALED + MIL.M_AUTO_RESET, MIL.M_NULL, MIL.M_NULL, UserHookData.DoneEvent)

        Dim UserHookDataPtr As GCHandle = GCHandle.Alloc(UserHookData)
        Dim HookFunctionPtr As New MIL_DIG_HOOK_FUNCTION_PTR(AddressOf HookFunction)

        ' Start the processing. The processing function is called with every frame grabbed.
        MIL.MdigProcess(MilDigitizer, MilGrabBuf, BUFFERING_SIZE_MAX, MIL.M_START, MIL.M_DEFAULT, HookFunctionPtr, GCHandle.ToIntPtr(UserHookDataPtr))

        ' Stop the processing when the event is triggered.
        MIL.MthrWait(UserHookData.DoneEvent, MIL.M_EVENT_WAIT + MIL.M_EVENT_TIMEOUT(2000), CType(MIL.M_NULL, IntPtr))

        ' Stop the processing.
        MIL.MdigProcess(MilDigitizer, MilGrabBuf, BUFFERING_SIZE_MAX, MIL.M_STOP, MIL.M_DEFAULT, HookFunctionPtr, GCHandle.ToIntPtr(UserHookDataPtr))
        UserHookDataPtr.Free()

        ' Free the grab and temporary buffers.
        For NbGrabBuf = 0 To BUFFERING_SIZE_MAX - 1
            MIL.MbufFree(MilGrabBuf(NbGrabBuf))
        Next NbGrabBuf
        MIL.MbufFree(UserHookData.MilImageTemp1)
        MIL.MbufFree(UserHookData.MilImageTemp2)

        ' Free defaults.
        MIL.MthrFree(UserHookData.DoneEvent)
        MIL.MappFreeDefault(MilApplication, MilSystem, MilDisplay, MilDigitizer, UserHookData.MilImageDisp)

        ' If the Matrox Profiler activated the traces, the trace file is now ready.
        If TracesActivated = MIL.M_YES Then

            Console.WriteLine("A PROCESSING SEQUENCE WAS EXECUTED AND LOGGED A NEW TRACE:")
            Console.WriteLine()
            Console.WriteLine("The trace can now be loaded in Matrox Profiler by selecting the")
            Console.WriteLine("corresponding file listed in the 'Trace Generation' dialog.")
            Console.WriteLine()
            Console.WriteLine("Once loaded, Matrox Profiler's main window displays the 'Main'")
            Console.WriteLine("and the 'MdigProcess' threads of the application.")
            Console.WriteLine()
            Console.WriteLine("- This main window can now be used to select a section")
            Console.WriteLine("  of a thread and to zoom or pan in it.")
            Console.WriteLine()
            Console.WriteLine("- The right pane shows detailed statistics as well as a")
            Console.WriteLine("  'Quick Access' list displaying all MIL function calls.")
            Console.WriteLine()
            Console.WriteLine("- The 'User Markers' tab lists the markers and sections logged")
            Console.WriteLine("  during the execution. For example, selecting 'Tag:Processing'")
            Console.WriteLine("  allows double-clicking to refocus the display on the related")
            Console.WriteLine("  calls.")
            Console.WriteLine()
            Console.WriteLine("- By clicking a particular MIL function call, either in the")
            Console.WriteLine("  'main view' or in the 'Quick Access', additional details")
            Console.WriteLine("  are displayed, such as its parameters and execution time.")
            Console.WriteLine()

        Else
            Console.WriteLine("ERROR: No active tracing detected in MIL Profiler!")
            Console.WriteLine()
        End If

        Console.WriteLine("Press <Enter> to end.")
        Console.ReadKey()
    End Sub

    Function HookFunction(ByVal HookType As MIL_INT, ByVal HookId As MIL_ID, ByVal HookDataPtr As IntPtr) As MIL_INT
        Dim CurrentImage As MIL_ID = MIL.M_NULL

        If HookDataPtr <> IntPtr.Zero Then
            Dim UserDataPtr As HookDataStruct = TryCast(GCHandle.FromIntPtr(HookDataPtr).Target, HookDataStruct)

            ' Add a marker to indicate the reception of a new grabbed image.
            MIL.MappTrace(MIL.M_DEFAULT, MIL.M_TRACE_MARKER, TRACE_TAG_HOOK_START, MIL.M_NULL, "New Image Grabbed")

            ' Retrieve the MIL_ID of the grabbed buffer.
            MIL.MdigGetHookInfo(HookId, MIL.M_MODIFIED_BUFFER + MIL.M_BUFFER_ID, CurrentImage)

            ' Start a Section to highlight the processing calls on the image.
            MIL.MappTrace(MIL.M_DEFAULT, MIL.M_TRACE_SECTION_START, TRACE_TAG_PROCESSING, UserDataPtr.ProcessedImageCount, "Processing Image")

            ' Add a Marker to indicate the start of the preprocessing section.
            MIL.MappTrace(MIL.M_DEFAULT, MIL.M_TRACE_MARKER, TRACE_TAG_PREPROCESSING, UserDataPtr.ProcessedImageCount, "Start Preprocessing")

            ' Do the preprocessing.
            MIL.MimConvert(CurrentImage, UserDataPtr.MilImageTemp1, MIL.M_RGB_TO_L)
            MIL.MimHistogramEqualize(UserDataPtr.MilImageTemp1, UserDataPtr.MilImageTemp1, MIL.M_UNIFORM, MIL.M_NULL, 55, 200)

            ' Add a Marker to indicate the end of the preprocessing section.
            MIL.MappTrace(MIL.M_DEFAULT, MIL.M_TRACE_MARKER, TRACE_TAG_PREPROCESSING, UserDataPtr.ProcessedImageCount, "End Preprocessing")

            ' Do the main processing.
            MIL.MimBinarize(UserDataPtr.MilImageTemp1, UserDataPtr.MilImageTemp2, MIL.M_IN_RANGE, 120, 140)
            MIL.MimBinarize(UserDataPtr.MilImageTemp1, UserDataPtr.MilImageTemp1, MIL.M_IN_RANGE, 220, 255)
            MIL.MimArith(UserDataPtr.MilImageTemp1, UserDataPtr.MilImageTemp2, UserDataPtr.MilImageDisp, MIL.M_OR)

            ' M_TRACE_SECTION_END: end a section in the trace to highlight 
            ' the processing calls on the image.
            MIL.MappTrace(MIL.M_DEFAULT, MIL.M_TRACE_SECTION_END, TRACE_TAG_PROCESSING, UserDataPtr.ProcessedImageCount, "Processing Image End")

            ' Signal that we have done enough processing.
            UserDataPtr.ProcessedImageCount += 1
            If UserDataPtr.ProcessedImageCount >= NUMBER_OF_FRAMES_TO_PROCESS Then
                MIL.MthrControl(UserDataPtr.DoneEvent, MIL.M_EVENT_SET, MIL.M_SIGNALED)
            End If
        End If

        Return 0
    End Function
End Module