C#RS485通信

C#实现485通信

1. 功能

  1. 心跳检测:心跳1s发一次,上电后一段时间内不检测心跳,此后,每3s检测一次是否收到心跳,若3s内一次都没有收到心跳,认为网络故障。
  2. 500ms内没收到ack,或者ack包的数据域里有错误代码,重发,重发超过3次,仍没有收到ack, 认为网络故障。
  3. 两帧间隔10ms:每次发送报文前,延时10ms再发送。
  4. 若网络故障,则关闭串口通信。

2. 设计到的C#知识点

  1. 串口通信:引用类RJCP.IO.Ports
  2. 定时器: 引用类System.Timers
  3. 进程间通信:event

3. 代码

using System;
using System.Threading;
using System.Threading.Tasks;
using RJCP.IO.Ports;
using System.Timers;

namespace demo485
{
    
    
    public class SynTimer
    {
    
    
        public static AutoResetEvent SendTimertimeout = new AutoResetEvent(false);  // sychronize send timer to send task
    }
    public class RS485: SynTimer
    {
    
    
        enum eCMDType
        {
    
       
        };

        enum eFrameType 
        {
    
     
            
        };
        enum eFrameOffset
        {
    
    
            
        };

        enum eRecvErrorCode
        {
    
     
           
        };
        private struct SFrameContent
        {
    
    
            public byte frame_head1;
            public byte frame_head2;
            public byte frame_des;
            public byte frame_tail1;
            public byte frame_tail2;
        };

        private struct SReceivedErrMsg
        {
    
    
           
        };
        private struct SIoTMsg
        {
    
    
            
        };

        SerialPortStream uart;
        SFrameContent SFramecontent;
        SReceivedErrMsg errMsg;
        SIoTMsg IoTMsgs;
                      
        private static byte CMD0ResendCount;                
        private static byte CMD3ResendCount;                
        private static bool isGetHeartbeat = false;  
        private static bool BusOff_485 = false;      
        private byte isGetRes = 0x00;            

        // 计时器
        private System.Timers.Timer HeartbeatTimer;                      
        private System.Timers.Timer CMD3Timer;                      
        private System.Timers.Timer CMD0Timer;                      
        private System.Timers.Timer SendTimer;                      
        // for debug
        private void showRcevInfo(byte[] recvBuf)
        {
    
    
            Console.Write("DATA RECEIVED: Length = {0}; ", recvBuf.Length);
            for (int i = 0; i < recvBuf.Length; i++)
            {
    
    
                Console.Write("buf[{0}]: {1:x} ", i, recvBuf[i]);
            }
            Console.WriteLine(" -->receive buf end.");
        }

        public RS485()
        {
    
    
            uartInit();
            SFramecontent = new SFrameContent();
            SFramecontent.frame_head1 = 0xaa;
            SFramecontent.frame_head2 = 0x55;
            SFramecontent.frame_des = 0x00;
            SFramecontent.frame_tail1 = 0x0a;
            SFramecontent.frame_tail2 = 0x0d;
        }

