using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using ORiN2.interop.CAO;

namespace ORiN_TC3_Demo1
{
    public partial class frmMain : Form
    {
        const string IPADDR_BECKHOFF = "192.168.225.70";
        const string IPADDR_RC8 = "192.168.225.71";
        const string IPADDR_CANON1 = "192.168.225.72";
        const string IPADDR_CANON2 = "192.168.225.73";
        const string COM_USBCAM = "1";
        const string COM_QRREADER = "8";

        // The number of message outputs
        const int MSG_ITEM_MAX = 10;

        // The maximum or minimum value of TC3 Motor parameters
        const int TC3_VEL_MIN = -1300;
        const int TC3_VEL_MAX = 1300;
        const int TC3_POS_MIN = -300;
        const int TC3_POS_MAX = 800;

        // ORiN CAO Common
        CaoEngine caoEng;
        CaoWorkspaces caoWss;
        CaoWorkspace caoWs;
        CaoControllers caoCtrls;
        // Canon IP Camera
        CaoController[] caoCanon;
        CaoVariable[] caovView;
        CaoVariable[,] caovPTZ;
        int[, ,] ptzMinMax;
        // DENSO QRCode Scanner
        CaoController caoQR;
        // DENSO Vision
        CaoController caoVis;
        CaoFile caofImg;
        CaoFile caofShow;
        // DENSO RC8
        CaoController caoRC8;
        CaoFile caofPcs;
        CaoTask caotTsk;
        CaoVariable caovLine;
        // TC3
        CaoController caoTC3;
        CaoVariable[] caovTC3IO;
        CaoVariable caovTC3VelRead;
        CaoVariable caovTC3DirRead;
        CaoVariable caovTC3PosRead;
        // Misc.
        bool bFull = false;

        // ===== Main form event handler =====
        public frmMain()
        {
            InitializeComponent();
            pbIPCam.BackgroundImage = ConvertTransparent(pbIPCam.BackgroundImage, Color.White);
            pbUSBCam.BackgroundImage = ConvertTransparent(pbUSBCam.BackgroundImage, Color.White);
            pbQRReader.BackgroundImage = ConvertTransparent(pbQRReader.BackgroundImage, Color.White);
            pbRC8.BackgroundImage = ConvertTransparent(pbRC8.BackgroundImage, Color.White);
            pbIO.BackgroundImage = ConvertTransparent(pbIO.BackgroundImage, Color.White);
            pbMotor.BackgroundImage = ConvertTransparent(pbMotor.BackgroundImage, Color.White);
            picLayer.BackgroundImage = ConvertTransparent(picLayer.BackgroundImage, Color.White);

            this.StartPosition = FormStartPosition.Manual;
            this.Location = new Point(0, 0);
        }

        // ===== Initialize ORiN CAO Common =====
        private void frmMain_Load(object sender, EventArgs e)
        {
            caoEng = new CaoEngine();
            caoWss = caoEng.Workspaces;
            caoWs = caoWss.Item(0);
            caoCtrls = caoWs.Controllers;
        }

        // ===== Uninitialize ORiN CAO Common =====
        private void frmMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            btnDisconnectAll_Click(sender, e);

            SafeCaoControllersRelease(ref caoCtrls);
            SafeCaoWorkspaceRelease(caoWss, ref caoWs);
            SafeCaoWorkspacesRelease(ref caoWss);
            SafeCaoEngineRelease(ref caoEng);
        }

