最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于WPF編寫一個串口轉(zhuǎn)UDP工具

 更新時間:2023年04月03日 08:20:14   作者:微小冷  
串口是設(shè)備和上位機(jī)通信的常用接口,UDP則是網(wǎng)絡(luò)通信常用的通信協(xié)議,串口和UDP之間的相互轉(zhuǎn)換是非常有意義的。本文就利用WPF編寫一個串口轉(zhuǎn)UDP工具,需要的可以參考一下

串口是設(shè)備和上位機(jī)通信的常用接口,UDP則是網(wǎng)絡(luò)通信常用的通信協(xié)議,通過將串口設(shè)備上傳的指令,用UDP發(fā)送出去,或者將UDP傳來的指令轉(zhuǎn)發(fā)給串口設(shè)備,就可以實(shí)現(xiàn)設(shè)備的遠(yuǎn)程控制。所以,串口和UDP之間的相互轉(zhuǎn)換是非常有意義的。

如果不熟悉C#串口以及UDP通信的相關(guān)內(nèi)容,可以參考這兩篇博客:C#串口通信 C# UDP通信

本項(xiàng)目會用到基于Task的并發(fā)編程,如果不了解,可以參照這篇:Task詳解

框架準(zhǔn)備

盡管希望做一個轉(zhuǎn)發(fā)工具,但如果自身不會發(fā)送的話,那還得再用其他軟件進(jìn)行測試,所以這個轉(zhuǎn)發(fā)工具,理應(yīng)具備串口和UDP協(xié)議的全部功能,這一點(diǎn)也要在界面上體現(xiàn)出來。

新建一個WPF項(xiàng)目,名字是portUDP,然后開始布局,結(jié)果如下

其中,串口設(shè)置中包含波特率、數(shù)據(jù)位、停止位和奇偶校驗(yàn)等信息,由于不常更換,所以隱藏起來。

串口只需要一個,但UDP通信需要設(shè)置本機(jī)和目標(biāo)的IP地址與端口。自動轉(zhuǎn)發(fā)單選框選中后,會自動將接收到的串口數(shù)據(jù)轉(zhuǎn)給UDP,并且UDP收到的數(shù)據(jù)也會轉(zhuǎn)給串口。

窗口尺寸為320 × 640 320\times640320×640,外部采用一個DockPanel,并對常用控件進(jìn)行基本的外觀設(shè)置

<DockPanel.Resources>
    <Style TargetType="TextBox">
        <Setter Property="VerticalAlignment" Value="Center"/>
        <Setter Property="Margin" Value="2"/>
    </Style>
    <Style TargetType="TextBlock">
        <Setter Property="VerticalAlignment" Value="Center"/>
        <Setter Property="HorizontalAlignment" Value="Center"/>
    </Style>
    <Style TargetType="ComboBox">
        <Setter Property="VerticalAlignment" Value="Center"/>
        <Setter Property="Margin" Value="2"/>
    </Style>
    <Style TargetType="Button">
        <Setter Property="VerticalAlignment" Value="Center"/>
        <Setter Property="Margin" Value="2"/>
    </Style>
    <Style TargetType="CheckBox">
        <Setter Property="VerticalAlignment" Value="Center"/>
        <Setter Property="HorizontalAlignment" Value="Center"/>
        <Setter Property="Margin" Value="2"/>
    </Style>
</DockPanel.Resources>

左側(cè)控制面板被寫在一個StackPanel中,最上面是一個Expander,里面包含串口設(shè)置的相關(guān)信息

<Expander Header="串口設(shè)置">
    <UniformGrid Columns="2" Visibility="Visible">
        <TextBlock Text="波特率"/>
        <ComboBox x:Name="cbBaud"/>
        <TextBlock Text="數(shù)據(jù)位"/>
        <ComboBox x:Name="cbDataBit"/>
        <TextBlock Text="停止位"/>
        <ComboBox x:Name="cbStopBit"/>
        <TextBlock Text="校驗(yàn)位"/>
        <ComboBox x:Name="cbParity"/>
    </UniformGrid>
    
</Expander>

然后是一個GroupBox,用于進(jìn)行基本設(shè)置,其中兩個按鈕需要在連接后更改內(nèi)容,所以設(shè)了個名字。

<UniformGrid Columns="2">
    <ComboBox x:Name="cbPorts" Margin="2"/>
    <Button x:Name="btnPort" Content="連接串口"/>

    <TextBox x:Name="txtSrcIP" Text="127.0.0.1"/>
    <TextBox x:Name="txtSrcPort" Text="91"/>
    
    <TextBox x:Name="txtDstIP" Text="127.0.0.1"/>
    <TextBox x:Name="txtDstPort" Text="91"/>
    
    <CheckBox Content="自動轉(zhuǎn)發(fā)" IsChecked="True"/>
    <Button  x:Name="btnUDP" Content="創(chuàng)建服務(wù)"/>
