using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

/*
 * 工程描述：模拟串口调试工具
 *           (1) 波特率设置(2)ASCII码及十六进制显示(3)串口发送及接收
 * 修改日期：2009-8-24
 * 
 */
namespace SerialPortCommunicationModel
{
    public partial class frmSerialPort : Form
    {
        //--------------------------------------------------------------------------------------------------
        private byte[] bRcvBuffer = new byte[1024];//串口接收缓冲区
        private byte[] bSndBuffer = new byte[1024];//串口发送缓冲区
        bool bHexShow = false;//十六进制显示

        delegate void SetTextStringCallBack(RichTextBox richTextBox, string rText);//跨线程设置RichTextBox
        delegate void SetRichTextBoxStringCallBack(RichTextBox richTextBox, byte[] bData, int length, bool bHex);//
        delegate void SetLabelCallBack(Label label, string lableStr);//跨线程设置Label
        
        //--------------------------------------------------------------------------------------------------

        //初始化
        public frmSerialPort()
        {
            InitializeComponent();
            this.timer1.Tick += new EventHandler(btnSndData_Click);//每过一秒即更新时间
        }

        //加载窗体
        private void frmSerialPort_Load(object sender, EventArgs e)
        {
            this.rTxtBoxRcvData.ReadOnly = true; //接收文本框只读

            ComboBoxComNameSetting(comboBoxComName);//初始化串口名下拉框
            ComboxBaudSetting(comboBoxBaud);//初始化波特率下拉框
            SerialPortSetting(serialPortCom);//串口参数初始化
            
            this.btnOpenSerialPort.Enabled = true;
            this.btnCloseSerialPort.Enabled = false;

            this.StatusLabelComRcvData.Text = "串口未接收到数据!";
            this.StatusLabelComState.Text = "串口已关闭!";
        }

        //====================================================================================================================
        
        //初始化串口名设置
        public void ComboBoxComNameSetting(ComboBox comBoxComNamePara)
        {
            comBoxComNamePara.Text = "COM1";
            comBoxComNamePara.Items.Add("COM1");
            //comBoxComNamePara.Items.Add("COM2");
        }

        //初始化波特率设置
        public void ComboxBaudSetting(ComboBox comBoxBaudPara)
        {
            comBoxBaudPara.Text = "9600";
            comboBoxBaud.Items.Add("4800");
            comboBoxBaud.Items.Add("9600");
            comboBoxBaud.Items.Add("14400");
            comboBoxBaud.Items.Add("19200");
            comboBoxBaud.Items.Add("38400");
            comboBoxBaud.Items.Add("56000");
            comboBoxBaud.Items.Add("57600");
            comboBoxBaud.Items.Add("115200");
            comboBoxBaud.Items.Add("128000");
        }

        //初始化串口参数
        public void SerialPortSetting(System.IO.Ports.SerialPort serialPortPara)
        {
            serialPortPara.PortName = "COM1";//COM端口号设置
            serialPortPara.BaudRate = 9600;//波特率
            serialPortPara.DataBits = 8;//数据位数
            serialPortPara.StopBits = System.IO.Ports.StopBits.One;//停止位
            serialPortPara.Parity = System.IO.Ports.Parity.None;//奇偶校验
            
            serialPortPara.ReadBufferSize = 4096;//读缓冲区大小
            serialPortPara.ReadTimeout = 4000;//读超时时间
            serialPortPara.ReceivedBytesThreshold = 1;//接收到2个可用字节就产生接受中断

            serialPortPara.WriteBufferSize = 4096;//写缓冲区大小
            serialPortPara.WriteTimeout = 4000;//写超时时间

            //serialPortPara.RtsEnable = true;//

            if (serialPortPara.IsOpen)
            {
                serialPortPara.Close();//打开串口串口
            }
        }
        
        //初始化波特率设置
        private void comboBoxBaud_SelectedIndexChanged(object sender, EventArgs e)
        {
            bool bIsOpen = this.serialPortCom.IsOpen;
            if (bIsOpen)
            {
                this.serialPortCom.Close();
            }
            serialPortCom.BaudRate = int.Parse(comboBoxBaud.Text);
            if (bIsOpen)
            {
                this.serialPortCom.Open();
            }
        }