        // ===== Canon IP Camera =====
        private void btnIPCamConn_Click(object sender, EventArgs e)
        {
            try
            {
                // If already connected, then execute disconnect process
                if (caoCanon != null) throw new Exception("");

                caoCanon = new CaoController[2];
                caovView = new CaoVariable[2];
                caovPTZ = new CaoVariable[2, 3];
                ptzMinMax = new int[2, 3, 2];

                bool bFlag = false, bTmp;
                for (int i = 0; i < 2; i++)
                {
                    bTmp = false;

                    try
                    {
                        // Connect Canon.WebView provider
                        string strAddr = (i == 0) ? IPADDR_CANON1 : IPADDR_CANON2;
                        caoCanon[i] = caoWs.AddController("Livescope" + (i + 1).ToString(), "CaoProv.Canon.WebView", "", "Timeout=1000,Server=" + strAddr);

                        // Get image objects
                        caovView[i] = caoCanon[i].AddVariable("Image");

                        // Get control objects
                        CaoVariables caoVars = caoCanon[i].Variables;
                        CaoVariable caoVar;
                        string[] strNames = new string[] { "PAN", "TILT", "ZOOM" };
                        for (int j = 0; j < 3; j++)
                        {
                            caovPTZ[i, j] = caoCanon[i].AddVariable(strNames[j]);

                            caoVar = caoCanon[i].AddVariable(strNames[j] + "_MIN");
                            ptzMinMax[i, j, 0] = (int)caoVar.Value;
                            SafeCaoVariableRelease(caoVars, ref caoVar);

                            caoVar = caoCanon[i].AddVariable(strNames[j] + "_MAX");
                            ptzMinMax[i, j, 1] = (int)caoVar.Value;
                            SafeCaoVariableRelease(caoVars, ref caoVar);
                        }

                        SafeCaoVariablesRelease(ref caoVars);

                        bTmp = true;
                    }
                    catch { }
                    finally
                    {
                        if (i == 0)
                        {
                            rdoIPCam1.Enabled = bTmp;
                            if (rdoIPCam1.Enabled)
                                rdoIPCam1.Select();
                        }
                        else
                        {
                            rdoIPCam2.Enabled = bTmp;
                            if (!rdoIPCam1.Enabled && rdoIPCam2.Enabled)
                                rdoIPCam2.Select();
                        }

                        bFlag |= bTmp;
                    }
                }

                if (!bFlag) throw new Exception("");

                rdoIPCam1_CheckedChanged(this, e);
                SwitchConnectButton(true, btnIPCamConn, lblIPCam, timIPCam);
                SwitchControlButton(true, btnIPCamConn);
            }
            catch (Exception ex)
            {
                rdoIPCam1.Enabled = false;
                rdoIPCam2.Enabled = false;
                SwitchControlButton(false, btnIPCamConn);
                SwitchConnectButton(false, btnIPCamConn, lblIPCam, timIPCam);

                // Disconnect process
                if (caoCanon != null)
                {
                    for (int i = 0; i < 2; i++)
                    {
                        if (caoCanon[i] != null)
                        {
                            CaoVariables caoVars = caoCanon[i].Variables;

                            for (int j = 0; j < 3; j++)
                            {
                                SafeCaoVariableRelease(caoVars, ref caovPTZ[i, j]);
                            }

                            SafeCaoVariableRelease(caoVars, ref caovView[i]);
                            SafeCaoVariablesRelease(ref caoVars);
                            SafeCaoControllerRelease(caoCtrls, ref caoCanon[i]);
                        }
                    }

                    ptzMinMax = null;
                    caovPTZ = null;
                    caovView = null;
                    caoCanon = null;
                }

                AddToMessagePane("IPCam", ex.Message);
            }
        }

        // ===== Timer for viewing Canon IP Camera =====
        private void timIPCam_Tick(object sender, EventArgs e)
        {
            Application.DoEvents();
            try
            {
                // Show the view of IP camera which selected in radio button
                byte[] bytImage = caovView[rdoIPCam1.Checked ? 0 : 1].Value;
                MemoryStream mstImage = new MemoryStream(bytImage);

                picIPCam.Image = Image.FromStream(mstImage);
            }
            catch (Exception ex)
            {
                AddToMessagePane("IPCam", ex.Message);
            }
        }

        private void rdoIPCam1_CheckedChanged(object sender, EventArgs e)
        {
            Application.DoEvents();
            try
            {
                int index = rdoIPCam1.Checked ? 0 : 1;

                trkPan.Minimum = ptzMinMax[index, 0, 0];
                trkPan.Maximum = ptzMinMax[index, 0, 1];
                trkPan.Value = caovPTZ[index, 0].Value;

                trkTilt.Minimum = ptzMinMax[index, 1, 0];
                trkTilt.Maximum = ptzMinMax[index, 1, 1];
                trkTilt.Value = caovPTZ[index, 1].Value;

                trkZoom.Minimum = ptzMinMax[index, 2, 0];
                trkZoom.Maximum = ptzMinMax[index, 2, 1];
                trkZoom.Value = caovPTZ[index, 2].Value;
            }
            catch (Exception ex)
            {
                AddToMessagePane("IPCam", ex.Message);
            }
        }

