样品串口通讯科code。使用异步API在.NET 4.5?串口、样品、通讯、code

2023-09-03 03:00:19 作者:想。

任何人都可以点我到使用.NET 4.5异步API(异步,待机,任务<>,ReadAsync等)工作的例子?做串行通信

我试着去适应现有的事件驱动的串行样本,和我得到的各种恐怖行为 - 端口被其他应用程序使用的错误,VS2013调试抛出异常,并锁定了 - 这通常需要一台PC重启从恢复。

修改

我已经写了从头开始我自己的样本。这是一个简单的WinForms项目写入到输出窗口。三个按钮的形式 - 打开端口,关闭端口和读取数据。该ReadDataAsync方法调用SerialPort.BaseStream.ReadAsync。

截至目前,它会从端口读取数据,但我遇到了问题,使其健壮。

例如,如果我拔掉串行电缆,打开端口,然后单击读取数据的两倍,我会得到一个System.IO.IOException(那种我期望的),但我的应用程序停止响应。

更糟的是,当我试图阻止我的程序,VS2013抛出了一个正在进行停止调试对话框,它永远不会完成,而我甚至不能杀VS从任务管理器。在每次发生这种情况的时间来重新启动我的电脑。

不是很好。

 使用系统;
使用System.Collections.Generic;
使用System.ComponentModel;
使用System.Data这;
使用System.Drawing中;
使用System.Linq的;
使用System.Text;
使用System.Threading.Tasks;
使用System.Windows.Forms的;

使用System.IO.Ports;

命名空间WindowsFormsApplication1
{
    公共部分类Form1中:形态
    {
        私人的SerialPort _serialPort;

        公共Form1中()
        {
            的InitializeComponent();
        }

        私人无效openPortbutton_Click(对象发件人,EventArgs的)
        {
                尝试
                {
                    如果(_serialPort == NULL)
                        _serialPort =新的SerialPort(COM3,9600,Parity.None,8,StopBits.One);

                    如果(!_serialPort.IsOpen)
                        _serialPort.Open();

                    Console.Write(打开...);
                }
                赶上(例外前)
                {
                    ClosePort();
                    的MessageBox.show(ex.ToString());
                }
        }

        私人无效closePortButton_Click(对象发件人,EventArgs的)
        {
            ClosePort();
        }

        私人异步无效ReadDataButton_Click(对象发件人,EventArgs的)
        {
            尝试
            {
                等待ReadDataAsync();
            }
            赶上(例外前)
            {
                ClosePort();
                的MessageBox.show(ex.ToString(),ReadDataButton_Click);
            }
        }

        私人异步任务ReadDataAsync()
        {
            byte []的缓冲区=新的字节[4096];
            任务< INT> readStringTask = _serialPort.BaseStream.ReadAsync(缓冲液,0,100);

            如果(!readStringTask.IsCompleted)
                Console.WriteLine(请稍候...);

            INT读取动作=等待readStringTask;

            字符串数据= Encoding.ASCII.GetString(缓冲,0,读取动作);

            Console.WriteLine(数据);
        }


        私人无效ClosePort()
        {
            如果(_serialPort == NULL)回报;

            如果(_serialPort.IsOpen)
                _serialPort.Close();

            _serialPort.Dispose();

            _serialPort = NULL;

            Console.WriteLine(关闭);
        }

        私人无效Form1_FormClosed(对象发件人,FormClosedEventArgs E)
        {
            ClosePort();
        }

    }
}
 
Moxa NPort 5230系列 2口RS 232 422 485串口服务器

解决方案

我会使用 TaskCompletionSource<> SerialDataReceivedEvent ,像这样(未经测试):

 使用系统;
使用System.IO.Ports;
使用的System.Threading;
使用System.Threading.Tasks;

