串口資料處理

來源:互聯網
上載者:User

標籤:os   資料   io   art   for   cti   

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.IO.Ports;using System.Runtime.CompilerServices;using System.Diagnostics;using System.Threading;using Util.Log;namespace SerialLibrary{    public class SerialLib    {        #region Custom        private bool running; //當前運行狀態        private SerialPort sp; //串口控制項        private long received_count, send_count; //發送總位元組,接收總位元組        private byte[] Received; //所有接收資料的緩衝        private int received_offset; //當前緩衝存在閒置位置        private int cmd_offset; //已解出的命令的位置,下一個可用的開始位置        const int BUF_SIZE = 512;        const byte STX = 0x02; //STX        const byte ETX = 0x03; //ETX        private Dictionary<byte, WaitEvent> waitList;        #endregion        public SerialLib()        {            send_count = 0;            received_count = 0;            received_offset = 0;            cmd_offset = 0;            waitList = new Dictionary<byte, WaitEvent>();            running = false;            sp = new SerialPort();            sp.ReadBufferSize = 512;            sp.WriteBufferSize = 512;            sp.BaudRate = 9600;            sp.StopBits = StopBits.One;            sp.DataBits = 8;            sp.Parity = Parity.None;            Received = new byte[BUF_SIZE];            sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);        }        ///        /// 命令等待回應        ///        class WaitEvent : IDisposable        {            public ManualResetEvent Event;            public byte[] recv;            public WaitEvent()            {                recv = null;                Event = new ManualResetEvent(false);            }            public void Dispose()            {                recv = null;                Event.Dispose();                Event = null;            }        }        /// 接收串口資料,取出有效命令,發到線程池待處理        /// 整個方法加鎖,防止多次進入接收影響全域變數的值。必須完成一次後才能再次進入接收        [MethodImpl(MethodImplOptions.Synchronized)]        private void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)        {            if (!running) return;            SerialPort sp = (SerialPort)sender;            int n = sp.BytesToRead;            if (received_offset + n > BUF_SIZE) //資料包超大,放不下,只接收能放下的位元組數            {                pack_received_buffer(); //壓縮                if (received_offset + n > BUF_SIZE) //壓縮後仍不能接收的,減少接收空間                {                    n = BUF_SIZE - received_offset;                }            }            received_count += n; //接收的總位元組數            byte[] buf = new byte[n];            sp.Read(buf, 0, n);            Array.Copy(buf, 0, Received, received_offset, n);            received_offset += n; //移入後位移            Debug.WriteLine("接收位置-{0},命令位置-{1},接收總位元組-{2},發送總位元組-{3}", received_offset, cmd_offset, received_count, send_count);            Debug.WriteLine("接收資料:" + dump_buffer(buf));            //按協議中長度找協議末尾            int datalen = get_int(Received, cmd_offset + 1, 2);            int end_offset = cmd_offset + 3 + datalen; //結束符的位置            end_offset = end_offset < received_offset ? end_offset : cmd_offset + 1; //防止超過當前已讀位元組,讀到垃圾位元組            if (Received[end_offset] != ETX) return; //接收資料不完整,等待下次接收            while (Received[end_offset] != ETX && end_offset < received_offset) end_offset++;            if (end_offset == received_offset)            {                //如果找了超過256位元組仍無有效內容,直接清空全部。單包資料應該不超過128                if (end_offset > cmd_offset + 256)                {                    received_offset = 0;                    cmd_offset = 0;                }                return; //沒有找到結束符            }            end_offset++; //後移一位元組,包插進 LRC校正值            byte[] cmd_buf = new byte[end_offset - cmd_offset + 1];            Array.Copy(Received, cmd_offset, cmd_buf, 0, cmd_buf.Length); //複製出一條完整的命令            ThreadPool.QueueUserWorkItem(receive_serial_data, cmd_buf);            cmd_offset = end_offset + 1; //跳過LRC,到下一條            if (received_offset > BUF_SIZE * 0.85) //緩衝位置超過85%。實際不太可能,除非受幹擾全亂碼            {                pack_received_buffer();            }        }        private void receive_serial_data(object data)        {            byte[] cmd_buf = (byte[])data;            Debug.WriteLine("處理資料:" + dump_buffer(cmd_buf));            Log.Info("處理資料:" + dump_buffer(cmd_buf));            byte lrc = cmd_buf[cmd_buf.Length - 1]; //取最後一位元組,lrc            if (lrc != calc_lrc(cmd_buf, 3, cmd_buf.Length - 2))            {                Debug.WriteLine("資料LRC校正失敗,不處理");            }            byte cmdtype = cmd_buf[3];            lock (waitList)            {                if (waitList.ContainsKey(cmdtype))                {                    int dlength = get_int(cmd_buf, 1, 2);                    WaitEvent we = waitList[cmdtype];                    if (dlength > 0)                    {                        we.recv = new byte[dlength + 5];                        Array.Copy(cmd_buf, 0, we.recv, 0, dlength + 5); //如果有資料返回,包括命令類型一起返回                    }                    we.Event.Set();                }            }        }        ///        /// 啟動串口        ///        ///通卡模組所在的串口號        ///        public int Start(int port)        {            if (running) return 0; //已經開啟過的,直接返回成功            sp.PortName = string.Format("COM{0}", port);            try            {                sp.Open();                sp.DiscardInBuffer();                sp.DiscardOutBuffer();                running = true;                Log.Info("通卡模組串口COM" + port.ToString() + "啟動成功");                return 0;            }            catch (Exception e)            {                Log.Error("啟動串口失敗: ", e);                return 1;            }        }        ///        /// 關閉串口        ///        public void Stop()        {            if (running)            {                try                {                    if (sp.IsOpen) sp.Close();                    Log.Info("通卡模組串口關閉成功");                }                catch (Exception e)                {                    Log.Error("串口關閉異常: ", e);                }                running = false;            }        }        ///        /// 發送串口命令        ///        ///命令類型        ///發送資料        ///接收資料。可能為 null        ///0為處理成功, 3為無響應,其他為失敗        public int process(byte cmdType, byte[] data, out byte[] recvdata, int Wait)        {            recvdata = null;            cmd_offset = 0;            int dlength = data.Length;            byte[] cmd = new byte[dlength + 6]; //1頭長+2長度(bcd)+1命令長度(bcd)+4長度終端編號(bcd)+4長度表文流水號(bcd)+N長度報文+1尾長+1校正長            int datalen = data.Length + 1;            bcdstr_to_byte_array(datalen.ToString(), ref cmd, 1, 2);//設定資料長度兩個位元組            cmd[0] = STX;            cmdType = int2bcd(cmdType);            cmd[3] = cmdType;            Array.Copy(data, 0, cmd, 4, dlength);            cmd[4 + dlength] = ETX;            cmd[5 + dlength] = calc_lrc(cmd, 3, cmd.Length - 2);            send_count += cmd.Length;            Debug.WriteLine("發送命令:" + dump_buffer(cmd));            Log.Info("發送命令:" + dump_buffer(cmd));            try            {                sp.Write(cmd, 0, cmd.Length);                if (Wait == 0) return 0; //不需要接收回複                //等待回複,逾時 Wait 秒                WaitEvent we = new WaitEvent();                lock (waitList)                {                    waitList[cmdType] = we;                }                DateTime dt = DateTime.Now;                dt = dt.AddSeconds(Wait);                while (!we.Event.WaitOne(50) && DateTime.Now < dt)                {                    Thread.Sleep(50);                }                if (!we.Event.WaitOne(1))                {                    lock (waitList)                    {                        waitList.Remove(cmdType);                    }                    Debug.WriteLine("命令逾時,沒收到回應");                    return 3; //未收到響應                }                if (we.recv != null)                {                    recvdata = new byte[we.recv.Length];                    Array.Copy(we.recv, recvdata, we.recv.Length);                    Log.Info("接受命令:" + dump_buffer(recvdata));                }                lock (waitList)                {                    waitList.Remove(cmdType);                }                we.Dispose();                we = null;                return 0;            }            catch (Exception e)            {                Log.Error("串口處理異常: ", e);                return 1;            }        }        ///        /// 整數轉為bcd碼, 12(dec) --> 0x12        ///        ///        ///        private byte int2bcd(int x)        {            x = (byte)x;            return (byte)((x / 10 * 16) + x % 10);        }        ///        /// bcd轉為整數, 0x12 --> 12(dec)        ///        ///        ///        private int bcd2int(byte x)        {            return (x / 16 * 10) + (x % 16);        }        ///        /// 將bcd字串轉換成位元組資料,填充到指定位置的指定長度        ///        ///        ///        ///        ///        private void bcdstr_to_byte_array(string bcdstr, ref byte[] array, int Start, int Length)        {            int i = bcdstr.Length;            int k = 0;            byte x;            string st;            while (i > 0)            {                if (i > 1) st = bcdstr.Substring(i - 2, 2);                else st = bcdstr.Substring(0, 1); //不是雙字元對齊時,會剩下一個字元要轉換                if (!byte.TryParse(st, out x)) x = 0;                array[Start + Length - k - 1] = int2bcd(x);                k++;                i -= 2;            }            while (Start + Length - k - 1 >= Start)            {                array[Start + Length - k - 1] = 0;                k++;            }        }        ///        /// 壓縮接收緩衝,使有效資料前移,空間後方空間        ///        private void pack_received_buffer()        {            //後續緩衝空間不夠,向前移動,覆蓋前面已用過的空間            byte[] swap = new byte[BUF_SIZE];            Array.Copy(Received, cmd_offset, swap, 0, received_offset - cmd_offset);            Array.Copy(swap, 0, Received, 0, received_offset - cmd_offset);            received_offset = received_offset - cmd_offset;            cmd_offset = 0;        }        private string dump_buffer(byte[] buffer)        {            StringBuilder sp = new StringBuilder(1024);            int n = 0;            foreach (byte x in buffer)            {                sp.AppendFormat("{0:X2}", x);                n++;                if (n > 512) break; //緩衝有限,只處理最長512位元組            }            return sp.ToString();        }        private int get_int(byte[] buffer, int Start, int Length)        {            int n = 0;            StringBuilder strBuilder = new StringBuilder();            for (int i = Start; i < Start + Length && i < buffer.Length; i++)            {                strBuilder.AppendFormat("{0:X2}",buffer[i]);            }            n = Convert.ToInt32(strBuilder.ToString());            return n;        }        ///        /// 計算LRC校正, xor異或        ///        ///        ///        ///        ///         private byte calc_lrc(byte[] data, int Start, int End)        {            byte lrc = 0;            int dlength = data.Length;            for (int i = Start; i < End && i < dlength; i++)            {                lrc ^= data[i];            }            return lrc;        }    }}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.