        private void uartDataReceived(object sender, SerialDataReceivedEventArgs e)
        {
    
    
            SerialPortStream serialPort = (SerialPortStream)sender;
            byte[] recvBuf = new byte[serialPort.BytesToRead];
            byte recvErrorCode = 0;

            if (recvBuf.Length < (int)eFrameOffset.FRAME_LENBASE)
            {
    
    
                #region DEBUG_MSG
#if true
                serialPort.Read(recvBuf, 0, recvBuf.Length);
                showRcevInfo(recvBuf);
#endif
                #endregion
                Console.WriteLine(" invalid lenth.lenth = {0}\n", recvBuf.Length);
                return;
            }
            //接收到数据包
            serialPort.Read(recvBuf, 0, recvBuf.Length);
            #region DEBUG_MSG
#if true
            showRcevInfo(recvBuf);
#endif
            #endregion
            //检查帧头
            if (recvBuf[(int)eFrameOffset.FRAME_HEAD1].Equals(SFramecontent.frame_head1) &&
                recvBuf[(int)eFrameOffset.FRAME_HEAD2].Equals(SFramecontent.frame_head2))
            {
    
    
                //检查帧尾
                if (recvBuf[recvBuf.Length - (int)eFrameOffset.FRAME_TAIL1].Equals(SFramecontent.frame_tail1) &&
                    recvBuf[recvBuf.Length - (int)eFrameOffset.FRAME_TAIL2].Equals(SFramecontent.frame_tail2))
                {
    
    
                    //检查目的地址
                    if (recvBuf[(int)eFrameOffset.FRAME_DES].Equals(SFramecontent.frame_des))
                    {
    
    
                        //检查帧长度
                        if (!recvBuf[(int)eFrameOffset.FRAME_LEN].Equals(recvBuf.Length))
                        {
    
    
                            //校验
                            if (CheckSum(recvBuf))
                            {
    
    
                                ParseCMD(recvBuf);
                            }
                            else
                            {
    
    
                                Console.WriteLine("check sum failed.\n");
                                recvErrorCode = (byte)eRecvErrorCode.Checksum_Error;
                            }
                        }
                        else
                        {
    
    
                            Console.WriteLine("check len failed. len = {0:x}, recv_len = {1:x}\n", recvBuf[(int)eFrameOffset.FRAME_LEN], recvBuf.Length);
                            recvErrorCode = (byte)eRecvErrorCode.Len_Error;
                        }
                    }
                    else
                    {
    
    
                        Console.WriteLine("check des failed.\n");
                        recvErrorCode = (byte)eRecvErrorCode.Des_Error;
                    }
                }
                else
                {
    
    
                    Console.WriteLine("check tail failed.\n");
                    recvErrorCode = (byte)eRecvErrorCode.Tail_Error;
                }
            }
            else
            {
    
    
                Console.WriteLine("check head failed.\n");
                recvErrorCode = (byte)eRecvErrorCode.Head_Error;
            }

            //发送故障码
            if (!recvErrorCode.Equals(0))
            {
    
    
                SendErrorCode(recvErrorCode);
            }
        }

        private byte CalSum(byte[] checkBuf)
        {
    
    
            int sum = 0;
            byte checksum = 0;

            for (int i = (int)eFrameOffset.FRAME_DES; i < checkBuf.Length - (int)eFrameOffset.FRAME_CHECKSUM; i++)
            {
    
    
                sum += checkBuf[i];
            }
            checksum = (byte)(sum & 0xff);
            checksum = (byte)(~checksum + 1);

            return checksum;
        }

        private bool CheckSum(byte[] checkBuf)
        {
    
    
            byte recv_checksum = checkBuf[checkBuf.Length - (int)eFrameOffset.FRAME_CHECKSUM];
            byte checksum = 0;

            checksum = CalSum(checkBuf);

        //    Console.WriteLine("CheckSum :recv_checksum = {0:x}, checksum = {1:x}.\n", recv_checksum, checksum);

            if (checksum.Equals(recv_checksum))
                return true;
            else
                return false;
        }
        private void ParseCMD(byte[] parseBuf)
        {
    
    
            byte cmd = parseBuf[(int)eFrameOffset.FRAME_CMD];

            // ack
            SendResponse(cmd);

            //处理命令
            switch(cmd)
            {
    
    
                case (byte)eCMDType.cmd_ElevatorReq:
                    ParseCMD0(parseBuf);
                    break;
                case (byte)eCMDType.cmd_LiTOElErrorUpload:
                    ParseCMD1(parseBuf);
                    break;
                case (byte)eCMDType.cmd_LiTOElIOTUpload:
                    ParseCMD2(parseBuf);
                    break;
                case (byte)eCMDType.cmd_ElTOLiSendFloor:
                    ParseCMD3(parseBuf);
                    break;
                case (byte)eCMDType.cmd_LiTOElUploadFloor:
                    ParseCMD4(parseBuf);
                    break;
                case (byte)eCMDType.cmd_Heartbeat:
                    ParseHeartbeat();
                    break;
                case (byte)eCMDType.cmd_PackageError:
                    ParsePackageError();
                    break;
                default:
                    Console.WriteLine("invalid cmd:{0}\n", cmd);
                    break;

            }

        }

        private void ParseCMD0(byte[] parseBuf)
        {
    
    
            Console.WriteLine("ParseCMD0.\n");
            // 正确接收到ack, 停止timer
            CMD0Timer.Enabled = false;
            CMD0Timer.Stop();
        }