类PortDataReceived
{
    公共静态异步任务ReadPort(的SerialPort端口,的CancellationToken令牌)
    {
        而(真)
        {
            token.ThrowIfCancellationRequested();

            等待TaskExt.FromEvent< SerialDataReceivedEventHandler,SerialDataReceivedEventArgs>(
                (完整的,取消,拒绝)=> //得到处理
                    (发件人,参数)=>完整(参数),
                处理器=> // 订阅
                    port.DataReceived + =处理程序,
                处理器=> //退订
                    port.DataReceived  -  =处理程序,
                (完整的,取消,拒绝)=> //开始操作
                    {如果(port.BytesToRead!= 0)完整的(空); },
                令牌);

            Console.WriteLine(收到:+ port.ReadExisting());
        }
    }

    公共静态无效的主要()
    {
        的SerialPort口=新的SerialPort(COM1);

        port.BaudRate = 9600;
        port.Parity = Parity.None;
        port.StopBits = StopBits.One;
        port.DataBits = 8;
        port.Handshake = Handshake.None;

        port.Open();

        Console.WriteLine(preSS Enter停止......);
        Console.WriteLine();

        VAR CTS =新CancellationTokenSource();
        VAR任务= ReadPort(端口,cts.Token);

        到Console.ReadLine();

        cts.Cancel();
        尝试
        {
            task.Wait();
        }
        赶上(AggregateException前)
        {
            Console.WriteLine(ex.InnerException.Message);
        }

        port.Close();
    }

    // FromEvent&其中;>中基于http://stackoverflow.com/a/22798789/1768303
    公共静态类TaskExt
    {
        公共静态异步任务< TEventArgs> FromEvent< TEventHandler,TEventArgs>(
            Func键<作用< TEventArgs>中行动,行动<异常>中TEventHandler> getHandler,
            动作< TEventHandler>订阅,
            动作< TEventHandler>退订,
            动作<作用< TEventArgs>中行动,行动<异常>>发起,
            的CancellationToken令牌),其中TEventHandler:类
        {
            VAR TCS =新TaskCompletionSource< TEventArgs>();

            动作< TEventArgs>完整=(参数)=> tcs.TrySetResult(参数);
            行动取消=()=> tcs.TrySetCanceled();
            动作<异常>拒绝=(前)=> tcs.TrySetException(前);

            TEventHandler处理器= getHandler(完成,取消,拒绝);

            订阅(处理);
            尝试
            {
                使用(token.Register(()=> tcs.TrySetCanceled()))
                {
                    启动(完成,取消,拒绝);
                    返回等待tcs.Task;
                }
            }
            最后
            {
                退订(处理);
            }
        }
    }
}
 

Can anyone point me to a working example that uses the .net 4.5 Async API (async, await, task<>, ReadAsync, etc) to do serial communications?

I've tried to adapt an existing event driven serial sample, and am getting all kinds of horrible behavior - "port in use by other application" errors, VS2013 debugger throwing exceptions and locking up - which usually require a PC reboot to recover from.

edit

I've written my own sample from scratch. It's a simple Winforms project that writes to the Output window. Three buttons on the form - Open Port, Close Port, and Read Data. The ReadDataAsync method calls SerialPort.BaseStream.ReadAsync.

As of now, it will read data from the port, but I'm running into problems making it robust.

For example, if I unplug the serial cable, open the port, and click Read Data twice, I will get an System.IO.IOException (which I kind of expect), but my app stops responding.

Worse, when I try to stop my program, VS2013 throws up a "Stop Debugging in Progress" dialog, which never completes, and I can't even kill VS from Task Manager. Have to reboot my PC every time this happens.

