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.Reflection;
using System.IO.Ports;
using System.Threading;
using CommonClass;namespace JSNForm
{
    public partial class QD : Form
    {
        public QD()
        {
            InitializeComponent();
            Init();
        }        BLLJSN.BLLDic blldic = new BLLJSN.BLLDic();
        DataSet DsMain = new DataSet();
        Queue objQ = new Queue();
        string bianhao = null;
        string fangxiang = null;
        string shijian = null;
        string str1 = null;        /// <summary>
        /// 串口初始化
        /// </summary>
        private void Init()
        {
            serialPort.PortName = "COM1";
            serialPort.BaudRate = 38400;
            serialPort.DataBits = 8;
            serialPort.StopBits = StopBits.One;
            serialPort.Parity = Parity.Even;
        }        private void QD_Load(object sender, EventArgs e)
        {
            OpenSerialPort();
            SerialPortSend("05 00 44 AE 04 ");//清除设备的缓存数据命令
        }        private void QD_FormClosed(object sender, FormClosedEventArgs e)
        {
            Application.Exit();
        }        private void tsb_ks_Click(object sender, EventArgs e)
        {
            if (serialPort.IsOpen)
            {
                timer1.Start();
            }
            else { MessageBox.Show("串口未打开!"); }
        }        private void timer1_Tick(object sender, EventArgs e)
        {
                SerialPortSend("05 00 43 11 70 ");//获取串口数据的命令
                timer1.Interval = 500;        }        private void tsb_dk_Click(object sender, EventArgs e)
        {
            timer1.Stop();
            if (tsb_dk.Text == "打开串口")
            {
                OpenSerialPort();
            }
            else
            {
                CloseSerialPort();
            }
        }        /// <summary>
        /// 打开串口
        /// </summary>
        private void OpenSerialPort()
        {
            if (serialPort.IsOpen)
            {
                return;
            }
            try
            {
                serialPort.DataReceived -= serialPort_DataReceived;
                serialPort.DataReceived += serialPort_DataReceived;
                serialPort.Encoding = Encoding.Default;
                serialPort.Open();
                if (serialPort.IsOpen)
                {
                    tsb_dk.Text = "关闭串口";
                }
                else
                {
                    tsb_dk.Text = "打开串口";
                }
            }
            catch
            {
                MessageBox.Show("关闭串口失败!");
            }
        }        /// <summary>
        /// 关闭串口
        /// </summary>
        private void CloseSerialPort()
        {
            if (!serialPort.IsOpen)
            {
                return;
            }
            try
            {
                serialPort.DataReceived -= serialPort_DataReceived;
                while (SerialPortIsReceiving)
                {
                    Application.DoEvents();
                }
                serialPort.Close();
                if (serialPort.IsOpen)
                {
                    tsb_dk.Text = "关闭串口";
                }
                else
                {
                    tsb_dk.Text = "打开串口";
                }
            }
            catch
            {
                MessageBox.Show("关闭串口失败!");
            }
        }        /// <summary>
        /// 向通到发送指令
        /// </summary>
        /// <param name="ss"></param>
        public void SerialPortSend(string ss)
        {
            if (!serialPort.IsOpen)
            {
                MessageBox.Show("串口未打开,无法发送数据!");
            }
            try
            {
                string sCmd = ss;
                byte[] byte_arr = new byte[sCmd.Length / 3];
                for (int i = 0; i < sCmd.Length; i += 3)
                {
                    try
                    {
                        byte_arr[i / 3] = Convert.ToByte(sCmd.Substring(i, 2), 16);
                    }
                    catch (SystemException ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                }
                serialPort.Write(byte_arr, 0, byte_arr.Length);
            }
            catch (SystemException ex)
            {
                MessageBox.Show(ex.Message);
            }
        }        /// <summary>
        /// 通道返回数据
        /// </summary>
        bool SerialPortIsReceiving = false;
        public void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPortIsReceiving = true;
            try
            {
                byte[] b = new byte[serialPort.BytesToRead];
                serialPort.Read(b, 0, b.Length);
                string str = "";
                for (int i = 0; i < b.Length; i++)
                {
                    str += string.Format("{0:X2} ", b[i]);
                }
                AppendTextBox(str);
                str = null;
            }
            catch (SystemException ex)
            {
                MessageBox.Show(ex.Message);
            }
            SerialPortIsReceiving = false;
        }        delegate void SetTextCallback(string text);
        private void AppendTextBox(string text)
        {
            try
            {
                if (Receive_TextBox.InvokeRequired)
                {
                    SetTextCallback d = new SetTextCallback(AppendTextBox);
                    this.Invoke(d, text);
                }
                else
                {
                    Receive_TextBox.SuspendLayout();
                    if (text.Length == 1 && text[0] == (char)0x08)
                    {
                        if (Receive_TextBox.Text.Length > 0)
                        {
                            Receive_TextBox.SelectionStart = Receive_TextBox.Text.Length - 1;
                            Receive_TextBox.SelectionLength = 1;
                            Receive_TextBox.SelectedText = "";
                        }
                    }
                    else
                    {
                        Receive_TextBox.AppendText(text);
                    }
                    if (Receive_TextBox.Text.Length > 100000)
                    {
                        Receive_TextBox.Text = Receive_TextBox.Text.Substring(50000, Receive_TextBox.Text.Length - 50000);
                    }
                    Receive_TextBox.ResumeLayout(false);
                }
            }
            catch (SystemException ex)
            {
                MessageBox.Show(ex.Message);
            }
        }至此, Receive_TextBox已经获得了串口返回的值,现在我想用一个线程把它写到一个队列中去,同时另一个线程从队列中获取数据,写入到数据库中去,这2个线程一直处于侦听状态。哪位能给代码实现啊!PS:不是我多此一举由于串口的数据一直在获取,用一个线程怕时间赶不上,所以用线程写到队列中去,然后再用线程获取。不知道阐述的明不明白,先发了,这几天搞的我头疼!PS:,此程序中数据发送和回传是参照串口调试助手源码。求解!

解决方案 »

  1.   

    你定义一个List,然后在serialPort_DataReceived,把数据追加到List里。
    然后开个线程,从list中开始位置取数据处理。
      

  2.   

    C# 串口操作系列(1) -- 入门篇,一个标准的,简陋的串口例子。 
    C# 串口操作系列(2) -- 入门篇,为什么我的串口程序在关闭串口时候会死锁 ? 
    C# 串口操作系列(3) -- 协议篇,二进制协议数据解析 
      

  3.   

        public class DataCache
        {
            internal static Queue<Record> ColRecord = new Queue<Record>();              // 装载记录信息
        }    /// <summary>
        /// 提取记录线程类
        /// </summary>
        public class ColRecordThread
        {
            private bool bThreadSign;                   // 线程标志, 为True时运行线程, 否则结束线程
            public bool BThreadSign
            {
                get { return bThreadSign; }
                set { bThreadSign = value; }
            }        public static void ThreadingControl(bool bRun)
            {
                if (bRun)
                {
                    // 创建并启动线程
                }
                else
                {
                    // 结束线程
                    return;
                }
            }
            public void JanitorRecord()
            {
                while (true == this.BThreadSign)
                {
                    Record record = GetRecord(); // 得到记录
                    DataCache.ColRecord.Enqueue(record); // 记录入队                Thread.Sleep(200);
                    if (!this.BThreadSign) return;
                }
            }
        }    /// <summary>
        /// 显示记录线程
        /// </summary>
        public class ResponseThread
        {
            public static Action<int, Record> MessageReceive;   // 信息接收事件        private bool bThreadSign;                           // 线程标志, 为True时运行线程, 否则结束线程
            public bool BThreadSign
            {
                get { return bThreadSign; }
                set { bThreadSign = value; }
            }        public static void ThreadingControl(bool bRun)
            {
                if (bRun)
                {
                    // 创建并启动线程
                }
                else
                {
                    // 结束线程
                    return;
                }
            }
            public void JanitorRecord()
            {
                while (true == this.BThreadSign)
                {
                    if (DataCache.ColRecord.Count == 0)                         // 如果队列中没有数据, 则等待三秒钟后继续
                    {
                        Thread.Sleep(3000);
                        continue;
                    }                if (MessageReceive != null)
                    {
                        MessageReceive(0, DataCache.ColRecord.Dequeue());       // 记录出队, 回传MessageSend事件
                    }                Thread.Sleep(200);
                    if (!this.BThreadSign) return;
                }
            }
        }
      

  4.   

    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.Collections;
    using System.Text.RegularExpressions;
    using System.IO.Ports;
    namespace SerialportSample
    {
      public partial class SerialportSampleForm : Form
      {
      private SerialPort comm = new SerialPort();
      private StringBuilder builder = new StringBuilder();//避免在事件处理方法中反复的创建,定义到外面。   
      private long received_count = 0;//接收计数   
      private long send_count = 0;//发送计数   
      private bool Listening = false;//是否没有执行完invoke相关操作   
      private bool Closing = false;//是否正在关闭串口,执行Application.DoEvents,并阻止再次invoke   
      private List<byte> buffer = new List<byte>(4096);//默认分配1页内存,并始终限制不允许超过   
      private byte[] binary_data_1 = new byte[9];//AA 44 05 01 02 03 04 05 EA     public SerialportSampleForm()
      {
      InitializeComponent();
      }      void comm_DataReceived(object sender, SerialDataReceivedEventArgs e)
      {
      if (Closing) return;//如果正在关闭,忽略操作,直接返回,尽快的完成串口监听线程的一次循环   
      try
      {
      Listening = true;//设置标记,说明我已经开始处理数据,一会儿要使用系统UI的。   
      int n = comm.BytesToRead;//先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致   
      byte[] buf = new byte[n];//声明一个临时数组存储当前来的串口数据   
      received_count += n;//增加接收计数   
      comm.Read(buf, 0, n);//读取缓冲数据     /////////////////////////////////////////////////////////////////////////////////////////////////////////////   
      //<协议解析>   
      bool data_1_catched = false;//缓存记录数据是否捕获到   
      //1.缓存数据   
      buffer.AddRange(buf);
      //2.完整性判断   
      while (buffer.Count >= 4)//至少要包含头(2字节)+长度(1字节)+校验(1字节)   
      {
      //请不要担心使用>=,因为>=已经和>,<,=一样,是独立操作符,并不是解析成>和=2个符号   
      //2.1 查找数据头   
      //if (buffer[0] == 0xAA && buffer[1] == 0x44)
      if (buffer[0] == 0xFE)
      {
      //2.2 探测缓存数据是否有一条数据的字节,如果不够,就不用费劲的做其他验证了   
      //前面已经限定了剩余长度>=4,那我们这里一定能访问到buffer[2]这个长度   
      int len = buffer[2];//数据长度   
      //数据完整判断第一步,长度是否足够   
      //len是数据段长度,4个字节是while行注释的3部分长度   
      if (buffer.Count < len + 4) break;//数据不够的时候什么都不做   
      //这里确保数据长度足够,数据头标志找到,我们开始计算校验   
      //2.3 校验数据,确认数据正确   
      //异或校验,逐个字节异或得到校验码   
      byte checksum = 0;
      for (int i = 0; i < len + 3; i++)//len+3表示校验之前的位置   
      {
      checksum ^= buffer[i];
      }
      if (checksum != buffer[len + 3]) //如果数据校验失败,丢弃这一包数据   
      {
      buffer.RemoveRange(0, len + 4);//从缓存中删除错误数据   
      continue;//继续下一次循环   
      }
      //至此,已经被找到了一条完整数据。我们将数据直接分析,或是缓存起来一起分析   
      //我们这里采用的办法是缓存一次,好处就是如果你某种原因,数据堆积在缓存buffer中   
      //已经很多了,那你需要循环的找到最后一组,只分析最新数据,过往数据你已经处理不及时   
      //了,就不要浪费更多时间了,这也是考虑到系统负载能够降低。   
      buffer.CopyTo(0, binary_data_1, 0, len + 4);//复制一条完整数据到具体的数据缓存   
      data_1_catched = true;
      buffer.RemoveRange(0, len + 4);//正确分析一条数据,从缓存中移除数据。   
      }
      else
      {
      //这里是很重要的,如果数据开始不是头,则删除数据   
        
      buffer.RemoveAt(0);
      }
      }
      //分析数据   
      if (data_1_catched)
      {
      //我们的数据都是定好格式的,所以当我们找到分析出的数据1,就知道固定位置一定是这些数据,我们只要显示就可以了   
      string data = "\r\n" + binary_data_1[3].ToString("X2") + " " + binary_data_1[4].ToString("X2") + " " +
      binary_data_1[5].ToString("X2") + " " + binary_data_1[6].ToString("X2") + " " +
      binary_data_1[7].ToString("X2");
      //更新界面   
      this.Invoke((EventHandler)(delegate { textData.Text = data; }));
      }
      //如果需要别的协议,只要扩展这个data_n_catched就可以了。往往我们协议多的情况下,还会包含数据编号,给来的数据进行   
      //编号,协议优化后就是: 头+编号+长度+数据+校验   
      //</协议解析>   
      /////////////////////////////////////////////////////////////////////////////////////////////////////////////     builder.Remove(0,builder.Length);//清除字符串构造器的内容   
      //因为要访问ui资源,所以需要使用invoke方式同步ui。   
      this.Invoke((EventHandler)(delegate
      {
      //判断是否是显示为16禁止   
      if (checkBoxHexView.Checked)
      {
      //依次的拼接出16进制字符串   
      foreach (byte b in buf)
      {
      builder.Append(b.ToString("X2") + " ");
      string bStr=b.ToString("X2");
      if (bStr == "FF"|| bStr =="EF")
      {
      builder.Append("\r\n");
      }
      }
      }
      else
      {
      //直接按ASCII规则转换成字符串   
      builder.Append(Encoding.ASCII.GetString(buf));  }
      //追加的形式添加到文本框末端,并滚动到最后。   
      this.txGet.AppendText(builder.ToString());
      //修改接收计数   
      labelGetCount.Text = "Get:" + received_count.ToString();
      }));
      }
      finally
      {
      Listening = false;//我用完了,ui可以关闭串口了。   
      }
      }      //动态的修改获取文本框是否支持自动换行。   
      private void checkBoxNewlineGet_CheckedChanged(object sender, EventArgs e)
      {
      txGet.WordWrap = checkBoxNewlineGet.Checked;
      }  private void buttonSend_Click(object sender, EventArgs e)
      {
      //定义一个变量,记录发送了几个字节   
      int n = 0;
      //16进制发送   
      if (checkBoxHexSend.Checked)
      {
      //我们不管规则了。如果写错了一些,我们允许的,只用正则得到有效的十六进制数   
      MatchCollection mc = Regex.Matches(txSend.Text, @"(?i)[\da-f]{2}");
      List<byte> buf = new List<byte>();//填充到这个临时列表中   
      //依次添加到列表中   
      foreach (Match m in mc)
      {
      buf.Add(byte.Parse(m.Value, System.Globalization.NumberStyles.HexNumber));
      }
      //转换列表为数组后发送   
      comm.Write(buf.ToArray(), 0, buf.Count);
      //记录发送的字节数   
      n = buf.Count;
      }
      else//ascii编码直接发送   
      {
      //包含换行符   
      if (checkBoxNewlineSend.Checked)
      {
      comm.WriteLine(txSend.Text);
      n = txSend.Text.Length + 2;
      }
      else//不包含换行符   
      {
      comm.Write(txSend.Text);
      n = txSend.Text.Length;
      }
      }
      send_count += n;//累加发送字节数   
      labelSendCount.Text = "Send:" + send_count.ToString();//更新界面   
      }  private void buttonReset_Click(object sender, EventArgs e)
      {
      //复位接受和发送的字节数计数器并更新界面。   
      send_count = received_count = 0;
      labelGetCount.Text = "Get:0";
      labelSendCount.Text = "Send:0";
      }
      //窗体初始化   
      private void SerialportSampleForm_Load_1(object sender, EventArgs e)
      {
      //初始化下拉串口名称列表框   
      string[] ports = SerialPort.GetPortNames();
      Array.Sort(ports);
      comboPortName.Items.AddRange(ports);
      comboPortName.SelectedIndex = comboPortName.Items.Count > 0 ? 0 : -1;
      comboBaudrate.SelectedIndex = comboBaudrate.Items.IndexOf("19200");  //初始化SerialPort对象   
      comm.NewLine = "\r\n";
      comm.RtsEnable = true;//根据实际情况吧。     //添加事件注册   
      comm.DataReceived += comm_DataReceived;
      }  private void buttonOpenClose_Click(object sender, EventArgs e)
      {
      //根据当前串口对象,来判断操作   
      if (comm.IsOpen)
      {
      Closing = true;
      while (Listening) Application.DoEvents();
      //打开时点击,则关闭串口   
      comm.Close();
      }
      else
      {
      //关闭时点击,则设置好端口,波特率后打开   
      comm.PortName = comboPortName.Text;
      //comm.BaudRate = int.Parse(comboBaudrate.Text);
      comm.BaudRate = 9600;
      try
      {
      comm.Open();
      }
      catch (Exception ex)
      {
      //捕获到异常信息,创建一个新的comm对象,之前的不能用了。   
      comm = new SerialPort();
      //现实异常信息给客户。   
      MessageBox.Show(ex.Message);
      }
      }
      //设置按钮的状态   
      buttonOpenClose.Text = comm.IsOpen ? "Close" : "Open";
      buttonSend.Enabled = comm.IsOpen;
      }   
        
      }
      

  5.   

    把你的任务分解得细致一点,不要粗放地滥用线程、监听这种概念。对于一个任务,比如我们写
    .......
    DoIt(myData);
    .......
    如果你不想让这一行代码阻塞当前程序执行,在DoIt方法设计已经比较独立因此不会出现数据冲突的情况下,修改一下就可以了:new Action(() => { DoIt(myData);}).BeginInvoke(null, null);
    使用BeginInvoke的第一个参数,你还可以设置异步处理完成时的回调方法,做其它需要跟它同步处理的工作。不要动不动就弄什么“启动线程、监听”这种概念,那很可能是听似响亮、实际设计软件时很想当然的做法。
      

  6.   

    当然,也可以以换做一种使用系统线程池的写法:ThreadPool.QueueUserWorkItem(h => { DoIt(myData); });
      

  7.   

    不要抄代码,希望你能学原理。这是属于.net的并发编程的最入门概念。我以前对马诺说人家对她“找不到入口”只是觉得好笑,现在我才真正知道对于当事人这确实是多么困惑的一件事啊!当我看到你写:“现在我想用一个线程把它写到一个队列中去,同时另一个线程从队列中获取数据,写入到数据库中去,这2个线程一直处于侦听状态”的时候,我首先是觉得你肯定是被人引导到错误的入口上了因此着急,接着我发现原来你根本就不知道“马诺的入口在哪里”。其实进行线程编程,不要先去滥用什么“启动线程”这个概念,如果滥用了,而忽视了“何时何地启动”这个细节,你就很容易用更加错误的“监听”概念来弥补你的线程的错误。
      

  8.   

    如果你觉得new Action(() => { DoIt(myData);}).BeginInvoke(null, null);这个写法看起来比较头晕,可以用老式的写法我在重写一下。首先你需要额外定义一个Delegate类型:public delegate MyProc(要传递的数据类型 data);然后将你的DoIt那一行代码修改为:MyProc p=DoIt;
    p(mydata,null,null);
      

  9.   

    啊哈,不用额外声明Delegate新类型的做法,也可以写为一句new Action<要传递的数据类型>(DoIt)(mydata,null,null);这些差别都是极其微小的皮毛,可以信手拈来,而原理是完全一样没有差别的。
      

  10.   

    sorry,上面“信手”丢了东西了,呵呵,应该写为new Action<要传递的数据类型>(DoIt).BeginInvoke(mydata,null,null);