/***************************************************************************************************
**    Copyright (C) 2018 - 2019 HMS Technology Center Ravensburg GmbH, all rights reserved
****************************************************************************************************
**
**        File: demo.cs
**     Summary: Simple demo application, sending and receiving 11-bit CAN messages at 250 KBaud.
**              Adapt the COM/TTY port in the k_SERIAL_PORT define.
**              To abort the demo application, press any key.
**
****************************************************************************************************
**    This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY.
***************************************************************************************************/
using System;
using IXXAT;
using static IXXAT.SimplyCAN;

namespace Demo
{
  class Demo
  {
    //private static char[] mserial_port = "/dev/ttyACM0\0".ToCharArray();   // Linux
    private static char[] mserial_port = "COM5\0".ToCharArray();   // Windows
    private static ushort mbitrate = 250;
    private static uint mticks_per_second = 10000000;   //  1 Tick = 100 nsec

    private static void ErrorExit()
    {
      string errorText;

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

      Console.Write("\nError: ");
      Console.Write(errorText);
      Console.WriteLine();
      simply_close();
      Environment.Exit(-1);
    }

    private static bool Status()
    {
      SimplyCAN.can_sts_t can_sts = new can_sts_t {};

      /* format and print CAN status */
      if (!simply_can_status(ref can_sts))
        return false;

      Console.Write("CAN status: ");
      if ((can_sts.sts & CAN_STATUS_RUNNING) !=0)
        Console.Write("--- ");

      if ((can_sts.sts & CAN_STATUS_RESET) !=0)
        Console.Write("RST ");

      if ((can_sts.sts & CAN_STATUS_BUSOFF) !=0)
        Console.Write("BOF ");
      else
        Console.Write("--- ");

      if ((can_sts.sts & CAN_STATUS_ERRORSTATUS) != 0)
        Console.Write("ERR ");
      else
        Console.Write("--- ");

      if ((can_sts.sts & CAN_STATUS_RXOVERRUN) != 0)
        Console.Write("RxO ");
      else
        Console.Write("--- ");

      if ((can_sts.sts & CAN_STATUS_TXOVERRUN) != 0)
        Console.Write("TxO ");
      else
        Console.Write("--- ");

      if ((can_sts.sts & CAN_STATUS_PENDING) != 0)
        Console.Write("PDG ");
      else
        Console.Write("--- ");

      Console.WriteLine();

      return true;
    }

    private static sbyte ReceiveMessages()
    {
      SimplyCAN.can_msg_t can_msg_rx = new can_msg_t {};
      string flag_string = "";

      sbyte res = simply_receive(ref can_msg_rx);
      if (res == 1)
      {
        /* format and print received message */
        if (can_msg_rx.ident >= 0x80000000)   //  MSB=1: extended frame
          flag_string += "E";
        else
          flag_string += " ";

        if (can_msg_rx.dlc >= 0x80)           //  MSB=1: remote frame
        {
          can_msg_rx.dlc = 1;
          flag_string += "R";
        }
        else
        {
          flag_string += " ";
        }

        Console.Write(string.Format("0x{0,8:X} ", can_msg_rx.ident & 0x7FFFFFFF));
        Console.Write(flag_string);
        Console.Write(string.Format(" [{0}]", can_msg_rx.dlc));
        for (byte idx = 0; idx < can_msg_rx.dlc; idx++)
          Console.Write(" {0:X2}", can_msg_rx.payload[idx]);

        Console.WriteLine();
        return 1;
      }
      else
      if (res == -1)
      {
        /* error occurred */
        return -1;
      }
      else
      {
        /* no new messages */
        return 0;
      }
    }

    static void Main(string[] args)
    {
      Console.WriteLine("\n#### simplyCAN Demo 1.0 (c) 2018-2019 HMS ####\n");

      if (!simply_open(mserial_port))
        ErrorExit();

      SimplyCAN.identification_t id;
      if (!simply_identify(out id))
        ErrorExit();

      Console.WriteLine(string.Format("Firmware version: {0}", id.fw_version));
      Console.WriteLine(string.Format("Hardware version: {0}", id.hw_version));
      Console.WriteLine(string.Format("Product version:  {0}", id.product_version));
      Console.WriteLine(string.Format("Product string:   {0}", id.product_string));
      Console.WriteLine(string.Format("Serial number:    {0}", id.serial_number));

      bool res = simply_stop_can();         // to be on the safer side
      res &= simply_initialize_can(mbitrate);
      res &= simply_start_can();
      if (!res)
        ErrorExit();

      /* generate tx CAN message */
      SimplyCAN.can_msg_t can_msg_tx = new can_msg_t { timestamp = 0, ident = 0x100, dlc = 4, payload = new Byte[8] { 0x01, 0x02, 0x03, 0x04, 0, 0, 0, 0 } };

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

      long last_sent = DateTime.Now.Ticks - mticks_per_second;
      while (!Console.KeyAvailable)
      {
        long now = DateTime.Now.Ticks;
        if (now - last_sent >= mticks_per_second)
        {
          /* send CAN message every second */
          if (!simply_send(ref can_msg_tx))
            ErrorExit();

          /* print status message every second */
          if (!Status())
            ErrorExit();

          last_sent = now;
          can_msg_tx.ident = (can_msg_tx.ident + 1) % 0x3FF;
        }

        /* handle received messages */
        sbyte result = ReceiveMessages();
        if (result == 0)
          /* no message received */
          System.Threading.Thread.Sleep(1);
        else
        if (result == -1)
          /* error occurred */
          ErrorExit();
      }

      simply_stop_can();
      simply_close();
    }
  }
}
