using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

using System.Threading;
using System.IO;
using System.IO.Ports;
using System.Windows.Forms;

using LFP_Manager.DataStructure;
using LFP_Manager.Function;
using LFP_Manager.Utils;

namespace LFP_Manager.Threads
{
    public delegate void CanDataUpdate(object sender, ref DeviceSystemData aSystemData);
    public delegate void CanDataRecv(UInt32 hdr, byte[] data);
    public delegate void CanDataPrint(object sender, string msg);

    class csCanThread
    {
        #region DLL FUNCTION

        [DllImport("library\\controlcan.dll")]
        static extern UInt32 VCI_OpenDevice(UInt32 DeviceType, UInt32 DeviceInd, UInt32 Reserved);
        [DllImport("library\\controlcan.dll")]
        static extern UInt32 VCI_CloseDevice(UInt32 DeviceType, UInt32 DeviceInd);
        [DllImport("library\\controlcan.dll")]
        static extern UInt32 VCI_InitCAN(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_INIT_CONFIG pInitConfig);
        [DllImport("library\\controlcan.dll")]
        static extern UInt32 VCI_ReadBoardInfo(UInt32 DeviceType, UInt32 DeviceInd, ref VCI_BOARD_INFO pInfo);
        [DllImport("library\\controlcan.dll")]
        static extern UInt32 VCI_ReadErrInfo(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_ERR_INFO pErrInfo);
        [DllImport("library\\controlcan.dll")]
        static extern UInt32 VCI_ReadCANStatus(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_CAN_STATUS pCANStatus);

        [DllImport("library\\controlcan.dll")]
        static extern UInt32 VCI_GetReference(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, UInt32 RefType, ref byte pData);
        [DllImport("library\\controlcan.dll")]
        //static extern UInt32 VCI_SetReference(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, UInt32 RefType, ref byte pData);
        unsafe static extern UInt32 VCI_SetReference(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, UInt32 RefType, byte* pData);

        [DllImport("library\\controlcan.dll")]
        static extern UInt32 VCI_GetReceiveNum(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);
        [DllImport("library\\controlcan.dll")]
        static extern UInt32 VCI_ClearBuffer(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);

        [DllImport("library\\controlcan.dll")]
        static extern UInt32 VCI_StartCAN(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);
        [DllImport("library\\controlcan.dll")]
        static extern UInt32 VCI_ResetCAN(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);

        [DllImport("library\\controlcan.dll")]
        static extern UInt32 VCI_Transmit(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_CAN_OBJ pSend, UInt32 Len);

        //[DllImport("library\\controlcan.dll")]
        //static extern UInt32 VCI_Receive(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_CAN_OBJ pReceive, UInt32 Len, Int32 WaitTime);
        [DllImport("library\\controlcan.dll", CharSet = CharSet.Ansi)]
        static extern UInt32 VCI_Receive(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, IntPtr pReceive, UInt32 Len, Int32 WaitTime);

        #endregion

        #region VARIABLES

        public delegate void invokeDelegate();

        CommConfig Config;
        DeviceSystemData SystemData;

        TCanTxBuff CanTxBuf;

        int SystemId = 0;

        Thread canCommTx = null;
        Thread canCommRx = null;
        //string Msg = "";

        static UInt32 can_devtype = 21;   //USBCAN-2e-u
        
        ////////////////////////////////////////
        const UInt32 STATUS_OK = 1;

        bool can_bOpen = false;
        UInt32 can_devind = 0;
        UInt32 can_canind = 0;

        bool CanTxThreadEnd = true;
        bool CanRxThreadEnd = true;

        bool CanPolling = false;
        bool AutoCanTx = true;

        int dcp = 0; // Data Control Parameter

        public event CanDataUpdate OnUpdate = null;
        public event CanDataPrint OnPrint = null;
        public event CanDataRecv OnDataRecv = null;

        private object lockObject = new object();
        #endregion