</UniformGrid>

最后是發(fā)送文本框與發(fā)送按鈕等,內(nèi)容如下

<TextBox x:Name="txtSend" TextWrapping="Wrap" Height="70"/>
<UniformGrid Columns="3">
    <CheckBox Content="Hex" IsChecked="False"/>
    <Button Content="串口發(fā)送"/>
    <Button Content="UDP發(fā)送"/>
    <CheckBox Content="時間"/>
    <Button Content="清空日志"/>
    <Button Content="保存日志"/>
</UniformGrid>

左側(cè)控制界面布局完成后,是右側(cè)的接收區(qū)域,內(nèi)容如下

<GroupBox Header="日志信息">
    <TextBox x:Name="txtInfo"  Height="270"/>
</GroupBox>

初始化

由于.Net6.0不內(nèi)置串口庫,所以需要額外下載,點(diǎn)擊菜單欄工具->NuGet包管理器->管理解決方案的NuGet包,點(diǎn)擊瀏覽選項(xiàng)卡,搜索Ports,選擇System.IO.Ports,安裝。

在對用戶界面進(jìn)行最簡單的布局后,可以在C#代碼中,對一些ComboBox做進(jìn)一步的設(shè)置。

public void initComContent()
{
    // 串口號
    cbPorts.ItemsSource = SerialPort.GetPortNames();
    cbPorts.SelectedIndex = 0;
    
    // 波特率
    cbBaud.ItemsSource = new int[] { 9600, 19200, 38400, 115200 };
    cbBaud.SelectedIndex = 3;

    // 數(shù)據(jù)位
    cbDataBit.ItemsSource = Enumerable.Range(1, 8);
    cbDataBit.SelectedIndex = 7;
    
    // 校驗(yàn)位
    cbParity.ItemsSource = Enum.GetNames(typeof(Parity));
    cbParity.SelectedIndex = 0;
    
    //停止位
    cbStopBit.ItemsSource = Enum.GetNames(typeof(StopBits));
    cbStopBit.SelectedIndex = 1;
}

這樣,在打開軟件之后,串口設(shè)置如下

串口設(shè)置

接下來設(shè)置串口,在xml編輯界面,將btnPort后面添加Click="btnPort_Click"后,按下F12,IDE會自動創(chuàng)建對應(yīng)的函數(shù)。

<Button x:Name="btnPort" Content="連接串口" Click="btnPort_Click"/>

在寫串口開關(guān)按鈕的控制指令之前,先新建一個全局的串口對象,然后寫btnPort_Click內(nèi)容

SerialPort sp;
private void btnPort_Click(object sender, RoutedEventArgs e)
{
    if (btnPort.Content.ToString() == "打開串口")
    {
        string name = cbPorts.SelectedItem.ToString();
        try
        {
            sp = new SerialPort(name,
                (int)cbBaud.SelectedItem,
                (Parity)cbParity.SelectedIndex,
                (int)cbDataBit.SelectedItem,
                (StopBits)cbStopBit.SelectedIndex);

            sp.Open();
            sp.DataReceived += Sp_DataReceived;
            txtInfo.AppendText($"串口{name}打開成功");
        }
        catch(Exception ex)
        {
            txtInfo.AppendText($"串口{name}打開失敗,原因是{ex}");
        }
    }
    else
    {
        try
        {
            sp.Close();
            initComContent();
        }
        catch (Exception ex)
        {
            txtInfo.AppendText($"串口關(guān)閉失敗,原因是{ex}");
        }
    }
    btnPort.Content = sp.IsOpen ? "關(guān)閉串口" : "打開串口";
}

其中sp.DataReceived += Sp_DataReceived;新增一個委托,用于規(guī)范串口接收到數(shù)據(jù)后的行為。

UDP設(shè)置

和串口設(shè)置相同,UDP也需要新建用于UDP通信的全局變量,包括本機(jī)節(jié)點(diǎn)、目標(biāo)節(jié)點(diǎn)以及UDP服務(wù)。

UdpClient udp;
IPEndPoint ptSrc;
IPEndPoint ptDst;

然后設(shè)置創(chuàng)建服務(wù)按鈕后,其邏輯與串口是相似的,都是在創(chuàng)建或關(guān)閉服務(wù)時,用try-catch語句以找到錯誤。