        private void trkPTZ_Changed(object sender, EventArgs e)
        {
            Application.DoEvents();
            try
            {
                int index = rdoIPCam1.Checked ? 0 : 1;

                trkPan.Minimum = ptzMinMax[index, 0, 0];
                trkPan.Maximum = ptzMinMax[index, 0, 1];
                trkPan.Value = caovPTZ[index, 0].Value;

                trkTilt.Minimum = ptzMinMax[index, 1, 0];
                trkTilt.Maximum = ptzMinMax[index, 1, 1];
                trkTilt.Value = caovPTZ[index, 1].Value;

                trkZoom.Minimum = ptzMinMax[index, 2, 0];
                trkZoom.Maximum = ptzMinMax[index, 2, 1];
                trkZoom.Value = caovPTZ[index, 2].Value;
            }
            catch (Exception ex)
            {
                AddToMessagePane("IPCam", ex.Message);
            }
        }

        // ===== DENSO QRCode Scanner =====
        private delegate void SetTextCallback(string msg); // Delegate for processing OnMessage

        private void btnQR_Click(object sender, EventArgs e)
        {
            try
            {
                // If already connected, then execute disconnect process
                if (caoQR != null) throw new Exception("");

                // Connect QRCode provider
                caoQR = caoWs.AddController("QR", "CaoProv.DENSO.QRCode", "", "Conn=com:" + COM_QRREADER + ", Mode=5");

                // Add event handler of OnMessage
                caoQR.OnMessage += new _ICaoControllerEvents_OnMessageEventHandler(OnMessageQR);

                SwitchConnectButton(true, btnQR, lblQR);
            }
            catch (Exception ex)
            {
                SwitchConnectButton(false, btnQR, lblQR);

                if (caoQR != null)
                {
                    caoQR.OnMessage -= new _ICaoControllerEvents_OnMessageEventHandler(OnMessageQR);

                    // Disconnect process
                    SafeCaoControllerRelease(caoCtrls, ref caoQR);
                }

                AddToMessagePane("QR", ex.Message);
            }
        }

        private void OnMessageQR(CaoMessage pICaoMsg)
        {
            try
            {
                // Processing OnMessage event with another thread
                SetTextCallback SetMsg = new SetTextCallback(SetQRText);
                Invoke(SetMsg, pICaoMsg.Value.ToString());
            }
            catch (Exception ex)
            {
                AddToMessagePane("QR", ex.Message);
            }
        }

        private void SetQRText(string msg)
        {
            txtQR.Text = msg;
        }

        // ===== DENSO Vision (RC Vision) =====
        private void btnVisConn_Click(object sender, EventArgs e)
        {
            try
            {
                // If already connected, then execute disconnect process
                if (caoVis != null) throw new Exception("");

                // Connect OpenCV provider
                caoVis = caoWs.AddController("Vis", "CaoProv.OpenCV", "", "");
                
                // Get image objects
                caofImg = caoVis.AddFile("Picture", "ID=" + COM_USBCAM);
                caofShow = caoVis.AddFile("Show", "ID=101");
                
                WaitCameraConnect(100, 50);

                SwitchConnectButton(true, btnVisConn, lblVis, timVis);
            }
            catch (Exception ex)
            {
                SwitchConnectButton(false, btnVisConn, lblVis, timVis);

                // Disconnect process
                if (caoVis != null)
                {
                    CaoFiles caoFls = caoVis.Files;
                    SafeCaoFileRelease(caoFls, ref caofImg);
                    SafeCaoFileRelease(caoFls, ref caofShow);
                    SafeCaoFilesRelease(ref caoFls);
                    SafeCaoControllerRelease(caoCtrls, ref caoVis);
                }

                AddToMessagePane("Vision", ex.Message);
            }
        }