        #region CONSTRUCTORS
        public csCanThread(int sId, CommConfig aConfig, ref DeviceSystemData aSystemData)
        {
            SystemId = sId;
            Config = aConfig;
            SystemData = aSystemData;

            CanTxBuf = new TCanTxBuff();

            canCommTx = new Thread(CanTxThread);
            canCommRx = new Thread(CanRxThread);
        }

        public void disposeThread()
        {
            CanTxThreadEnd = true;
            if (canCommTx != null)
            {
                if (canCommTx.IsAlive)
                {
                    canCommTx.Abort();
                }
                canCommTx = null;
            }
            CanRxThreadEnd = true;
            if (canCommRx != null)
            {
                if (canCommRx.IsAlive)
                {
                    canCommRx.Abort();
                }
                canCommRx = null;
            }

            if (can_bOpen == true)
            {
                _ = VCI_CloseDevice(can_devtype, can_devind);
            }
        }

        public unsafe bool Start(ref CommConfig aConfig, int sId, bool aPolling)
        {
            bool result = false;

            SystemId = sId;
            CanPolling = aPolling;
            Config = aConfig;

            can_devtype = (UInt32)csCanConstData.CanDeviceInfo.DeviceIds[Config.CanDevice];
            can_devind = (UInt32)Config.CanIndex;
            can_canind = (UInt32)Config.CanNo;

            try
            {
                if (VCI_OpenDevice(can_devtype, can_devind, 0) == 0)
                    throw new System.InvalidOperationException("Failed to open the device, please check if the device type and device index number are correct.");

                //USB-E-U Code
                UInt32 baud;
                VCI_INIT_CONFIG config = new VCI_INIT_CONFIG();

                if (can_devtype == csCanConstData.CanDeviceInfo.VCI_USBCAN_2E_U)
                {
                    baud = (UInt32)csCanConstData.BaudRate.BaudRates[aConfig.CanBaudrate];
                    config.Timing0 = 0x00;
                    config.Timing1 = 0x14;

                    if (VCI_SetReference(can_devtype, can_devind, can_canind, 0, (byte*)&baud) != STATUS_OK)
                    {
                        VCI_CloseDevice(can_devtype, can_devind);
                        throw new System.InvalidOperationException("Set baud rate error, failed to open device!");
                    }
                }
                else
                {
                    baud = (UInt32)csCanConstData.BaudRate.BaudRatesOther[aConfig.CanBaudrate];
                    config.Timing0 = (byte)(baud >> 8);
                    config.Timing1 = (byte)(baud & 0xFF);
                }
                can_bOpen = true;

                //Config setting
                config.AccCode = 0x00000000;
                config.AccMask = 0xFFFFFFFF;
                config.Filter = 1;
                config.Mode = 0;

                VCI_InitCAN(can_devtype, can_devind, can_canind, ref config);

                ////Filter setting
                //Int32 filterMode = comboBox_e_u_Filter.SelectedIndex;
                //if (2 != filterMode)//Not disabled
                //{
                //    VCI_FILTER_RECORD filterRecord = new VCI_FILTER_RECORD();
                //    filterRecord.ExtFrame = (UInt32)filterMode;
                //    filterRecord.Start = System.Convert.ToUInt32("0x" + textBox_e_u_startid.Text, 16);
                //    filterRecord.End = System.Convert.ToUInt32("0x" + textBox_e_u_endid.Text, 16);
                //    //Fill filter table
                //    VCI_SetReference(m_devtype, m_devind, m_canind, 1, (byte*)&filterRecord);
                //    //Make the filter table take effect
                //    byte tm = 0;
                //    if (VCI_SetReference(m_devtype, m_devind, m_canind, 2, &tm) != STATUS_OK)
                //    {
                //        MessageBox.Show("Set filter failed", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                //        VCI_CloseDevice(m_devtype, m_devind);
                //        return;
                //    }
                //}
                ////////////////////////////////////////////////////////////////////////////
                ////buttonConnect.Text = m_bOpen == 1 ? "DISCONNECT" : "CONNECT";
                ////timer_rec.Enabled = m_bOpen == 1 ? true : false;

                UInt32 res;
                res = VCI_StartCAN(can_devtype, can_devind, can_canind);

                CanTxThreadEnd = false;
                CanRxThreadEnd = false;

                canCommTx.Start();
                canCommRx.Start();

                result = true;
            }
            catch (Exception ex)
            {
                System.Exception Ex = new System.Exception(ex.Message, ex);
                throw Ex;
            }
            return result;
        }
        #endregion