        private void ParseCMD1(byte[] parseBuf)
        {
    
    
            
            Console.WriteLine("ParseCMD1.\n");
        }

        private void ParseCMD2(byte[] parseBuf)
        {
    
    
            Console.WriteLine("ParseCMD2.\n");
        }

        private bool ParseCMD3(byte[] parseBuf)
        {
    
    
            Console.WriteLine("ParseCMD3.\n");
            if (parseBuf[(int)eFrameOffset.FRAME_DATA].Equals(0))
            {
    
    
                // 正确接收到ack, 停止timer
                CMD3Timer.Enabled = false;
                CMD3Timer.Stop();

                return true;
            }
            else     // 对方接收到的报文不对, 重发
            {
    
    
                Console.WriteLine("parse cmd3:{0}", DateTime.Now);
                if (!BusoffHandler((byte)eCMDType.cmd_ElTOLiSendFloor))
                {
    
    
                    Console.WriteLine("cmd3 error:{0}", parseBuf[(int)eFrameOffset.FRAME_DATA]);
                    // 重发
                    CMD3ResendCount++;
                    SendCMD3();
                }
                else
                {
    
    
                    Console.WriteLine("cmd3 timeout:{0}", DateTime.Now);
                }
            }
           
            return false;
        }

        private void ParseCMD4(byte[] parseBuf)
        {
    
    
            Console.WriteLine("ParseCMD4: floor num from light curtain:{0}", recvdFloorNum);
        }

        private void ParseHeartbeat()
        {
    
    
            byte[] buf = new byte[1];
            buf[0] = 0;

            isGetHeartbeat = true;
            SendCMD(buf, (byte)eCMDType.cmd_Heartbeat, 0x01, (byte)eFrameType.Ack);

            Console.WriteLine("res heartbeat. res time = {0}", DateTime.Now);
        }

        private void ParsePackageError()
        {
    
    
            Console.WriteLine("ParsePackageError \n");
        }

        private void SendResponse(byte cmd)
        {
    
    
            byte[] resbuf = new byte[1];
            resbuf[0] = (byte)eRecvErrorCode.No_Error;

            if(cmd != (byte)eCMDType.cmd_ElevatorReq && cmd != (byte)eCMDType.cmd_ElTOLiSendFloor && cmd != (byte)eCMDType.cmd_Heartbeat)
                SendCMD(resbuf, cmd, 0x01, (byte)eFrameType.Ack);
        }
        private void SendErrorCode(byte errorCode)
        {
    
    
            // TODO: 
            //    byte[] errcode = new byte[1];
            //    errcode[0] = errorCode;
            //    SendCMD(errcode, (byte)eCMDType.cmd_PackageError, 0x01, (byte)eFrameType.Error);

            Console.WriteLine("SendErrorCode errorCode:{0} \n", errorCode);
        }

        private void SendCMD(byte[] buf, byte cmd, byte len, byte type)
        {
    
    
            byte sendlen = (byte)(eFrameOffset.FRAME_LENBASE + len);
            byte[] sendbuf = new byte[sendlen];
            
            SendTimer.Start();
            Console.WriteLine("Send timer start, time = {0}", DateTime.Now.TimeOfDay);

            sendbuf[(int)eFrameOffset.FRAME_HEAD1] = SFramecontent.frame_head1;
            sendbuf[(int)eFrameOffset.FRAME_HEAD2] = SFramecontent.frame_head2;
            sendbuf[(int)eFrameOffset.FRAME_DES] = 0x01;
            sendbuf[(int)eFrameOffset.FRAME_CMD] = cmd;
            sendbuf[(int)eFrameOffset.FRAME_TYPE] = type;
            sendbuf[(int)eFrameOffset.FRAME_LEN] = sendlen;
            //数据域赋值
            for (int i = 0; i < len; i++)
            {
    
    
                sendbuf[(int)(eFrameOffset.FRAME_DATA + i)] = buf[i];
            }
            //校验
            sendbuf[(int)(sendlen - eFrameOffset.FRAME_CHECKSUM)] = CalSum(sendbuf); ;
            sendbuf[(int)(sendlen - eFrameOffset.FRAME_TAIL1)] = SFramecontent.frame_tail1;
            sendbuf[(int)(sendlen - eFrameOffset.FRAME_TAIL2)] = SFramecontent.frame_tail2;

#region DEBUG_MSG   
#if true            // todo: remove in publish
            Console.Write("send buf: ");
            for (int j = 0; j < sendlen; j++)
            {
    
    
                Console.Write(" {0:x} ", sendbuf[j]);
            }
            Console.WriteLine("\n");
#endif
#endregion
            if (uart.IsOpen)
            {
    
    
                try
                {
    
    
                    SendTimertimeout.WaitOne();
                    Console.WriteLine("send package: {0}", DateTime.Now.TimeOfDay);
                    uart.Write(sendbuf, 0, sendlen);
                }
                catch (Exception e)
                {
    
    
                    Console.WriteLine("send error: {0}",e);
                }
            }
            else
            {
    
    
                Console.WriteLine("uart {0} is closed!", uart.PortName);
            }
           
        }