        private void timVis_Tick(object sender, EventArgs e)
        {
            Application.DoEvents();
            try
            {
                MakeShowImage();

                // Show the view of camera
                byte[] bytImage = caofShow.Value;
                MemoryStream mstImage = new MemoryStream(bytImage);

                picVis.Image = Image.FromStream(mstImage);
            }
            catch (Exception ex) {
                AddToMessagePane("Vision", ex.Message);
            }
        }

        // Wait until the camera has been connected
        private void WaitCameraConnect(int iTime, int iMaxCount)
        {
            int iCount = 0;

            while (true)
            {
                if ((bool)caofImg.Execute("IsEmpty") == false)
                {
                    break;
                }
                else if (iCount >= iMaxCount)
                {
                    break;
                }
                else
                {
                    System.Threading.Thread.Sleep(iTime);
                    iCount++;
                }
            }
        }

        // Make image which will show with some filters
        private void MakeShowImage()
        {
            if (rdoVisNormal.Checked) // Show natural view of camera
            {
                caofImg.Execute("Copy", new object[] { caofShow.ID });
            }
            else if (rdoVisGray.Checked) // Show view of camera converting to grayscale
            {
                caofImg.Execute("ConvertGray", new object[] { caofShow.ID });
            }
            else // if (rdoVisEdge.Checked) // Show view of camera with edge enhancement
            {
                caofImg.Execute("Laplace", new object[] { caofShow.ID, 3 });
            }

            if (chkVisHist.Checked) // Show view of camera with its histgram
            {
                double[] dHistList = caofShow.Execute("CalcHistEx", new object[] { 256 });
                double dStepWidth = (double)picVis.Width / dHistList.Length;

                for (int i = 0; i < dHistList.Length; i++)
                {
                    caofShow.Execute("Rectangle", new object[] { caofShow.ID, i * dStepWidth, picVis.Height - dHistList[i] / 10, (i + 1) * dStepWidth, picVis.Height, 128, 128, 128, -1, 0 });
                }
            }
        }

        // ===== DENSO Robot =====
        private void btnRoboConn_Click(object sender, EventArgs e)
        {
            try
            {
                // If already connected, then execute disconnect process
                if (caoRC8 != null) throw new Exception("");

                // Connect RC8 provider
                caoRC8 = caoWs.AddController("", "CaoProv.DENSO.RC8", "", "Server=" + IPADDR_RC8);

                ShowPCSList();
                SwitchConnectButton(true, btnRoboConn, lblRobo);
                SwitchControlButton(true, btnRoboConn);
            }
            catch (Exception ex)
            {
                SwitchControlButton(false, btnRoboConn);
                SwitchConnectButton(false, btnRoboConn, lblRobo, timRobot);
                ClearPCSList();

                // Disconnect process
                SafeCaoControllerRelease(caoCtrls, ref caoRC8);

                AddToMessagePane("Robot", ex.Message);
            }
        }

        // Start task
        private void btnRoboStart_Click(object sender, EventArgs e)
        {
            try
            {
                caotTsk.Start(1);
                timRobot.Enabled = true;
            }
            catch (Exception ex)
            {
                AddToMessagePane("Robot", ex.Message);
            }
        }

        // Stop task
        private void btnRoboStop_Click(object sender, EventArgs e)
        {
            try
            {
                caotTsk.Stop(0);
                timRobot.Enabled = false;
            }
            catch (Exception ex)
            {
                AddToMessagePane("Robot", ex.Message);
            }
        }

        // Highlight the executing line of task
        private void timRobot_Tick(object sender, EventArgs e)
        {
            Application.DoEvents();
            try
            {
                // If the task is not running, then stop timer
                //if (caotTsk.Execute("GetStatus") != 3) timRobot.Enabled = false;

                // Get the executing line
                int[] iLineNo = caovLine.Value;

                int i, iStart = 0;
                for (i = 0; i < iLineNo[0] - 1; i++)
                {
                    iStart += (txtPCSValue.Lines[i].Length + 1);
                }

                txtPCSValue.SelectAll();
                txtPCSValue.SelectionBackColor = SystemColors.Control;

                txtPCSValue.Select(iStart, txtPCSValue.Lines[i].Length + 1);
                txtPCSValue.SelectionBackColor = Color.Yellow;
                txtPCSValue.ScrollToCaret();
            }
            catch (Exception ex)
            {
                timRobot.Enabled = false;
                AddToMessagePane("Robot", ex.Message);
            }
        }