        //串口名设置
        private void comboBoxComName_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (serialPortCom.IsOpen)
            {
                MessageBox.Show("串口打开时,不能设置串口参数!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            serialPortCom.PortName = comboBoxComName.Text;
        }

        //打开串口
        private void btnOpenSerialPort_Click(object sender, EventArgs e)
        {
            try
            {
                if (!this.serialPortCom.IsOpen)
                {
                    this.serialPortCom.PortName = this.comboBoxComName.Text;
                    this.serialPortCom.BaudRate = int.Parse(this.comboBoxBaud.Text);

                    this.serialPortCom.Open();

                    this.btnOpenSerialPort.Enabled = false;
                    this.btnCloseSerialPort.Enabled = true;

                    this.StatusLabelComState.Text = "串口已经打开!";
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "串口打开", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        //关闭串口
        private void btnCloseSerialPort_Click(object sender, EventArgs e)
        {
            if (this.serialPortCom.IsOpen)
            {
                this.serialPortCom.DiscardInBuffer();//丢弃接收缓冲区的数据
                this.serialPortCom.DiscardOutBuffer();//丢弃发送缓冲区的数据
                this.serialPortCom.Dispose();//释放串行通信的所有资源
                this.serialPortCom.Close();//关闭端口连接

                this.btnOpenSerialPort.Enabled = true;
                this.btnCloseSerialPort.Enabled = false;

                this.StatusLabelComState.Text = "串口已经关闭!";
            }
            this.timer1.Stop();
        }

        //串口发送数据
        private void btnSndData_Click(object sender, EventArgs e)
        {
            if (this.rTxtBoxSndData.Text != "")//textbox中有默认的值，所以刚开始执行时总会执行到这一步
            {
                this.rTxtBoxSndData.Text ="";//要发送日期，清空默认值
                int month, day,hour;
                month = Convert.ToInt16(System.DateTime.Today.Month);
                day = Convert.ToInt16(System.DateTime.Today.Day);
                hour = Convert.ToInt16(System.DateTime.Now.Hour);
                if (month > 9)
                {
                    if (day > 9)
                        this.rTxtBoxSndData.Text = System.DateTime.Now.Date.ToShortDateString() + "      ";//保证第一行的十六个字节，不足部分用空格填充
                    else
                        this.rTxtBoxSndData.Text = System.DateTime.Now.Date.ToShortDateString()+ "       ";
                }
                else
                {
                    if (day > 9)
                        this.rTxtBoxSndData.Text = System.DateTime.Now.Date.ToShortDateString()+ "       ";
                    else
                        this.rTxtBoxSndData.Text = System.DateTime.Now.Date.ToShortDateString()+ "        ";
                }

                if (hour > 9)
                    this.rTxtBoxSndData.Text += System.DateTime.Now.ToLongTimeString() + "        ";
                else
                    this.rTxtBoxSndData.Text += System.DateTime.Now.ToLongTimeString() + "         ";//时间部分按照时分秒都是双位的来算
               
                this.serialPortCom.Write(Encoding.ASCII.GetBytes(this.rTxtBoxSndData.Text), 0,
                                                                this.rTxtBoxSndData.Text.Length);//发送三十二位的数据，与低端程序一致
              
            }
        }

        //清空发送输入框
        private void btnClearSnd_Click(object sender, EventArgs e)
        {
            this.rTxtBoxSndData.Text = "";
        }

        //串口接收数据
        private void serialPortCom_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            if (!serialPortCom.IsOpen)
            {//串口未打开
                return;
            }

            //同步阻塞读取
            try
            {
                int index = 0;
                byte temp;

                while (this.serialPortCom.BytesToRead > 0)
                {
                    //使用SerialPort.ReadByte方法
                    for (int i = 0; i < this.serialPortCom.BytesToRead; i++)
                    {
                        temp = (Byte)this.serialPortCom.ReadByte();
                        bRcvBuffer[index] = temp;
                        index++;
                    }
                    System.Threading.Thread.Sleep(10);      //延时一定时间用于接收数据
                }

                if (index > 0)
                {//接收到数据
                    SetTextString(this.rTxtBoxRcvData, bRcvBuffer, index, bHexShow);
                    this.StatusLabelComRcvData.Text = "串口接收到数据!";
                }
            }
            catch (Exception ex)
            {
                //SetStatusStripLabel(this.StatusLabelComState,ex.ToString());
                this.StatusLabelComRcvData.Text = ex.ToString();
            }
        }

        //清空接收框
        private void btnClearRcv_Click(object sender, EventArgs e)
        {
            this.rTxtBoxRcvData.Text = "";
        }

        //====================================================================================================================

        //显示接收内容,字符串
        private void SetTextString(RichTextBox richTextBox,string rText)
        {
            //如果调用方位于创建该控件线程以外的线程时，需要获取是否要调用Invoke方法
            if (richTextBox.InvokeRequired)
            {
                SetTextStringCallBack setTxtStringCallBack = new SetTextStringCallBack(SetTextString);
                this.Invoke(setTxtStringCallBack, new object[] { richTextBox, rText });
            }
            else
            {
                //如果不是线程外的调用时：设置文本框的值
                richTextBox.Text += rText;

                //使滚动条始终在最底部
                SetTextScrollToBottom(richTextBox);
            }
        }

        //richTextBox显示接收内容
        //入口：richTextBox ==> 寄托控件
        //      rData       ==> 待显示数据
        //      length      ==> 待显示数据长度
        //      bHex        ==> 显示形式 true：十六进制；false：原样显示
        private void SetTextString(RichTextBox richTextBox, byte[] rData, int length,bool bHex)
        {
            //如果调用方位于创建该控件线程以外的线程时，需要获取是否要调用Invoke方法
            if (richTextBox.InvokeRequired)
            {
                SetRichTextBoxStringCallBack setTxtStrCallBack = new SetRichTextBoxStringCallBack(SetTextString);
                this.Invoke(setTxtStrCallBack, new object[] { richTextBox, rData, length, bHex });
            }
            else
            {
                //如果不是线程外的调用时：设置文本框的值
                string str = string.Empty;
                byte[] bRcvBufferCpy = new byte[1024];

                //拷贝到临时缓冲区中
                for (int i = 0; i < length; i++)
                {
                    bRcvBufferCpy[i] = bRcvBuffer[i];
                }

                //根据bHex的值，显示接收到的数据到屏幕上。
                //bHex=>true：十六进制的形式显示(如：字符“1”==>31)；false:原样显示
                if (bHex)
                {
                    str = Bytes2HexString(bRcvBufferCpy, length);//显转化后的16进制字符串
                }
                else
                {
                    str = Encoding.ASCII.GetString(rData, 0, length);//原样显示
                }
                richTextBox.Text += str;//在文本框中显示

                //使滚动条始终在最底部
                SetTextScrollToBottom(richTextBox);
            }
        }

        //设置Lable内容
        private void SetLabel(Label label,string labelStr)
        {
            //如果调用方位于创建该控件线程以外的线程时，需要获取是否要调用Invoke方法
            //如果这个程序和键盘程序的C语言一起用的话，就需要调用Invoke方法
            if (label.InvokeRequired)
            {
                SetLabelCallBack setLabelCallBack = new SetLabelCallBack(SetLabel);
                this.Invoke(setLabelCallBack, new object[] { label,labelStr });
            }
            else
            {
                //如果不是线程外的调用时：设置文本框的值
                label.Text = labelStr;
            }
        }

        //设置文本框显示末端
        void SetTextScrollToBottom(RichTextBox rtxtBox)
        {
            rtxtBox.SelectionStart = rtxtBox.Text.Length;
            rtxtBox.SelectionLength = 0;//选定的字符书
            rtxtBox.ScrollToCaret();
        }
        
        //将字节数组转化成16进制字符串
        private string Bytes2HexString(byte[] bData,int length)
        {
            string strData = "";
            //保存接收到的数据
            for (int i = 0; i < length; i++)
            {
                if (bData[i] < 16)
                {
                    strData += ("0" + bData[i].ToString("X"));
                }
                else
                {
                    strData += (bData[i].ToString("X"));
                }
                strData += " ";
            }
            return strData;
        }
       
        //十六进制显示复选
        private void checkBoxHexShow_CheckedChanged(object sender, EventArgs e)
        {
            bHexShow = this.checkBoxHexShow.Checked;
            string txtStr = this.rTxtBoxRcvData.Text;

            //将文本框中的数据，转换成十六进制显示
            if (bHexShow)
            {
                this.rTxtBoxRcvData.Text = DecStr2HexStr(txtStr);
            }
            else
            {
                this.rTxtBoxRcvData.Text = HexStr2DecStr(txtStr);
            }
        }

        //将十进制显示的字符串转换成十六进制形式
        private string DecStr2HexStr(string decStr)
        {
            string strReslt = "";
            byte[] bytesDec = Encoding.ASCII.GetBytes(decStr);

            for (int i = 0; i < bytesDec.Length; i++)
            {
                if ((bytesDec[i] == 0x0d) || (bytesDec[i] == 0x0a))
                {
                    continue;
                }

                if (bytesDec[i] < 16)
                {
                    strReslt += ("0" + bytesDec[i].ToString("X"));
                }
                else
                {
                    strReslt += bytesDec[i].ToString("X");
                }
                strReslt += " ";
            }
            return strReslt;
        }

        //将十六进制显示的字符串转换成十进制形式
        //入口：hexStr格式为 ‘0’~‘9’，‘A’~‘F’及‘a’~‘f’
        private string HexStr2DecStr(string hexStr)
        {
            string strReslt = "";
            byte[] bytesDec = Encoding.ASCII.GetBytes(hexStr);

            string[] hexStrSplit = hexStr.Split(' ', '\t', '\r', '\n');

            try
            {
                for (int i = 0; i < hexStrSplit.Length; i++)
                {
                    int iHex = 0;
                    for (int j = 0; j < hexStrSplit[i].Length; j++)
                    {
                        char chr = hexStrSplit[i][j];
                        int tmpChr = 0;
                        if (chr >= '0' && chr <= '9')
                        {
                            tmpChr = chr - '0';
                        }
                        else if(chr >= 'A' && chr <= 'F')
                        {
                            tmpChr = chr - 'A' + 10;
                        }
                        else if (chr >= 'a' && chr <= 'f')
                        {
                            tmpChr = chr - 'a' + 10;
                        }
                        iHex = iHex * 16 + tmpChr;
                        //iHex = iHex * 16 + int.Parse(hexStrSplit[i].Substring(j, 1));
                    }
                    strReslt += (Convert.ToChar(iHex)).ToString();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "转十六进制", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            return strReslt;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (!this.serialPortCom.IsOpen)
            {
                MessageBox.Show("请先打开串口!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            else
              this.timer1.Start();
        }
    }
}



/*
 
        delegate void SetTextBytesStringCallBack(RichTextBox richTextBox,byte[] bData, int length);
        delegate void SetTextHexStringCallBack(RichTextBox richTextBox, byte[] bData, int length);
 ////显示接收内容,字符串
        //private void SetTextString(RichTextBox richTextBox, byte[] rData, int length)
        //{
        //    //如果调用方位于创建该控件线程以外的线程时，需要获取是否要调用Invoke方法
        //    if (this.rTxtBoxRcvData.InvokeRequired)
        //    {
        //        SetTextBytesStringCallBack setTxtStringCallBack = new SetTextBytesStringCallBack(SetTextString);
        //        this.Invoke(setTxtStringCallBack, new object[] { richTextBox, rData, length });
        //    }
        //    else
        //    {
        //        //如果不是线程外的调用时：设置文本框的值
        //        string str = Encoding.ASCII.GetString(rData, 0, length);
        //        richTextBox.Text += str;

        //        //使滚动条始终在最底部
        //        SetTextScrollToBottom(richTextBox);
        //    }
        //}

        ////richTextBox显示接收内容，十六进制
        ////入口：rData=字节数组；length=字节数组的长度
        //private void SetTextHexString(RichTextBox richTextBox, byte[] rData, int length)
        //{
        //    //如果调用方位于创建该控件线程以外的线程时，需要获取是否要调用Invoke方法
        //    if (this.rTxtBoxRcvData.InvokeRequired)
        //    {
        //        SetTextHexStringCallBack setTxtHexStrCallBack = new SetTextHexStringCallBack(SetTextHexString);
        //        this.Invoke(setTxtHexStrCallBack, new object[] { richTextBox, rData, length });
        //    }
        //    else
        //    {
        //        //如果不是线程外的调用时：设置文本框的值
        //        string str = string.Empty;
        //        byte[] bRcvBufferCpy = new byte[1024];
                
        //        //拷贝到临时缓冲区中
        //        for (int i = 0; i < length; i++)
        //        {
        //            bRcvBufferCpy[i] = bRcvBuffer[i];
        //        }

        //        //显示转化后的16进制字符串
        //        str = Bytes2HexString(bRcvBufferCpy, length);
        //        richTextBox.Text += str;//在文本框中显示

        //        //使滚动条始终在最底部
        //        SetTextScrollToBottom(richTextBox);
        //    }
        //}
 
 //设置StatusStripLable显示内容
          delegate void SetStatusStripLabelCallBack(ToolStripStatusLabel statuslabel, string strValue);//
        //private void SetStatusStripLabel(ToolStripStatusLabel statusLabel,string strValue)
        //{
        //    //如果调用方位于创建该控件线程以外的线程时，需要获取是否要调用Invoke方法
        //    if (statusLabel)
        //    {
        //        SetStatusStripLabelCallBack setStatusStripLabelCallBack = new SetStatusStripLabelCallBack(SetStatusStripLabel);
        //        this.Invoke(setStatusStripLabelCallBack, new object[] { statusLabel, strValue });
        //    }
        //    else
        //    {
        //        //如果不是线程外的调用时：设置文本框的值
        //        statusLabel.Text = strValue;
        //    }
        //}
  
 
 */