C#串口通讯(变参、委托、线程安全队列)

这两天由于项目需要,调试了一下C#的串口通讯,参考了以前做的Android项目所用的设计模式:

C#串口通讯(变参、委托、线程安全队列)_第1张图片

父类DataTransport设计成单例模式,程序始终保持只有一种通讯链路,DataTransport中的方法都由子类实现。
由于对C#不是很熟练,调试过程中遇到了较多问题,最终通过努力找到了解决方法,稍微总结一下。

变参函数

 串口、蓝牙和Wifi的设置参数不同,它们各自的setConfig函数的形参也不同,如果设计不同的方法,父类就不能统一继承了。使用C#的__arglist来解决这个问题。

DataTransport.cs

public virtual void setConfig(__arglist) { }

Usart.cs

public override void setConfig(__arglist)
{
    ArgIterator args = new ArgIterator(__arglist);
    if (args.GetRemainingCount() > 0)
    {                
        port.PortName = TypedReference.ToObject(args.GetNextArg()).ToString();
        port.BaudRate = Convert.ToInt32(TypedReference.ToObject(args.GetNextArg()));
        port.DataBits = Convert.ToInt32(TypedReference.ToObject(args.GetNextArg()));
        port.StopBits = (StopBits)TypedReference.ToObject(args.GetNextArg());
        port.Parity = (Parity)TypedReference.ToObject(args.GetNextArg());
    }         
}

串口数据的收发和解析

串口之间的通讯需要读写并发,读取port中的数据并解析这个过程使用了线程安全的Queue作为缓存,避免另一端发送数据太快导致数据不能及时解析而丢失

C#串口通讯(变参、委托、线程安全队列)_第2张图片

在使用Java编写这一段逻辑的时候,使用了线程安全的ArrayBlockingQueue,然而C#并没有提供此类的API,可以使用Queue.Synchronized来保证多线程访问Queue同步
DQueue = Queue.Synchronized(new Queue());
数据接收可以用线程不断的读取串口,也可以用事件触发的方式,我采用了后者:
port.DataReceived += Port_DataReceived;
private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    try {
        byte[] readBuffer = new byte[port.BytesToRead];
        int count = port.Read(readBuffer, 0, readBuffer.Length);

        for (int i = 0; i < count; i++)
        {
            DQueue.Enqueue(readBuffer[i]);
        }               
    } catch(Exception ex) {
        throw ex;
    }
}
解析用线程实现,循环判断Queue的大小,Dequeue元素:
try
{
    b = (byte)DataTransport.getInstance().DQueue.Dequeue();
}
catch (Exception e)
{
    throw e;
}

数据广播(分发)到前台UI更新

数据解析完成后要在界面上实时更新,也可能需要发送给另外的业务逻辑模块进一步处理,在Android平台使用Handler来实现:
switch(command)
{
case CommandHelper.COMMAND_RESET:
    if(ShareValues.msgHandler != null)
    {
        Message msg = ShareValues.msgHandler.obtainMessage();
        msg.what = CommandHelper.COMMAND_RESET;
        ShareValues.msgHandler.sendMessage(msg);
    }       
    break;
case CommandHelper.COMMAND_REALTIMEDATA:
    RealTimeData rt = CommUtils.getRealTimeData(read_one);
    if(ShareValues.msgHandler != null)
    {
        Message msg = ShareValues.msgHandler.obtainMessage();
        msg.what = ShareValues.MSG_SUC;
        msg.obj = rt;
        ShareValues.msgHandler.sendMessage(msg);
    }                           
    break;
case CommandHelper.COMMAND_ENCODER:
    if(ShareValues.msgHandler != null)
    {
        Message msg = ShareValues.msgHandler.obtainMessage();
        msg.what = CommandHelper.COMMAND_ENCODER;
        msg.obj = CommUtils.byteArrayToInt(read_one, 4, 1);
        msg.arg1 = CommUtils.byteArrayToInt(read_one, 5, 2);
        msg.arg2 = CommUtils.byteArrayToInt(read_one, 7, 2);
        ShareValues.msgHandler.sendMessage(msg);
    }                                                                               
    break;
case CommandHelper.COMMAND_BATTERY_ID:/

    break;
case CommandHelper.COMMAND_RELOAD_WIFI:

default:break;
}
C#中需要用到EventHandler和delegate来实现:
class MessageEventArgs : EventArgs {
 private byte[] src;
 public byte[] Src
 {
     get{return src;}set
     {src = value;}
 }
}
...
public delegate void changeEventHandler(object sender , EventArgs args);
public event changeEventHandler changed;
protected virtual void onChanged(EventArgs args)
{
    if (this.changed != null)
        this.changed(this, args);
}
...
MessageEventArgs args = new MessageEventArgs();
args.Src = read_one;
onChanged(args);
数据接收事件触发后处理函数:
private void textChanged(object sender,EventArgs args) {
    MessageEventArgs mArgs = (MessageEventArgs)args;
    receiveLen += mArgs.DataLen;
    txt_msg.Dispatcher.Invoke(new Action(delegate { txt_msg.Text = "收到数据:"+receiveLen+"   发送数据:"+sendLen; }));
}
...
AnalysisData adata = new AnalysisData();
adata.changed += textChanged;
上一段代码中Dispatcher.Invoke是将接收和发送的数据显示到TextBox中,前台用的WPF,UI的更新必须在UI线程,如果直接在这里更新会报异常。
最终效果:

C#串口通讯(变参、委托、线程安全队列)_第3张图片

调试工具用到了虚拟串口VSPD,SSCOM串口调试助手。

你可能感兴趣的:(C#)