        // Change task
        void cmbProgList_SelectedIndexChanged(object sender, System.EventArgs e)
        {
            try
            {
                timRobot.Enabled = false;

                if (caovLine != null)
                {
                    CaoVariables caoVars = caotTsk.Variables;
                    SafeCaoVariableRelease(caoVars, ref caovLine);
                    SafeCaoVariablesRelease(ref caoVars);
                }

                if (caotTsk != null)
                {
                    CaoTasks caoTsks = caoRC8.Tasks;
                    SafeCaoTaskRelease(caoTsks, ref caotTsk);
                    SafeCaoTasksRelease(ref caoTsks);
                }

                if (caofPcs != null)
                {
                    CaoFiles caoFls = caoRC8.Files;
                    SafeCaoFileRelease(caoFls, ref caofPcs);
                    SafeCaoFilesRelease(ref caoFls);
                }

                if (cmbProgList.SelectedIndex > -1)
                {
                    string[] strTmp = ((string)cmbProgList.SelectedItem).Split('.');
                    caofPcs = caoRC8.AddFile(strTmp[0] + ".pcs");
                    caotTsk = caoRC8.AddTask(strTmp[0]);
                    caovLine = caotTsk.AddVariable("@LINE_NO");

                    string[] strPCSText = new string[]{caofPcs.Value};
                    
                    txtPCSValue.Lines = strPCSText;
                }
            }
            catch (Exception ex)
            {
                AddToMessagePane("Robot", ex.Message);
            }
        }

        // Show all tasks in the connected controller
        private void ShowPCSList()
        {
            try
            {
                cmbProgList.Items.Clear();

                string[] strPCSNames = caoRC8.get_FileNames();
                for (int i = 0; i < strPCSNames.Length; i++)
                {
                    if (string.Compare(".pcs", System.IO.Path.GetExtension(strPCSNames[i])) == 0)
                    {
                        cmbProgList.Items.Add(strPCSNames[i]);
                    }
                }
                
                cmbProgList.SelectedIndex = (cmbProgList.Items.Count != 0) ? 0 : -1;
            }
            catch (Exception ex)
            {
                ClearPCSList();
                AddToMessagePane("Robot", ex.Message);
            }
        }

        // Clear task lists
        private void ClearPCSList()
        {
            cmbProgList.SelectedIndex = -1;
            cmbProgList.Items.Clear();
            cmbProgList.Text = "";
            txtPCSValue.Lines = new string[] { };
        }