Not good.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.IO.Ports;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private SerialPort _serialPort;

        public Form1()
        {
            InitializeComponent();
        }

        private void openPortbutton_Click(object sender, EventArgs e)
        {
                try
                {
                    if (_serialPort == null )
                        _serialPort = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One);

                    if (!_serialPort.IsOpen)
                        _serialPort.Open();

                    Console.Write("Open...");
                }
                catch(Exception ex)
                {
                    ClosePort(); 
                    MessageBox.Show(ex.ToString());
                }
        }

        private void closePortButton_Click(object sender, EventArgs e)
        {
            ClosePort();
        }

        private async void ReadDataButton_Click(object sender, EventArgs e)
        {
            try
            {
                await ReadDataAsync();
            }
            catch (Exception ex)
            {
                ClosePort();
                MessageBox.Show(ex.ToString(), "ReadDataButton_Click");
            }
        }

        private async Task ReadDataAsync()
        {
            byte[] buffer = new byte[4096];
            Task<int> readStringTask = _serialPort.BaseStream.ReadAsync(buffer, 0, 100);

            if (!readStringTask.IsCompleted)
                Console.WriteLine("Waiting...");

            int bytesRead = await readStringTask;

            string data = Encoding.ASCII.GetString(buffer, 0, bytesRead);

            Console.WriteLine(data);
        }


        private void ClosePort()
        {
            if (_serialPort == null) return;

            if (_serialPort.IsOpen)
                _serialPort.Close();

            _serialPort.Dispose();

            _serialPort = null;

            Console.WriteLine("Close");
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            ClosePort();
        }

    }
}

解决方案

I'd use TaskCompletionSource<> to wrap SerialDataReceivedEvent, something like this (untested):

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

class PortDataReceived
{
    public static async Task ReadPort(SerialPort port, CancellationToken token)
    {
        while (true)
        {
            token.ThrowIfCancellationRequested();

            await TaskExt.FromEvent<SerialDataReceivedEventHandler, SerialDataReceivedEventArgs>(
                (complete, cancel, reject) => // get handler
                    (sender, args) => complete(args),
                handler => // subscribe
                    port.DataReceived += handler,
                handler => // unsubscribe
                    port.DataReceived -= handler,
                (complete, cancel, reject) => // start the operation
                    { if (port.BytesToRead != 0) complete(null); },
                token);

            Console.WriteLine("Received: " + port.ReadExisting());
        }
    }

    public static void Main()
    {
        SerialPort port = new SerialPort("COM1");

        port.BaudRate = 9600;
        port.Parity = Parity.None;
        port.StopBits = StopBits.One;
        port.DataBits = 8;
        port.Handshake = Handshake.None;

        port.Open();

        Console.WriteLine("Press Enter to stop...");
        Console.WriteLine();

        var cts = new CancellationTokenSource();
        var task = ReadPort(port, cts.Token);

        Console.ReadLine();

        cts.Cancel();
        try
        {
            task.Wait();
        }
        catch (AggregateException ex)
        {
            Console.WriteLine(ex.InnerException.Message);
        }

        port.Close();
    }

    // FromEvent<>, based on http://stackoverflow.com/a/22798789/1768303
    public static class TaskExt
    {
        public static async Task<TEventArgs> FromEvent<TEventHandler, TEventArgs>(
            Func<Action<TEventArgs>, Action, Action<Exception>, TEventHandler> getHandler,
            Action<TEventHandler> subscribe,
            Action<TEventHandler> unsubscribe,
            Action<Action<TEventArgs>, Action, Action<Exception>> initiate,
            CancellationToken token) where TEventHandler : class
        {
            var tcs = new TaskCompletionSource<TEventArgs>();

            Action<TEventArgs> complete = (args) => tcs.TrySetResult(args);
            Action cancel = () => tcs.TrySetCanceled();
            Action<Exception> reject = (ex) => tcs.TrySetException(ex);

            TEventHandler handler = getHandler(complete, cancel, reject);

            subscribe(handler);
            try
            {
                using (token.Register(() => tcs.TrySetCanceled()))
                {
                    initiate(complete, cancel, reject);
                    return await tcs.Task;
                }
            }
            finally
            {
                unsubscribe(handler);
            }
        }
    }
}