' ****************************************************************************************************
' **    Copyright (C) 2018 - 2020 HMS Technology Center Ravensburg GmbH, all rights reserved
' ****************************************************************************************************
' **
' **        File: demo.vb
' **     Summary: Simple demo application, sending and receiving 11-bit CAN messages at 250 KBaud.
' **              Adapt the COM/TTY port in the mComPort variable.
' **              To abort the demo application, press any key.
' **
' ****************************************************************************************************
' **    This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY.
' ****************************************************************************************************
Imports System
Imports System.Text

Module demo

    Private mbitrate As UShort = 250
    Private mticks_per_second As UInteger = 10000000   '  1 Tick = 100 nsec
    Private mComPort As String = "COM5"

    ''' <summary>
    ''' End the application with an error code -1.
    ''' Shows before the last error from the SimplyCAN as text.
    ''' </summary>
    Private Sub ErrorExit()
        Dim errorText As String

        Dim simplyError As Short = simply.simply_get_last_error()
        Select Case simplyError
            Case simply.SIMPLY_S_NO_ERROR
                errorText = "No error occurred"
            Case simply.SIMPLY_E_SERIAL_OPEN
                errorText = "Unable to open the serial port"
            Case simply.SIMPLY_E_SERIAL_ACCESS
                errorText = "Access on serial port denied"
            Case simply.SIMPLY_E_SERIAL_CLOSED
                errorText = "Serial communication port is closed"
            Case simply.SIMPLY_E_SERIAL_COMM
                errorText = "Serial communication error"""
            Case simply.SIMPLY_E_CMND_REQ_UNKNOWN
                errorText = "Command unknown on device"
            Case simply.SIMPLY_E_CMND_RESP_TIMEOUT
                errorText = "Command response timeout reached"
            Case simply.SIMPLY_E_CMND_RESP_UNEXPECTED
                errorText = "Unexpected command response received"
            Case simply.SIMPLY_E_CMND_RESP_ERROR
                errorText = "Command response error"
            Case simply.SIMPLY_E_INVALID_PROTOCOL_VERSION
                errorText = "Invalid simplyCAN protocol version"
            Case simply.SIMPLY_E_INVALID_FW_VERSION
                errorText = "Invalid device firmware version"
            Case simply.SIMPLY_E_INVALID_PRODUCT_STRING
                errorText = "Invalid simplyCAN product string"
            Case simply.SIMPLY_E_CAN_INVALID_STATE
                errorText = "Invalid CAN state"
            Case simply.SIMPLY_E_CAN_INVALID_BAUDRATE
                errorText = "Invalid CAN baud-rate"
            Case simply.SIMPLY_E_TX_BUSY
                errorText = "Message could not be sent. TX is busy"
            Case simply.SIMPLY_E_API_BUSY
                errorText = "API is busy"
            Case Else
                errorText = String.Format("{0}", simplyError)
        End Select


        Console.Write("\nError: ")
        Console.Write(errorText)
        Console.WriteLine()
        simply.simply_close()
        Environment.Exit(-1)
    End Sub


    ''' <summary>
    ''' Check the current state of the SimplyCAN.
    ''' This function must called periodically.
    ''' </summary>
    ''' <returns>
    ''' true - status could be read
    ''' false - status could not read
    ''' </returns>
    Private Function Status() As Boolean
        Dim can_sts As simply.can_sts_t = New simply.can_sts_t()

        ' format and print CAN status
        If Not simply.simply_can_status(can_sts) Then
            Return False
        End If


        Console.Write("CAN status: ")
        If ((can_sts.sts And simply.CAN_STATUS_RUNNING) = simply.CAN_STATUS_RUNNING) Then
            Console.Write("--- ")
        End If


        If ((can_sts.sts And simply.CAN_STATUS_RESET) = simply.CAN_STATUS_RESET) Then
            Console.Write("RST ")
        End If


        If ((can_sts.sts And simply.CAN_STATUS_BUSOFF) = simply.CAN_STATUS_BUSOFF) Then
            Console.Write("BOF ")
        Else
            Console.Write("--- ")
        End If


        If ((can_sts.sts And simply.CAN_STATUS_ERRORSTATUS) = simply.CAN_STATUS_ERRORSTATUS) Then
            Console.Write("ERR ")
        Else
            Console.Write("--- ")
        End If

        If ((can_sts.sts And simply.CAN_STATUS_RXOVERRUN) = simply.CAN_STATUS_RXOVERRUN) Then
            Console.Write("RxO ")
        Else
            Console.Write("--- ")
        End If


        If ((can_sts.sts And simply.CAN_STATUS_TXOVERRUN) = simply.CAN_STATUS_TXOVERRUN) Then
            Console.Write("TxO ")
        Else
            Console.Write("--- ")
        End If


        If ((can_sts.sts And simply.CAN_STATUS_PENDING) = simply.CAN_STATUS_PENDING) Then
            Console.Write("PDG ")
        Else
            Console.Write("--- ")
        End If

        Console.WriteLine()

        Return True
    End Function

    ''' <summary>
    ''' Read CAN messages from the SimplyCAN and show it on the console.
    ''' </summary>
    ''' <returns><para>
    ''' 1 - Message received </para><para>
    ''' 0 - No message available in the receive queue </para><para>
    ''' -1 - Error occurred, call simply_get_last_error for more information. </para>
    ''' </returns>
    Private Function ReceiveMessages() As SByte
        Dim can_msg_rx As simply.can_msg_t = New simply.can_msg_t()
        Dim flag_string As String = ""

        Dim res As SByte = simply.simply_receive(can_msg_rx)
        If res = 1 Then
            ' format and print received message 
            If can_msg_rx.ident >= &H80000000UI Then   ' MSB=1: extended frame
                flag_string += "E"
            Else
                flag_string += " "
            End If


            If can_msg_rx.dlc >= &H80 Then           '  MSB=1: remote frame
                can_msg_rx.dlc = 1
                flag_string += "R"
            Else
                flag_string += " "
            End If

            Console.Write(String.Format("&H{0,8:X} ", can_msg_rx.ident And &H7FFFFFFFUI))
            Console.Write(flag_string)
            Console.Write(String.Format(" [{0}]", can_msg_rx.dlc))
            For idx As Byte = 1 To can_msg_rx.dlc
                Console.Write(" {0:X2}", can_msg_rx.payload(idx - 1))
            Next

            Console.WriteLine()
            Return 1
        Else
            If res = -1 Then
                ' error occurred 
                Return -1
            Else
                ' no new messages 
                Return 0
            End If
        End If

    End Function

    ''' <summary>
    ''' Defines the entry point of the application.
    ''' Initialize the SimplyCAN and start of send and receive CAN messages.
    ''' </summary>
    Sub Main()

        Dim result As Boolean = simply.simply_open(mComPort)
        If result Then
            Dim id As simply.identification_t = New simply.identification_t()
            result = simply.simply_identify(id)
        End If

        If result Then
            result = simply.simply_stop_can()  ' to be on the safer side
            result = result Or simply.simply_initialize_can(mbitrate)
            result = result Or simply.simply_start_can()
        End If

        If result Then

            ' The array size value is the highest index, index starts by default with zero
            ' use Option Base 1 to Set default array subscripts to 1. 
            Dim can_msg_tx As New simply.can_msg_t With {.timestamp = 0, .ident = &H100, .dlc = 4, .payload = New Byte(7) {&H1, &H2, &H3, &H4, 0, 0, 0, 0}}

            Console.WriteLine("\nRun application...\n")

            Dim last_sent As Long = DateTime.Now.Ticks - mticks_per_second
            While Not Console.KeyAvailable
                Dim now As Long = DateTime.Now.Ticks
                If (now - last_sent >= mticks_per_second) Then
                    ' send CAN message every second
                    If Not simply.simply_send(can_msg_tx) Then
                        ErrorExit()
                    End If


                    ' print status message every second
                    If Not Status() Then
                        ErrorExit()
                    End If


                    last_sent = now
                    can_msg_tx.ident = (can_msg_tx.ident + 1) Mod &H3FF
                End If

                ' handle received messages
                Dim rxCount As SByte = ReceiveMessages()
                If rxCount = 0 Then
                    ' no message received 
                    System.Threading.Thread.Sleep(1)
                ElseIf rxCount = -1 Then
                    ' error occurred
                    ErrorExit()
                End If


            End While

            simply.simply_stop_can()
        End If

        simply.simply_close()

    End Sub


End Module
