'***************************************************************************************
'
' File name: MappTrace.vb
' Location:  ...\Matrox Imaging\MILxxx\Examples\General\MappTrace\VB
'             
'
' 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-2015.
' 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.Write(Constants.vbLf + "MIL PROGRAM TRACING AND PROFILING:" + Constants.vbLf)
        Console.Write("----------------------------------" + Constants.vbLf + Constants.vbLf)

        Console.Write("This example shows how to generate a trace for the execution" + Constants.vbLf)
        Console.Write("of the MIL functions, and to visualize it using" + Constants.vbLf)
        Console.Write("the Matrox Profiler utility." + Constants.vbLf + Constants.vbLf)
        Console.Write("ACTION REQUIRED:" + Constants.vbLf + Constants.vbLf)
        Console.Write("Open 'Matrox Profiler' from the 'MIL Control Center' and" + Constants.vbLf)
        Console.Write("select 'Generate New Trace' from the 'File' menu." + Constants.vbLf)
        Console.Write("Press <Enter> to continue." + Constants.vbLf + Constants.vbLf)
        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)

        Console.Write("A dummy processing has been executed to generate ")
        Console.Write("operations in the trace." + Constants.vbLf + Constants.vbLf)
        Console.Write("Press <Enter> to continue." + Constants.vbLf + Constants.vbLf)
        Console.ReadKey()

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

            Console.Write("A PROCESSING SEQUENCE WAS EXECUTED AND LOGGED A NEW TRACE:" + Constants.vbLf + Constants.vbLf)

            Console.Write("The trace can now be loaded in Matrox Profiler by selecting the" + Constants.vbLf)
            Console.Write("corresponding file listed in the 'Trace Generation' dialog." + Constants.vbLf + Constants.vbLf)

            Console.Write("Once loaded, Matrox Profiler's main window displays the 'Main'" + Constants.vbLf)
            Console.Write("and the 'MdigProcess' threads of the application." + Constants.vbLf + Constants.vbLf)

            Console.Write("- This main window can now be used to select a section" + Constants.vbLf + Constants.vbLf)
            Console.Write("  of a thread and to zoom or pan in it." + Constants.vbLf + Constants.vbLf)

            Console.Write("- The right pane shows detailed statistics as well as a" + Constants.vbLf)
            Console.Write("  'Quick Access' list displaying all MIL function calls." + Constants.vbLf)

            Console.Write("- The 'User Markers' tab lists the markers and sections logged" + Constants.vbLf)
            Console.Write("  during the execution. For example, selecting 'Tag:Processing'" + Constants.vbLf)
            Console.Write("  allows double-clicking to refocus the display on the related" + Constants.vbLf)
            Console.Write("  calls." + Constants.vbLf)

            Console.Write("- By clicking a particular MIL function call, either in the" + Constants.vbLf)
            Console.Write("  'main view' or in the 'Quick Access', additional details" + Constants.vbLf)
            Console.Write("  are displayed, such as its parameters and execution time." + Constants.vbLf)

        Else
            Console.Write("ERROR: No active tracing detected in MIL Profiler!" + Constants.vbLf)
        End If

        Console.Write("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