        public void SendCMD0()
        {
    
    
            SynSign.mUpdateEventForRS485.WaitOne();
            Console.WriteLine("ElevatorSendCMD0.\n");

            byte[] buf = new byte[6];
            buf[0] = 0x01;
            SendCMD(buf, (byte)eCMDType.cmd_ElevatorReq, 0x06, (byte)eFrameType.Request);

            CMD0Timer.Enabled = true;
            CMD0Timer.Start();
        }

        public void SendCMD3()
        {
    
    
            byte[] sendbuf = new byte[7];

            sendbuf[0] = 0;        

            Console.WriteLine("ElevatorSendCMD3.\n");

            SendCMD(sendbuf, (byte)eCMDType.cmd_ElTOLiSendFloor, 0x07, (byte)eFrameType.Download);

            CMD3Timer.Enabled = true;
            CMD3Timer.Start();
        }

        private bool BusoffHandler(byte cmd)
        {
    
    
            byte  ResendCount = 0;

            if (cmd == (byte)eCMDType.cmd_ElevatorReq)
            {
    
    
                ResendCount = CMD0ResendCount;
            }
            else if (cmd == (byte)eCMDType.cmd_ElTOLiSendFloor)
            {
    
    
                ResendCount = CMD3ResendCount;
            }

            if (ResendCount > 3 || cmd == (byte)eCMDType.cmd_Heartbeat)
            {
    
    
#if true   //TODO: in publish version
                // TODO: 心跳超时, 485网络故障, 关闭485
                BusOff_485 = true;
                uart.Close();
                // 关闭定时器
                HeartbeatTimer.Dispose();
                CMD3Timer.Dispose();
                CMD0Timer.Dispose();
                // TODO: 上报到com stack
#endif
                return true;
            }
            return false;
        }
        private void HeartbeatTimeoutExecute(object source, System.Timers.ElapsedEventArgs e)
        {
    
    
            HeartbeatTimer.Stop();

            if (isGetHeartbeat)
            {
    
    
                isGetHeartbeat = false;
                Console.WriteLine("HeartbeatTimeoutExecute: get heartbeat time = {0}",DateTime.Now);
            }
            else
            {
    
    
                Console.WriteLine("HeartbeatTimeoutExecute:{0}", DateTime.Now);
                if (BusoffHandler((byte)eCMDType.cmd_Heartbeat))
                {
    
    
                    Console.WriteLine("heartbeat timeout:{0}", DateTime.Now);
                    return;
                }
            }
            HeartbeatTimer.Interval = 3000;     // 第一次检测心跳为上电10s后, 此后每3s检测一次
            HeartbeatTimer.Start();
        }

        private void CMD0TimeoutExecute(object source, System.Timers.ElapsedEventArgs e)
        {
    
    
            CMD0Timer.Stop();
			// 没有接收到对方回复
            {
    
    
                Console.WriteLine("cmd0 TimeoutExecute:{0}", DateTime.Now);
                if (!BusoffHandler((byte)eCMDType.cmd_ElevatorReq))
                {
    
    
                    // 超时未收到 res, 重发
                    CMD0ResendCount++;
                    SendCMD0();
                }
                Console.WriteLine("CMD0 get res timeout, resend.CMD0ResendCount = {0}", CMD0ResendCount);
            }
        }