        #region TX FUNCTION
        public void SetAutoTx(bool autoTx)
        {
            AutoCanTx = autoTx;
        }

        public void SetDcp(int aDcp)
        {
            dcp = aDcp;
        }

        //public void SendProcessFromApp(int sId, int mode, int index, int flag, ref DeviceParamData aParam, ref DeviceCalibration aCalib, int type)
        //{
        //    if (can_bOpen == true)
        //    {
        //        TCanTRxData[] CanTRxData = csCanCommFunction.SendProcessFromAppPacket(sId, mode, index, flag, ref aParam, ref aCalib);
        //        if (CanTRxData != null)
        //        {
        //            for (int i = 0; i < CanTRxData.Length; i++)
        //            {
        //                CanTRxData[i].type = type;
        //                CanTxBuf.PutBuff(CanTRxData[i]);
        //            }
        //        }
        //    }
        //}
        public void SendDataFromApp(uint header, byte[] data, int type)
        {
            if (can_bOpen == true)
            {
                TCanTRxData CanTRxData = new TCanTRxData
                {
                    exid = header
                };
                for (int i = 0; i < data.Length; i++)
                {
                    CanTRxData.data[i] = data[i];
                }
                CanTxBuf.PutBuff(CanTRxData);
            }
        }
        unsafe public void SendDataFromAppA(UInt32 header, byte[] data)
        {
            if (can_bOpen == false) return;

            VCI_CAN_OBJ sendobj = new VCI_CAN_OBJ();

            sendobj.SendType = csCanConstData.SendType.Normal;
            sendobj.ExternFlag = csCanConstData.FrameType.Extended;
            sendobj.RemoteFlag = csCanConstData.FrameFormat.Data_Frame;
            sendobj.ID = header;  // 0x1815c9c8

            int len = 8;
            sendobj.DataLen = System.Convert.ToByte(len);
            for (int i = 0; i < len; i++)
                sendobj.Data[i] = data[i];

            //int nTimeOut = 3000;
            //if (can_devtype == csCanConstData.CanDeviceInfo.VCI_USBCAN_2E_U)
            //{
            //    VCI_SetReference(can_devtype, can_devind, can_canind, 3, (byte*)&nTimeOut);
            //}

            if (VCI_Transmit(can_devtype, can_devind, can_canind, ref sendobj, 1) == 0)
            {
                throw new Exception("Failed to send");
            }

            OnPrint?.Invoke(this, csCanCommFunction.PacketToMsg(sendobj, 1));
        }

        public void SetPolling(bool flag, int sId, ref DeviceSystemData aSystemData)
        {
            SystemId = sId;
            CanPolling = flag;
            SystemData = aSystemData;
            SystemData.mNo = SystemId;
        }

        unsafe public void SendData()
        {
            if (can_bOpen == false) return;

            VCI_CAN_OBJ sendobj = new VCI_CAN_OBJ();
            sendobj.SendType = csCanConstData.SendType.Normal;
            sendobj.ExternFlag = csCanConstData.FrameType.Extended;
            sendobj.RemoteFlag = csCanConstData.FrameFormat.Data_Frame;
            sendobj.ID = 0x1815c9c8;  // 0x1815c9c8

            int len = 8;
            sendobj.DataLen = Convert.ToByte(len);
            sendobj.Data[0] = 0xff;
            sendobj.Data[1] = 0xff;
            sendobj.Data[2] = 0xff;
            sendobj.Data[3] = 0xff;
            sendobj.Data[4] = 0xff;
            sendobj.Data[5] = 0xff;
            sendobj.Data[6] = 0xff;
            sendobj.Data[7] = 0xff;

            //int nTimeOut = 3000;
            //if (can_devtype == csCanConstData.CanDeviceInfo.VCI_USBCAN_2E_U)
            //{
            //    VCI_SetReference(can_devtype, can_devind, can_canind, 3, (byte*)&nTimeOut);
            //}

            if (VCI_Transmit(can_devtype, can_devind, can_canind, ref sendobj, 1) == 0)
            {
                throw new Exception("Failed to send");
            }
        }