        // ===== EtherCAT Device =====
        private void btnTC3Conn_Click(object sender, EventArgs e)
        {
            try
            {
                // If already connected, then execute disconnect process
                if (caoTC3 != null) throw new Exception("");

                // Connect Beckhoff.ADS provider
                caoTC3 = caoWs.AddController("TC3", "CaoProv.Beckhoff.ADS", "", "Port=851");

                // Get variable object
                caovTC3IO = new CaoVariable[5];
                caovTC3IO[0] = caoTC3.AddVariable("Main.bEl2008_01");
                caovTC3IO[1] = caoTC3.AddVariable("Main.bEl2008_02");
                caovTC3IO[2] = caoTC3.AddVariable("Main.bEl2008_03");
                caovTC3IO[3] = caoTC3.AddVariable("Main.bEl2008_04");
                caovTC3IO[4] = caoTC3.AddVariable("Main.bEl2008_05");

                caovTC3VelRead = caoTC3.AddVariable("Main.UI_ActVelocity");
                caovTC3DirRead = caoTC3.AddVariable("Main.UI_MotorMode");
                caovTC3PosRead = caoTC3.AddVariable("Main.UI_ActPosition");

                // Reflect range of velocity and position
                prgVelocity.Maximum = TC3_VEL_MAX - TC3_VEL_MIN;
                prgPosition.Maximum = TC3_POS_MAX - TC3_POS_MIN;

                SwitchConnectButton(true, btnTC3Conn, lblTC3, timTC3);
                SwitchControlButton(true, btnTC3Conn);
            }
            catch (Exception ex)
            {
                SwitchControlButton(false, btnTC3Conn);
                SwitchConnectButton(false, btnTC3Conn, lblTC3, timTC3);

                // Disconnect process
                if (caoTC3 != null)
                {
                    CaoVariables caoVars = caoTC3.Variables;
                    if (caovTC3IO != null)
                    {
                        for (int i = 0; i < caovTC3IO.Length; i++)
                        {
                            SafeCaoVariableRelease(caoVars, ref caovTC3IO[i]);
                        }
                        caovTC3IO = null;
                    }

                    SafeCaoVariableRelease(caoVars, ref caovTC3VelRead);
                    SafeCaoVariableRelease(caoVars, ref caovTC3DirRead);
                    SafeCaoVariableRelease(caoVars, ref caovTC3PosRead);

                    SafeCaoVariablesRelease(ref caoVars);

                    SafeCaoControllerRelease(caoCtrls, ref caoTC3);
                }

                AddToMessagePane("IPC", ex.Message);
            }
        }

        // Watch timer for TwinCAT3
        private void timTC3_Tick(object sender, EventArgs e)
        {
            Application.DoEvents();
            try
            {
                // Get object values
                rcDI1.FillColor = caovTC3IO[0].Value ? System.Drawing.Color.LimeGreen : System.Drawing.Color.Gray;
                rcDI2.FillColor = caovTC3IO[1].Value ? System.Drawing.Color.LimeGreen : System.Drawing.Color.Gray;
                rcDI3.FillColor = caovTC3IO[2].Value ? System.Drawing.Color.LimeGreen : System.Drawing.Color.Gray;
                rcDI4.FillColor = caovTC3IO[3].Value ? System.Drawing.Color.LimeGreen : System.Drawing.Color.Gray;
                rcDI5.FillColor = caovTC3IO[4].Value ? System.Drawing.Color.LimeGreen : System.Drawing.Color.Gray;
             
                prgVelocity.Value = (int)caovTC3VelRead.Value - TC3_VEL_MIN;
                txtVelocity.Text = Double2String(caovTC3VelRead.Value);
                lblDirection.Text = caovTC3DirRead.Value;
                prgPosition.Value = (int)caovTC3PosRead.Value - TC3_POS_MIN;
                txtPosition.Text = Double2String(caovTC3PosRead.Value);
            }
            catch (Exception ex)
            {
                AddToMessagePane("IPC", ex.Message);
            }
        }

        // ===== Utility Function =====
        private void AddToMessagePane(string devName, string msg)
        {
            if (lstMsgPane.Items.Count < MSG_ITEM_MAX) {
                if (string.Compare(msg, "") != 0)
                {
                    lstMsgPane.Items.Add(DateTime.Now + " " + devName + ": " + msg);
                }
            }
            else {
                if (bFull == false) {
                    lstMsgPane.Items.Add("(Full)");
                    bFull = true;
                }
            }
        }

        private void SwitchConnectButton(bool bConnect, Button btnConnect, Label lblLED, Timer timSampling = null)
        {
            if (bConnect) {
                if (timSampling != null) timSampling.Enabled = true;
                btnConnect.Text = "Disconnect";
                lblLED.BackColor = Color.Lime;
            }
            else {
                if (timSampling != null) timSampling.Enabled = false;
                btnConnect.Text = "Connect";
                lblLED.BackColor = Color.Gray;
            }
        }

        private void SwitchControlButton(bool bEnabled, Button btnConnect)
        {
            if (btnConnect.Equals(btnIPCamConn))
            {
                trkPan.Enabled = bEnabled;
                trkTilt.Enabled = bEnabled;
                trkZoom.Enabled = bEnabled;
            }
            else if (btnConnect.Equals(btnRoboConn))
            {
                btnRoboStart.Enabled = bEnabled;
                btnRoboStop.Enabled = bEnabled;
            }
        }