private void btnUDP_Click(object sender, RoutedEventArgs e)
{
    if (btnUDP.Content.ToString() == "創(chuàng)建服務(wù)")
    {
        try
        {
            ptSrc = new IPEndPoint(IPAddress.Parse(txtSrcIP.Text), int.Parse(txtSrcPort.Text));
            ptDst = new IPEndPoint(IPAddress.Parse(txtDstIP.Text), int.Parse(txtDstPort.Text));
            udp = new UdpClient(ptSrc);
            txtInfo.AppendText("成功創(chuàng)建服務(wù)");
            btnUDP.Content = "關(guān)閉服務(wù)";
        }catch(Exception ex)
        {
            txtInfo.AppendText($"服務(wù)創(chuàng)建失敗,原因?yàn)閧ex}\n");
        }
    }
    else
    {
        try
        {
            udp.Close();
            btnUDP.Content = "創(chuàng)建服務(wù)";
        }
        catch(Exception ex)
        {
            txtInfo.AppendText($"服務(wù)關(guān)閉失敗,原因?yàn)閧ex}");
        }
    }

???????}

發(fā)送設(shè)置

首先是串口發(fā)送,在xaml文件中,為串口發(fā)送按鈕掛載一個Click動作,其內(nèi)容即為串口發(fā)送功能

<Button Content="串口發(fā)送" Click="btnPortSend_Click"/>
private void btnPortSend_Click(object sender, RoutedEventArgs e)
{
    var data = Encoding.UTF8.GetBytes(txtSend.Text);
    txtInfo.AppendText($"串口發(fā)送{txtSend.Text}\n");
    sp.Write(data, 0, data.Length);
}

然后是UDP發(fā)送,其改裝過程也大同小異

<Button Content="UDP發(fā)送" Click="btnUDPSend_Click"/>
private void btnUDPSend_Click(object sender, RoutedEventArgs e)
{
    var data = Encoding.UTF8.GetBytes(txtSend.Text);
    txtInfo.AppendText($"UDP發(fā)送{txtSend.Text}\n");
    udp.Send(data, data.Length, ptDst);    //將內(nèi)容發(fā)給ptDst
}

轉(zhuǎn)發(fā)設(shè)置

轉(zhuǎn)發(fā)是本軟件的核心功能,但其前提是接收到數(shù)據(jù)。所以,先來充實(shí)創(chuàng)建串口時就已經(jīng)提到的Sp_DataReceived函數(shù)

private void Sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    byte[] data = new byte[sp.BytesToRead];
    sp.Read(data, 0, data.Length);//從串口讀取數(shù)據(jù)
    Dispatcher.Invoke(() => spReceived(data));
}

private void spReceived(byte[] data)
{
    string info = Encoding.UTF8.GetString(data);
    txtInfo.AppendText("串口接收數(shù)據(jù):{info}");
    if ((bool)chkTransmit.IsChecked)
    {
        try
        {
            udp.Send(data, data.Length, ptDst);    //將內(nèi)容發(fā)給ptDst
            txtInfo.AppendText($"UDP轉(zhuǎn)發(fā):{info}");
        }
        catch (Exception ex)
        {
            txtInfo.AppendText($"UDP轉(zhuǎn)發(fā)失敗,原因?yàn)閧ex}\nd");
        }
    }
}

然后創(chuàng)建UPD接收和轉(zhuǎn)發(fā)函數(shù)

private void udpReceiving(CancellationToken token)
{
    while (! token.IsCancellationRequested)
    {
        var data = udp.Receive(ref ptDst);
        Dispatcher.Invoke(() => udpReceived(data));
    }
}
private void udpReceived(byte[] data)
{
    string info = Encoding.UTF8.GetString(data);
    txtInfo.AppendText("UDP接收數(shù)據(jù):{info}");
    if ((bool)chkTransmit.IsChecked)
    {
        try
        {
            sp.Write(data, 0, data.Length);
            txtInfo.AppendText($"串口轉(zhuǎn)發(fā){info}\n");
        }
        catch (Exception ex)
        {
            txtInfo.AppendText($"串口轉(zhuǎn)發(fā)失敗,原因?yàn)閧ex}");
        }
    }
}

其中,udpReceiving里面是一個死循環(huán),表示一直等待UDP信息的到來,這個函數(shù)作為一個Task的創(chuàng)建時機(jī),自然是在UDP服務(wù)創(chuàng)建之時,

//...
txtInfo.AppendText("成功創(chuàng)建服務(wù)");
//這是一個全局變量
cts = new CancellationTokenSource();
Task.Run(() => udpReceiving(cts.Token), cts.Token);