        public void SendProcessFromApp(int sId, int mode, int flag, int dcp, ref DeviceParamData aParam, ref DeviceCalibration aCalib)
        {
            switch (mode)
            {
                case 5:         // Cell Volatge Parameter
                    SendData(sId, 5, flag, dcp, ref aParam, ref aCalib);
                    SendData(sId, 23, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 7:
                    SendData(sId, 7, flag, dcp, ref aParam, ref aCalib);
                    SendData(sId, 23, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 6:
                    SendData(sId, 6, flag, dcp, ref aParam, ref aCalib);
                    SendData(sId, 24, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 22:
                    SendData(sId, 22, flag, dcp, ref aParam, ref aCalib);
                    SendData(sId, 24, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 14:
                    SendData(sId, 14, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 99:            // All param read
                    SendData(sId, 5, flag, dcp, ref aParam, ref aCalib);
                    SendData(sId, 6, flag, dcp, ref aParam, ref aCalib);
                    SendData(sId, 7, flag, dcp, ref aParam, ref aCalib);
                    SendData(sId, 14, flag, dcp, ref aParam, ref aCalib);
                    SendData(sId, 22, flag, dcp, ref aParam, ref aCalib);
                    SendData(sId, 23, flag, dcp, ref aParam, ref aCalib);
                    SendData(sId, 24, flag, dcp, ref aParam, ref aCalib);
                    SendData(sId, 2, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 9:             // Cell Voltage Calibration
                    SendData(sId, 9, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 1:             // Cell Balancing Flag Set
                    SendData(sId, 1, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 12:             // Cell Voltage Calibration
                    SendData(sId, 12, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 8:              // Battery Parameter Setting
                    SendData(sId, 8, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 17:             // Cell Balancing Parameter - 240531
                    SendData(sId, 17, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 21:             // System Information (Device Address)
                    SendData(sId, 21, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 13:             // Soc Calibration
                    SendData(sId, 13, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 15:             // Default Parameter
                    SendData(sId, 15, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 16:             // Default Parameter
                    if (aParam.DefalutParamAll == 1)
                        SendData(0xFF, 16, flag, dcp, ref aParam, ref aCalib);
                    else
                        SendData(sId, 16, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 2:              // Cell Voltage Difference Parameter
                    SendData(sId, 2, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 25:
                    SendData(sId, 25, flag, dcp, ref aParam, ref aCalib);
                    break;

                // Inventory Data Control
                case 900:       // Manufacturer Cmd
                    SendData(sId, 900, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 901:       // Serial Number #1 Cmd
                    SendData(sId, 901, flag, dcp, ref aParam, ref aCalib);
                    break;
                case 902:       // Serial Number #2 Cmd
                    SendData(sId, 902, flag, dcp, ref aParam, ref aCalib);
                    break;
            }
        }

        //unsafe void SendData(int sId, int mode, int flag, ref DeviceParamData aParam, ref DeviceCalibration aCalib)
        //{
        //    if (can_bOpen == 0) return;

        //    VCI_CAN_OBJ sendobj = MakeTxData(sId, mode, flag, ref aParam, ref aCalib);

        //    Monitor.Enter(lockObject);
        //    try
        //    {
        //        OnPrint?.Invoke(this, csCanCommFunction.PacketToMsg(sendobj, 1));

        //        if (VCI_Transmit(can_devtype, can_devind, can_canind, ref sendobj, 1) == 0)
        //        {
        //            throw new Exception("Failed to send");
        //        }
        //    }
        //    catch (Exception)
        //    {
        //        //throw new Exception(String.Format("CAN Transmit Exception", ex.Message));
        //    }
        //    finally
        //    {
        //        Monitor.Exit(lockObject);
        //    }
        //}

        void SendData(int sid, int mode, int flag, int dcp, ref DeviceParamData aParam, ref DeviceCalibration aCalib)
        {
            UInt32 exID;
            byte[] sendData = new byte[8];

            exID = MakeTxData(sid, mode, flag, dcp, ref aParam, ref aCalib, ref sendData);

            SendDataFromApp(exID, sendData, 0);
            //SendCAN(exID, sendData);
        }

        unsafe void SendCAN(UInt32 exID, byte[] data)
        {
            if (can_bOpen == false) return;

            VCI_CAN_OBJ sendobj = new VCI_CAN_OBJ();

            sendobj.SendType = csCanConstData.SendType.Normal;
            sendobj.RemoteFlag = csCanConstData.FrameFormat.Data_Frame;
            sendobj.ExternFlag = csCanConstData.FrameType.Extended;

            sendobj.ID = exID;
            sendobj.DataLen = (byte)data.Length;
            for (int i = 0; i < data.Length; i++)
                sendobj.Data[i] = data[i];

            //Monitor.Enter(lockObject);
            if (Monitor.TryEnter(lockObject, 2000))
            {
                try
                {
                    if (VCI_Transmit(can_devtype, can_devind, can_canind, ref sendobj, 1) == 0)
                    {
                        throw new Exception("Failed to send");
                    }
                    OnPrint?.Invoke(this, csCanCommFunction.PacketToMsg(sendobj, 1));

                    //OnPrint?.Invoke(this, csCanCommFunction.PacketToMsg(sendobj, 1));
                }
                catch (Exception)
                {
                    //throw new Exception(String.Format("CAN Transmit Exception", ex.Message));
                }
                finally
                {
                    Monitor.Exit(lockObject);
                }
            }
            else
            {
                //OnPrint?.Invoke(this, "CAN SEND FAIL");
                throw new Exception("CAN SEND FAIL");
            }
            //try
            //{
            //    if (VCI_Transmit(can_devtype, can_devind, can_canind, ref sendobj, 1) == 0)
            //    {
            //        throw new Exception("Failed to send");
            //    }
            //    OnPrint?.Invoke(this, csCanCommFunction.PacketToMsg(sendobj, 1));
            //}
            //catch (Exception)
            //{
            //    //throw new Exception(String.Format("CAN Transmit Exception", ex.Message));
            //}
            //finally
            //{
            //    Monitor.Exit(lockObject);
            //}
        }

        private UInt32 MakeID(int sId, int mode)
        {
            UInt32 result = 0;
            PACKET_HEADER hdr = new PACKET_HEADER();

            hdr.Index = 6;                                  // Fixed 6
            hdr.R = 0;                                      // Fixed 0
            hdr.DP = 0;                                     // Fixed 0
            hdr.PF = (byte)mode;                            // Command
            if (sId == 0xFF) hdr.PS = (byte)(sId);          // PDU SPECIFIC
            else             hdr.PS = (byte)(sId + 200);    // PDU SPECIFIC
            hdr.SA = 200;                                   // SOURCE ADDRESS

            result = csCanCommFunction.CovertHtoP(hdr);
            return result;
        }

        private UInt32 MakeInvID(int sId, int mode)
        {
            UInt32 result = 0;
            PACKET_HEADER hdr = new PACKET_HEADER();

            hdr.Index = 1;                                  // Fixed 1 for Inventory Data
            hdr.R = 0;                                      // Fixed 0
            hdr.DP = 0;                                     // Fixed 0
            hdr.PF = (byte)((mode % 900) + 1);              // Command
            if (sId == 0xFF) hdr.PS = (byte)(sId);          // PDU SPECIFIC
            else hdr.PS = (byte)(sId + 200);                // PDU SPECIFIC
            hdr.SA = 200;                                   // SOURCE ADDRESS

            result = csCanCommFunction.CovertHtoP(hdr);
            return result;
        }

        unsafe private VCI_CAN_OBJ MakeTxData(int sId, int mode, int flag, int dcp, ref DeviceParamData aParam, ref DeviceCalibration aCalib)
        {
            VCI_CAN_OBJ sendobj = new VCI_CAN_OBJ();

            sendobj.SendType = csCanConstData.SendType.Normal;
            sendobj.RemoteFlag = csCanConstData.FrameFormat.Data_Frame;
            sendobj.ExternFlag = csCanConstData.FrameType.Extended;
            if (mode < 900)
                { sendobj.ID = MakeID(sId, mode); }
            else
                { sendobj.ID = MakeInvID(sId, mode); }
            sendobj.DataLen = (byte)(8);

            byte[] aData = csCanCommFunction.MakeCanData(mode, flag, dcp, ref aParam, ref aCalib);
            for (int i = 0; i < 8; i++)
                { sendobj.Data[i] = aData[i]; }

            return sendobj;
        }

        private UInt32 MakeTxData(int sId, int mode, int flag, int dcp, ref DeviceParamData aParam, ref DeviceCalibration aCalib, ref byte[] wData)
        {
            UInt32 result = 0;

            if (mode < 900)
                { result = MakeID(sId, mode); }
            else
                { result = MakeInvID(sId, mode); }

            wData = csCanCommFunction.MakeCanData(mode, flag, dcp, ref aParam, ref aCalib);

            return result;
        }

        #endregion

        #region COMM TX THREAD
        int current_cmd = 0;
        int RxSystemId = 0;

        public int GetCommSystemId()
        {
            return RxSystemId;
        }

        unsafe private void CanTxThread()
        {
            while (CanTxThreadEnd == false)
            {
                TCanTRxData tData = CanTxBuf.GetBuff();
                if (tData != null)
                {
                    SendCanPacket(tData, tData.type);
                    //Thread.Sleep(1);
                }
                else
                {
                    if ((CanPolling) && (AutoCanTx))
                    {
                        int flag = 0;

                        TxProcess(ref flag);
                        RxSystemId = SystemId;
                        SendData(SystemId, current_cmd, flag, dcp, ref SystemData.ParamData, ref SystemData.CalibriationData);
                    }
                    Thread.Sleep(50);
                }
                Thread.Sleep(1);
            }
        }

        int TxFlag = 0;
        void TxProcess(ref int aflag)
        {
            switch (TxFlag)
            {
                case 0:
                    current_cmd = 900;
                    TxFlag = 1;
                    break;
                case 1:
                    current_cmd = 901;
                    TxFlag = 2;
                    break;
                case 2:
                    current_cmd = 902;
                    TxFlag = 3;
                    break;
                default:
                    switch (current_cmd)
                    {
                        case 900: current_cmd = 901; break;     // INV Manufacture Date
                        case 901: current_cmd = 902; break;     // INV pcb sn MSB
                        case 902: current_cmd = 9; break;       // INV pcb sn LSB

                        case 9: current_cmd = 20; break;        // Module Address get

                        case 20: current_cmd = 1; break;

                        case 1: current_cmd = 19; break;        // Status
                        case 19: current_cmd = 3; break;
                        case 3: current_cmd = 4; break;
                        case 4: current_cmd = 11; break;
                        case 10: current_cmd = 11; break;
                        case 11: current_cmd = 15; break;
                        case 15: current_cmd = 16; break;
                        case 16: current_cmd = 900; break;
                        default: current_cmd = 900; break;
                    }
                    break;
            }
        }
        private unsafe void SendCanPacket(TCanTRxData CanTxData, int type)
        {
            VCI_CAN_OBJ sendobj = new VCI_CAN_OBJ();

            sendobj.SendType = csCanConstData.SendType.Normal;
            sendobj.RemoteFlag = csCanConstData.FrameFormat.Data_Frame;
            sendobj.ExternFlag = csCanConstData.FrameType.Extended;
            sendobj.ID = CanTxData.exid;
            sendobj.DataLen = (byte)(8);

            for (int i = 0; i < 8; i++)
            {
                sendobj.Data[i] = CanTxData.data[i];
            }

            Monitor.Enter(lockObject);
            try
            {
                if (VCI_Transmit(can_devtype, can_devind, can_canind, ref sendobj, 1) == 0)
                {
                    throw new Exception("Failed to send");
                }
                //OnPrint?.Invoke(this, csCanConstData.PacketToMsg(CanTxData.exid, CanTxData.data, 1, type));
            }
            catch (Exception)
            {
            }
            finally
            {
                Monitor.Exit(lockObject);
            }
        }

        #endregion

        #region COMM RX THREAD
        unsafe private void CanRxThread()
        {
            DateTime aTime;

            while (CanRxThreadEnd == false)
            {
                UInt32 res = new UInt32();
                aTime = DateTime.Now;

                res = VCI_GetReceiveNum(can_devtype, can_devind, can_canind);
                if (res > 0)
                {
                    // Received data
                    IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(VCI_CAN_OBJ)) * (Int32)res);
                    res = VCI_Receive(can_devtype, can_devind, can_canind, pt, res, 100);

                    for (UInt32 i = 0; i < res; i++)
                    {
                        VCI_CAN_OBJ obj = (VCI_CAN_OBJ)Marshal.PtrToStructure((IntPtr)((UInt32)pt + i * Marshal.SizeOf(typeof(VCI_CAN_OBJ))), 
                                                                              typeof(VCI_CAN_OBJ));
                        OnPrint?.Invoke(this, csCanCommFunction.PacketToMsg(obj, 0));

                        UInt32 exID = obj.ID;
                        byte[] adata = new byte[obj.DataLen];
                        for (int j = 0; j < obj.DataLen; j++) adata[j] = obj.Data[j];

                        PACKET_HEADER pHeader = csCanCommFunction.CovertPtoH(exID);

                        switch (pHeader.Index)
                        {
                            case 0x01:                  // Inventory Data Packet
                                if ((pHeader.SA == (200 + SystemId)))
                                {
                                    RxSystemId = pHeader.SA - 200;
                                    csCanCommFunction.CanInvRxProcess(RxSystemId, exID, adata, ref SystemData, aTime);
                                    OnUpdate?.Invoke(this, ref SystemData);
                                }
                                break;
                            case 0x04:                  // Firmware Update Packet
                                OnDataRecv?.Invoke(exID, adata);
                                break;
                            case 0x06:                  // Normal Packet
                                if (pHeader.SA == (200 + SystemId))
                                {
                                    RxSystemId = pHeader.SA - 200;
                                    csCanCommFunction.CanRxProcess(ref Config, RxSystemId, exID, adata, ref SystemData, aTime);

                                    OnUpdate?.Invoke(this, ref SystemData);
                                }
                                break;
                        }
                    }
                    Marshal.FreeHGlobal(pt);

                    Thread.Sleep(1);
                }
                else if (res == 0)
                {
                    TimeSpan dTime = DateTime.Now - SystemData.LastRxTime;
                    if (SystemData.ShelfCommFail == false)
                    {
                        if (dTime.TotalSeconds > 3)
                        {
                            SystemData.ShelfCommFail = true;
                            DataInit();
                        }
                    }
                    OnUpdate?.Invoke(this, ref SystemData);
                    Thread.Sleep(100);
                }
            }
        }
        #endregion

        #region DATA INIT
        private void DataInit()
        {
            DataFunction.DataInit(ref SystemData, ref Config);
        }
        #endregion
    }
}