        private void CMD3TimeoutExecute(object source, System.Timers.ElapsedEventArgs e)
        {
    
    
            CMD3Timer.Stop();
			// 没有接收到对方回复, 重发
            {
    
    
                Console.WriteLine("cmd3 TimeoutExecute:{0}", DateTime.Now);
                if (!BusoffHandler((byte)eCMDType.cmd_ElTOLiSendFloor))
                {
    
    
                    // 超时未收到res, 重发
                    CMD3ResendCount++;
                    SendCMD3();
                }
                Console.WriteLine("CMD3 get res timeout, resend.ResendCount = {0}", CMD3ResendCount);
            }
        }

        // delay 10ms before send cmd package
        private void SendtimerTimeoutExecute(object source, System.Timers.ElapsedEventArgs e)
        {
    
    
            SendTimer.Stop();
            SendTimertimeout.Set();
            Console.WriteLine("Send timer timeout, time = {0}", DateTime.Now.TimeOfDay);
        }

        public void uartInit()
        {
    
    
            uart = new SerialPortStream();
            uart.BaudRate = 115200;
            uart.PortName = "/dev/ttymxc2";
        //    uart.PortName = "COM8";
            uart.Parity = Parity.None;
            uart.StopBits = StopBits.One;
            uart.DataReceived += uartDataReceived;

            // 初始化心跳定时器
            HeartbeatTimer = new System.Timers.Timer(10000);         // 心跳超时时间 第一次进中断为10s以后
            HeartbeatTimer.Elapsed += new System.Timers.ElapsedEventHandler(HeartbeatTimeoutExecute);
            HeartbeatTimer.AutoReset = true;

            // 初始化cmd0 定时器
            CMD0Timer = new System.Timers.Timer(500);
            CMD0Timer.Elapsed += new System.Timers.ElapsedEventHandler(CMD0TimeoutExecute);
            CMD0Timer.AutoReset = false;

            // 初始化cmd3 定时器
            CMD3Timer = new System.Timers.Timer(500);
            CMD3Timer.Elapsed += new System.Timers.ElapsedEventHandler(CMD3TimeoutExecute);
            CMD3Timer.AutoReset = false;

            // 初始化 send cmd 定时器
            SendTimer = new System.Timers.Timer(10);
            SendTimer.Elapsed += new System.Timers.ElapsedEventHandler(SendtimerTimeoutExecute);
            SendTimer.AutoReset = false;

            try
            {
    
    
                uart.Open();
            }
            catch (Exception e)
            {
    
    
                Console.WriteLine("uart open error:{0}", e);
            }
            Console.WriteLine("init uart.!");
        }

        public void Task()
        {
    
    
            Console.WriteLine(" uart task.!");
            string cmd = "";
            
            if(uart.IsOpen)
            {
    
    
                Console.WriteLine(" open heartbeat timer.!");
                HeartbeatTimer.Enabled = true;
                HeartbeatTimer.Start();
            }
                
            while (true)
            {
    
    
                Console.WriteLine("pls input cmd(end with enter_ksy): ");
                cmd = Console.ReadLine();

                switch (cmd)
                {
    
    
                    case "cmd0":
                        SynSign.mUpdateEventForRS485.Set();
                        break;
                    case "cmd3":
                        ElevatorSendCMD3();
                        break;
                    case "open heart":
                        HeartbeatTimer.Enabled = true;
                        HeartbeatTimer.Start();
                        break;
                    case "close heart":
                        HeartbeatTimer.Stop();
                        break;
                    default:
                        Console.WriteLine("invalid cmd: {0}", cmd);
                        break;
                }
                ElevatorSendCMD0();
            }
            
        }

        static void Main()
        {
    
    
       //     RS485 uart485 = new RS485();
            Task Task = new TaskFactory().StartNew(() =>
            {
    
    
                new Task(() =>
                {
    
    
                    Console.WriteLine("Create rs485 task");
                    new RS485().Task();
                }).Start();
            });

            Thread.CurrentThread.Join();
        }

    }
}

猜你喜欢

转载自blog.csdn.net/weixin_45444963/article/details/113863482
今日推荐