測試

至此,一個串口-UDP轉(zhuǎn)發(fā)工具便算完成了,盡管界面上還有幾個功能沒有實(shí)現(xiàn),比如Hex以及時間的單選框等,但這些均為錦上添花。

下面做一下基礎(chǔ)的測試,效果如下

到此這篇關(guān)于基于WPF編寫一個串口轉(zhuǎn)UDP工具的文章就介紹到這了,更多相關(guān)WPF串口轉(zhuǎn)UDP內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C# 類型轉(zhuǎn)換(隱式類型,顯式類型,自定義類型)

    C# 類型轉(zhuǎn)換(隱式類型,顯式類型,自定義類型)

    本文詳細(xì)介紹了C#中的類型轉(zhuǎn)換,包括隱式類型轉(zhuǎn)換和顯式類型轉(zhuǎn)換(強(qiáng)制類型轉(zhuǎn)換),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-11-11
  • C#判斷訪問來源是否為搜索引擎鏈接的方法

    C#判斷訪問來源是否為搜索引擎鏈接的方法

    這篇文章主要介紹了C#判斷訪問來源是否為搜索引擎鏈接的方法,實(shí)例分析了C#解析URL來源的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-03-03
  • 解析C#彩色圖像灰度化算法的實(shí)現(xiàn)代碼詳解

    解析C#彩色圖像灰度化算法的實(shí)現(xiàn)代碼詳解

    本篇文章是對C#中彩色圖像灰度化算法的實(shí)現(xiàn)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C#使用開源驅(qū)動連接操作MySQL數(shù)據(jù)庫

    C#使用開源驅(qū)動連接操作MySQL數(shù)據(jù)庫

    這篇文章主要介紹了C#使用開源驅(qū)動連接操作MySQL數(shù)據(jù)庫,本文講解使用SourceForge上的mysqldrivercs驅(qū)動連接操作MySQL數(shù)據(jù)庫,需要的朋友可以參考下
    2015-02-02
  • 10分鐘學(xué)會VS NuGet包私有化部署

    10分鐘學(xué)會VS NuGet包私有化部署

    本文主要介紹了10分鐘學(xué)會VS NuGet包私有化部署,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • WPF實(shí)現(xiàn)雷達(dá)圖(仿英雄聯(lián)盟)的示例代碼

    WPF實(shí)現(xiàn)雷達(dá)圖(仿英雄聯(lián)盟)的示例代碼

    這篇文章主要介紹了如何利用WPF實(shí)現(xiàn)雷達(dá)圖(仿英雄聯(lián)盟)的繪制,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)或工作有一定幫助,需要的可以參考一下
    2022-07-07
  • C#紋理畫刷TextureBrush用法實(shí)例

    C#紋理畫刷TextureBrush用法實(shí)例

    這篇文章主要介紹了C#紋理畫刷TextureBrush用法,實(shí)例分析了紋理畫刷TextureBrush的具體使用技巧,需要的朋友可以參考下
    2015-06-06
  • Winform基于多線程實(shí)現(xiàn)每隔1分鐘執(zhí)行一段代碼

    Winform基于多線程實(shí)現(xiàn)每隔1分鐘執(zhí)行一段代碼

    這篇文章主要介紹了Winform基于多線程實(shí)現(xiàn)每隔1分鐘執(zhí)行一段代碼的方法,設(shè)計(jì)線程的操作及時間函數(shù)的用法,需要的朋友可以參考下
    2014-10-10
  • 關(guān)于c#中單例模式的一些問題

    關(guān)于c#中單例模式的一些問題

    這篇文章主要給大家介紹了關(guān)于c#中單例模式的一些問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • 基于Unity3D實(shí)現(xiàn)仿真時鐘詳解

    基于Unity3D實(shí)現(xiàn)仿真時鐘詳解

    這篇文章主要為大家詳細(xì)介紹了如何利用Unity3D模擬實(shí)現(xiàn)一個簡單是時鐘效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-01-01

最新評論

绥棱县| 西安市| 吕梁市| 丹阳市| 黔西| 亚东县| 宁明县| 瓦房店市| 化州市| 大化| 东兴市| 呼伦贝尔市| 山东省| 壤塘县| 岳池县| 台南县| 博爱县| 吉安县| 黎川县| 始兴县| 东兰县| 绥江县| 信丰县| 通榆县| 靖宇县| 呼玛县| 杨浦区| 津南区| 星子县| 海城市| 青铜峡市| 木兰县| 山阴县| 道孚县| 金寨县| 高平市| 南溪县| 西盟| 红桥区| 丰顺县| 甘南县|