        private string Double2String(double dblVal)
        {
            string[] strTmp;

            strTmp = dblVal.ToString().Split('.');
            if (strTmp.Length == 2)
            {
                return strTmp[0] + "." + strTmp[1].Substring(0, 2);
            }
            else
            {
                return strTmp[0] + ".00";
            }
        }

        // Connect all providers
        private void btnConnectAll_Click(object sender, EventArgs e)
        {
            if (lblIPCam.BackColor == Color.Gray) btnIPCamConn_Click(sender, e);
            if (lblVis.BackColor == Color.Gray) btnVisConn_Click(sender, e);
            if (lblQR.BackColor == Color.Gray) btnQR_Click(sender, e);
            if (lblRobo.BackColor == Color.Gray) btnRoboConn_Click(sender, e);
            if (lblTC3.BackColor == Color.Gray) btnTC3Conn_Click(sender, e);
        }

        // Disconnect all providers
        private void btnDisconnectAll_Click(object sender, EventArgs e)
        {
            if (lblIPCam.BackColor == Color.Lime) btnIPCamConn_Click(sender, e);
            if (lblVis.BackColor == Color.Lime) btnVisConn_Click(sender, e);
            if (lblQR.BackColor == Color.Lime) btnQR_Click(sender, e);
            if (lblRobo.BackColor == Color.Lime) btnRoboConn_Click(sender, e);
            if (lblTC3.BackColor == Color.Lime) btnTC3Conn_Click(sender, e);
        }

        // Exit this application
        private void btnClose_Click(object sender, EventArgs e)
        {
            Close();
        }

        // ===== COM Utility Function =====
        private void SafeCaoEngineRelease(ref CaoEngine eng)
        {
            if (eng != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(eng);
                eng = null;
            }
        }

        private void SafeCaoWorkspacesRelease(ref CaoWorkspaces wss)
        {
            if (wss != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(wss);
                wss = null;
            }
        }

        private void SafeCaoWorkspaceRelease(CaoWorkspaces wss, ref CaoWorkspace ws)
        {
            if (wss != null && ws != null)
            {
                wss.Remove(ws.Index);

                System.Runtime.InteropServices.Marshal.ReleaseComObject(ws);
                ws = null;
            }
        }

        private void SafeCaoControllersRelease(ref CaoControllers ctrls)
        {
            if (ctrls != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(ctrls);
                ctrls = null;
            }
        }

        private void SafeCaoControllerRelease(CaoControllers ctrls, ref CaoController ctrl)
        {
            if (ctrls != null && ctrl != null)
            {
                ctrls.Remove(ctrl.Index);

                System.Runtime.InteropServices.Marshal.ReleaseComObject(ctrl);
                ctrl = null;
            }
        }

        private void SafeCaoFilesRelease(ref CaoFiles files)
        {
            if (files != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(files);
                files = null;
            }
        }

        private void SafeCaoFileRelease(CaoFiles files, ref CaoFile file)
        {
            if (files != null && file != null)
            {
                files.Remove(file.Index);

                System.Runtime.InteropServices.Marshal.ReleaseComObject(file);
                file = null;
            }
        }

        private void SafeCaoTasksRelease(ref CaoTasks tasks)
        {
            if (tasks != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(tasks);
                tasks = null;
            }
        }

        private void SafeCaoTaskRelease(CaoTasks tasks, ref CaoTask task)
        {
            if (tasks != null && task != null)
            {
                tasks.Remove(task.Index);

                System.Runtime.InteropServices.Marshal.ReleaseComObject(task);
                task = null;
            }
        }

        private void SafeCaoVariablesRelease(ref CaoVariables vars)
        {
            if (vars != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(vars);
                vars = null;
            }
        }

        private void SafeCaoVariableRelease(CaoVariables vars, ref CaoVariable var)
        {
            if (vars != null && var != null)
            {
                vars.Remove(var.Index);

                System.Runtime.InteropServices.Marshal.ReleaseComObject(var);
                var = null;
            }
        }
    }
}
