• 对项目进行代码重构


        临近公司放假,也好在年前对这次项目的一项总结(包括代码)。刚分配到这份项目时,我认为自己可以完成这次的项目,但是在项目的开发过程中,还是遇到许多的问题,总的来说,分为以下几点:

        1.前期对项目的背景不是很清楚(我一开始以为只要拿到项目就开始Coding,完成项目,其实不是这样);

        2.文档写的不够完善(只写了需求分析、概要设计、没有数据库设计,因为文件都是存放在XML文档里);

        3.对使用的技术不会(操作RS232串口,以前在学校从来没学过这些,当然,也不需要学习这些。本人认为,自己主动去学和学校所教最大的区别在于主动是有意识的去学,而学校所教是被动的);

        4.与老板(客户)的沟通不够,客户那边一有新的需求,就马上换成新的需求,然后换来换去,弄的心情很不爽,特别是一个项目需要分成好几种类型,比如,四川省的我要这种,福建省的我要那种,北京市的又是另外的,而且还是同时更新。所以改的比较痛苦,也很纠结。(现在已改用OO的方法);

        5.最重要的是我自己对技术看的太轻松了,我认为技术会了就是会了,其实太天真了~ ~(其实是看个人的心态拉,我认为自己还Ok)。

         所以,我一直在改变、进步!好了,下面是项目中一个串口操作类重构后的Code。

         这里我专门写了一个类来处理对串口的操作,主要是write、read,其中有用到包括delegate、event、lock、多线程。

         首先,实例化串行端口,有端口名称 波特率 奇偶校验位 数据位 停止位这些。然后在Program实例化这个串口操作类。

     SettingClass sc=new SettingClass();
        public class SettingClass
    {
    /// <summary>
    /// 实例化串行端口资源 端口名称 波特率 奇偶校验位 数据位 停止位.
    /// </summary>
    public static SerialPort serPort = null;


    //声明委托
    public delegate void InsertData(byte[] ByData);
    //声明事件
    public static event InsertData GetData;

        写个构造函数,来处理串口对象的初始化

            /// <summary>
    /// 构造函数,处理串口对象的初始化.
    /// </summary>
    public SettingClass()
    {
    try
    {
    serPort = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
    serPort.DataReceived += new SerialDataReceivedEventHandler(serPort_DataReceived);
    serPort.Open();
    }
    catch (Exception ex)
    {
    System.Windows.Forms.MessageBox.Show("未发现到串口COM1,请检查.!\n" + ex.Message, "错误警告");
    }
    }

        我们还可以写个带参的构造函数,用来后续选择端口的初始化

            /// <summary>
    /// 后续可以选择COM端口初始化
    /// </summary>
    /// <param name="portName"></param>
    /// <param name="baudRate"></param>
    /// <param name="parity"></param>
    /// <param name="dataBits"></param>
    /// <param name="stopBits"></param>
    public SettingClass(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
    {
    try
    {
    serPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
    serPort.DataReceived += new SerialDataReceivedEventHandler(serPort_DataReceived);
    serPort.Open();
    }
    catch (Exception ex)
    {
    System.Windows.Forms.MessageBox.Show("未发现到串口COM1,请检查.!\n" + ex.Message, "错误警告");
    }
    }


        在操作serialport的时候,如果需要实行实时的监听来自设备的数据包,那么,在SerialPort类中有DataReceived事件,当串口的读缓存有数据到达时则触发DataReceived事件。(这个在上一篇文章中有,这里只是提一下)

            /// <summary>
    /// 处理来自设备的数据.事件.
    /// </summary>
    /// 缓存数组
    List<byte> buffer_list = new List<byte>();
    private void serPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
    try
    {
    //获取缓冲区的字节个数.
    int intToRead = serPort.BytesToRead;

    //声明临时数组存储串口数据.
    byte[] byteData = new byte[intToRead];

    //读取来自缓冲区的数据.
    serPort.Read(byteData, 0, byteData.Length);

    byte[] bytedata = new byte[byteData.Length];
    //把接收到的数据保存到缓存里
    for (int i = 0; i < byteData.Length; i++)
    {
    bytedata[i] += byteData[i];
    buffer_list.Add(bytedata[i]);
    }

    lock (_syncLock)
    {
    // 开启新线程
    Thread th = new Thread(new ThreadStart(ReturnBytaData));
    th.Start();
    //if (th.IsAlive)
    //{
    // th.Abort();
    //}
    }
    }
    catch { }

    }
            private static readonly object _syncLock = new object();

        在上面这个事件里面,可以看到我声明了一个缓存数组来接收数据,作用是:如果设备发包只发了一个、或者多个、又或者1个半,因为包是由Code+Length+Data+CRC组成,接收的时候怕只接收到了Code+Length,后面的没接收到,那么这个buffer的用处就来了,可以把前面的包和后面的包拼接成一个完整的包,然后传输到form的接收函数里。设备发的数据包不可能只发一个Code+Length,后面在发一个Code+Length,规则协议和TCP传输原理一样。

       下面的函数是用来处理从设备接收到的数据包,最后只返回Code和Data,返回Code是因为我一个界面上有不同的命令,但是会有相同的参数,视情况而定。在这个函数中,也处理了
    buffer缓存,对设备的数据包进行校验。
    note:下面代码第19行 ,返回的是set ok,返回的是成功的数据包格式,需要进行处理。
     1       /// <summary>
    2 /// 校验从设备得到的数据包
    3 /// </summary>
    4 /// <param name="listbytedata">缓存数组</param>
    5 /// <returns>去除length+crc的数据包</returns>
    6 public void ReturnBytaData()
    7 {
    8 try
    9 {
    10 //查询缓存是否还存在完整数据包
    11 while (buffer_list.Count > 4)
    12 {
    13 //缓存里第一个数据包
    14 int bytelength = buffer_list[1];
    15
    16 //声明需要返回的数据包
    17 byte[] returndata = new byte[bytelength - 1];
    18
    19 if (bytelength != 101)
    20 {
    21 //把缓存内完整的数据包遍历到字节数组
    22 byte[] returnbytedata = new byte[bytelength + 2];
    23 for (int i = 0; i < buffer_list[1] + 2; i++)
    24 {
    25 returnbytedata[i] += buffer_list[i];
    26 }
    27
    28 //判断数据包的数据是否真实
    29 if (bytelength == returnbytedata.Length - 2)
    30 {
    31 //得到高八位、低八位
    32 byte j1 = returnbytedata[returnbytedata.Length - 2];
    33 byte j2 = returnbytedata[returnbytedata.Length - 1];
    34
    35 int sum = 0;
    36
    37 //校验高八位、低八位.
    38 for (int j = 0; j < returnbytedata.Length - 2; j++)
    39 {
    40 sum += returnbytedata[j];
    41 }
    42 byte g = (byte)(sum >> 8);
    43 byte d = (byte)(sum);
    44 if (j1 == g && j2 == d)
    45 {
    46 //得到data data=-crc,-code,-length
    47 int datalength = returnbytedata.Length - 4;
    48 byte[] byteData = new byte[datalength];
    49 Array.Copy(returnbytedata, 2, byteData, 0, datalength);
    50
    51 //得到code
    52 byte[] bytecode = new byte[1];
    53 Array.Copy(returnbytedata, 0, bytecode, 0, 1);
    54
    55 //返回code+data
    56 for (int i = 0; i < bytecode.Length; i++)
    57 {
    58 returndata[i] += bytecode[i];
    59 }
    60 for (int n = 0; n < datalength; n++)
    61 {
    62 returndata[n + 1] += byteData[n];
    63 }
    64
    65 buffer_list.RemoveRange(0, returnbytedata.Length);
    66
    67 if (GetData != null)
    68 {
    69 //绑定到事件
    70 GetData(returndata);
    71 }
    72 }
    73 else
    74 {
    75 buffer_list.RemoveRange(0, returnbytedata[1] + 2);
    76 return;
    77 }
    78 }
    79 else
    80 {
    81 return;
    82 }
    83 }
    84 else
    85 {
    86 buffer_list.RemoveRange(0, buffer_list.Count);
    87 }
    88 }
    89 }
    90 catch { }
    91 }


        最后面的是接收来自form传过来的data.

            /// <summary>
    /// 处理各个窗体传来的数据.各窗体只需要传送命令+数据即可.
    /// </summary>
    /// <param name="by">接收到的数据.</param>
    public static void InsertserPortData(byte[] Data)
    {
    try
    {
    //因为各窗体发来的数据不包括CRC校验,所以长度+2.
    byte[] byteText = new byte[Data.Length + 2];
    int sum = 0;
    for (int i = 0; i < Data.Length; i++)
    {
    byteText[i] += Convert.ToByte(Data[i]);
    }
    //遍历sum的累加和,判断高八位和低八位.
    for (int n = 0; n < byteText.Length; n++)
    {
    sum += byteText[n];
    }
    //高八位.
    byte g = (byte)(sum >> 8);
    //低八位.
    byte d = (byte)(sum);
    //把检验加到字节数组中.
    byteText[byteText.Length - 2] += g;
    byteText[byteText.Length - 1] += d;

    //写入串口.
    serPort.Write(byteText, 0, byteText.Length);

    }
    catch { }
    }


        最后,在需要接收数据包的form里面注册下这个事件。

    SettingClass.GetData += new SettingClass.InsertData(SettingClass_GetData);

       下面的ByData是数据包(去除Length+CRC).

    void SettingClass_GetData(byte[] ByData)

    //处理接收到的数据包,显示到界面里...


       这个项目对我的意义重大,我会一直对它进行重构下去。下次应该就是用OO的方法来写文章了。这个项目是我今年毕业以来独立负责的第一个项目。所以,帮助真的很大,在这里我非常感谢我的公司,相信我,给了我锻炼的机会,也很感谢我的同事FHW、LB、FBY、RJB等。

  • 相关阅读:
    读《构建之法》8、9、10章有感
    评论
    复利计算器(4)——jQuery界面美化、自动补全
    送给搭档的“汉堡”
    MFC之TreeCtrl控件使用经验总结
    MFC绘制图片闪烁详解
    MFC 网络编程中::connect返回-1问题
    C++网络编程之select
    C++关于Condition Variable
    Mutex 和 Lock
  • 原文地址:https://www.cnblogs.com/aehoo/p/a_20120117.html
Copyright © 2020-2023  润新知