C#串口調試工具 (基於WPF史無前例/MVVM結構完整示例版)

来源:https://www.cnblogs.com/zh7791/archive/2018/07/16/9317042.html
-Advertisement-
Play Games

前文 由於經常用到串口調試, 儘管有現成的軟體, 因為前端時間涉及一個二次開發, 就因為一個RtsEnable設置, 折騰半天, 網上各種版本的也很多, 功能擴展的很開也多。所以現在自己做了一個夠用版,基於自己的需求,簡單的實現發送接收功能, 至於那些擴展功能可以自己根據需求添加。 正文 先上個運行 ...


前文

由於經常用到串口調試, 儘管有現成的軟體, 因為前端時間涉及一個二次開發, 就因為一個RtsEnable設置, 折騰半天,  網上各種版本的也很多, 功能擴展的很開也多。所以現在自己做了一個夠用版,基於自己的需求,簡單的實現發送接收功能, 至於那些擴展功能可以自己根據需求添加。

正文

先上個運行效果圖:

 

項目架構

該實例用的GalaSoft.Mvvm, 該插件可以直接在NuGet中並且添加。

1.串口參數 , 為了方便, 埠號並沒有用動態載入的方式, 如下枚舉結構:

    /// <summary>
    /// 埠號
    /// </summary>
    public enum Port
    {
        COM1,
        COM2,
        COM3,
        COM4,
        COM5,
        COM6,
        COM7,
        COM8,
        COM9,
        COM10,
        COM11,
        COM12,
        COM13,
        COM14,
        COM15,
        COM16,
        COM17,
        COM18,
        COM19,
        COM20,
        COM21,
        COM22,
        COM23,
        COM24,
        COM25,
        COM26,
        COM27,
        COM28,
        COM29,
        COM30
    }

    /// <summary>
    /// 奇偶校驗
    /// </summary>
    public enum CheckMode
    {
        None = 0,
        Odd = 1,
        Even = 2,
        Mark = 3,
        Space = 4
    }

    /// <summary>
    /// 停止位
    /// </summary>
    public enum StopBit
    {
        One=1,
        Two=2,
        OnePointFive=3,
    }

2.串口參數配置類 , 

作用: 主要用於綁定界面的參數選項。

    /// <summary>
    /// 串口參數設置類
    /// </summary>
    public class ComParameterConfig : ViewModelBase
    {
        public ComParameterConfig()
        {
            Port = System.Enum.GetValues(typeof(Port));
            CheckMode = System.Enum.GetValues(typeof(CheckMode));
            StopBit = System.Enum.GetValues(typeof(StopBit));
            BaudRate = new List<int>() { 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200, };
            DataBit = new List<int>() { 6, 7, 8 };
        }

        private Array port;
        private Array checkMode;
        private Array stopBit;
        private List<int> dataBit;
        private List<int> baudRate;

        /// <summary>
        ////// </summary>
        public Array Port
        {
            get { return port; }
            set { port = value; RaisePropertyChanged(); }
        }

        /// <summary>
        /// 校驗模式
        /// </summary>
        public Array CheckMode
        {
            get { return checkMode; }
            set { checkMode = value; RaisePropertyChanged(); }
        }

        /// <summary>
        /// 停止位
        /// </summary>
        public Array StopBit
        {
            get { return stopBit; }
            set { stopBit = value; RaisePropertyChanged(); }
        }

        /// <summary>
        /// 波特率
        /// </summary>
        public List<int> BaudRate
        {
            get { return baudRate; }
            set { baudRate = value; RaisePropertyChanged(); }
        }

        /// <summary>
        /// 數據位
        /// </summary>
        public List<int> DataBit
        {
            get { return dataBit; }
            set { dataBit = value; RaisePropertyChanged(); }
        }

    }

3.當前配置參數類

作用: 用於保存當前的串口參數、串口功能開關接收數據等業務。

    /// <summary>
    /// 當前配置參數
    /// </summary>
    public class CurrentParameter : ViewModelBase
    {
        private int baudRdate = 9600;
        private int dataBit = 8;
        private Port port;
        private CheckMode checkMode;
        private StopBit stopBit = StopBit.One;
        private SerialPort serialPort;
        private string dataReceiveInfo;
        private string sendData;
        private bool isOpen;


        #region UI綁定參數

        /// <summary>
        /// 接收區數據
        /// </summary>
        public string DataReceiveInfo
        {
            get { return dataReceiveInfo; }
            set { dataReceiveInfo = value; RaisePropertyChanged(); }
        }

        /// <summary>
        /// 發送數據
        /// </summary>
        public string SendData
        {
            get
            {
                return sendData;
            }
            set { sendData = value; RaisePropertyChanged(); }
        }

        #endregion

        #region 串口參數信息

        /// <summary>
        /// 開關
        /// </summary>
        public bool IsOpen
        {
            get { return isOpen; }
            set { isOpen = value; RaisePropertyChanged(); }
        }


        /// <summary>
        /// 數據位
        /// </summary>
        public int DataBit
        {
            get { return dataBit; }
            set { dataBit = value; RaisePropertyChanged(); }
        }

        /// <summary>
        /// 波特率
        /// </summary>
        public int BaudRdate
        {
            get { return baudRdate; }
            set { baudRdate = value; RaisePropertyChanged(); }
        }

        /// <summary>
        ////// </summary>
        public Port Port
        {
            get { return port; }
            set { port = value; RaisePropertyChanged(); }
        }

        /// <summary>
        /// 校驗
        /// </summary>
        public CheckMode CheckMode
        {
            get { return checkMode; }
            set { checkMode = value; RaisePropertyChanged(); }
        }

        /// <summary>
        /// 停止位
        /// </summary>
        public StopBit StopBit
        {
            get { return stopBit; }
            set { stopBit = value; RaisePropertyChanged(); }
        }

        /// <summary>
        /// COM
        /// </summary>
        public SerialPort SerialPort
        {
            get { return serialPort; }
            set { serialPort = value; RaisePropertyChanged(); }
        }


        #endregion

        #region 串口操作方法

        /// <summary>
        /// 開啟串口
        /// </summary>
        /// <returns></returns>
        public bool Open()
        {
            if (serialPort != null && serialPort.IsOpen)
            {
                return Close();
            }
            try
            {
                serialPort = new SerialPort();
                serialPort.DataBits = this.DataBit;
                serialPort.StopBits = ComHelper.GetStopBits(this.StopBit.ToString());
                serialPort.Parity = ComHelper.GetParity(this.CheckMode.ToString());
                serialPort.PortName = this.Port.ToString();
                serialPort.RtsEnable = true;
                serialPort.DataReceived += SerialPort_DataReceived;
                serialPort.Open();

                if (serialPort.IsOpen)
                    return IsOpen = true;
                else
                    return IsOpen = false;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            return IsOpen = false;
        }

        /// <summary>
        /// 關閉串口
        /// </summary>
        /// <returns></returns>
        public bool Close()
        {
            try
            {
                if (serialPort.IsOpen)
                {
                    serialPort.Close();
                    return IsOpen = serialPort.IsOpen;
                }
                else
                {
                    return IsOpen = serialPort.IsOpen;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return IsOpen = false;
            }
        }

        /// <summary>
        /// 發送數據
        /// </summary>
        public void Send()
        {

        }

        #endregion

        /// <summary>
        /// 返回事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            Thread.Sleep(100);
            string ReadText = serialPort.ReadExisting();
            DataReceiveInfo += ReadText + "\r\n";
        }
    }

 

4.核心MainViewModel類

作用: 關聯首頁的上下文, 通過DataContext綁定, 關聯界面元素、命令等作用。

public class MainViewModel : ViewModelBase
    {
        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            ComParameterConfig = new ComParameterConfig();
            CurrentParameter = new CurrentParameter();
        }

        private ComParameterConfig comParameter;

        /// <summary>
        /// 參數類
        /// </summary>
        public ComParameterConfig ComParameterConfig
        {
            get { return comParameter; }
            set { comParameter = value; RaisePropertyChanged(); }
        }

        private CurrentParameter currentParameter;
   
        /// <summary>
        /// 當前配置參數
        /// </summary>
        public CurrentParameter CurrentParameter
        {
            get { return currentParameter; }
            set { currentParameter = value; RaisePropertyChanged(); }
        }
        
        #region Command

        private RelayCommand _ToOpen;
        public RelayCommand ToOpen
        {
            get
            {
                if (_ToOpen == null)
                {
                    _ToOpen = new RelayCommand(Open);
                }
                return _ToOpen;
            }
            set
            {
                _ToOpen = value;
            }
        }
        /// <summary>
        /// 根據配置打開埠
        /// </summary>
        public void Open()
        {
            this.CurrentParameter.Open();
        }

        private RelayCommand _ToClick;
        public RelayCommand ToClick
        {
            get
            {
                if (_ToClick == null)
                {
                    _ToClick = new RelayCommand(Click);
                }
                return _ToClick;
            }
            set
            {
                _ToClick = value;
            }
        }

        /// <summary>
        /// 發送數據
        /// </summary>
        public void Click()
        {
            this.currentParameter.Send();
        }

        #endregion
    }

 

5.CRC校驗核心類

作用:主要實現數據校驗, 含ModbusCR標準校驗

/// <summary>
    /// CRC校驗
    /// </summary>
    public class CRC
    {

        #region  CRC16

        public static byte[] CRC16(byte[] data)
        {
            int len = data.Length;
            if (len > 0)
            {
                ushort crc = 0xFFFF;

                for (int i = 0; i < len; i++)
                {
                    crc = (ushort)(crc ^ (data[i]));
                    for (int j = 0; j < 8; j++)
                    {
                        crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1);
                    }
                }
                byte hi = (byte)((crc & 0xFF00) >> 8);  //高位置
                byte lo = (byte)(crc & 0x00FF);         //低位置

                return new byte[] { hi, lo };
            }
            return new byte[] { 0, 0 };
        }
        #endregion

        #region  ToCRC16

        public static string ToCRC16(string content)
        {
            return ToCRC16(content, Encoding.UTF8);
        }

        public static string ToCRC16(string content, bool isReverse)
        {
            return ToCRC16(content, Encoding.UTF8, isReverse);
        }

        public static string ToCRC16(string content, Encoding encoding)
        {
            return ByteToString(CRC16(encoding.GetBytes(content)), true);
        }

        public static string ToCRC16(string content, Encoding encoding, bool isReverse)
        {
            return ByteToString(CRC16(encoding.GetBytes(content)), isReverse);
        }

        public static string ToCRC16(byte[] data)
        {
            return ByteToString(CRC16(data), true);
        }

        public static string ToCRC16(byte[] data, bool isReverse)
        {
            return ByteToString(CRC16(data), isReverse);
        }
        #endregion

        #region  ToModbusCRC16

        public static string ToModbusCRC16(string s)
        {
            return ToModbusCRC16(s, true);
        }

        public static string ToModbusCRC16(string s, bool isReverse)
        {
            return ByteToString(CRC16(StringToHexByte(s)), isReverse);
        }

        public static string ToModbusCRC16(byte[] data)
        {
            return ToModbusCRC16(data, true);
        }

        public static string ToModbusCRC16(byte[] data, bool isReverse)
        {
            return ByteToString(CRC16(data), isReverse);
        }
        #endregion

        #region  ByteToString

        public static string ByteToString(byte[] arr, bool isReverse)
        {
            try
            {
                byte hi = arr[0], lo = arr[1];
                return Convert.ToString(isReverse ? hi + lo * 0x100 : hi * 0x100 + lo, 16).ToUpper().PadLeft(4, '0');
            }
            catch (Exception ex) { throw (ex); }
        }

        public static string ByteToString(byte[] arr)
        {
            try
            {
                return ByteToString(arr, true);
            }
            catch (Exception ex) { throw (ex); }
        }

        #endregion

        #region  StringToHexString

        public static string StringToHexString(string str)
        {
            StringBuilder s = new StringBuilder();
            foreach (short c in str.ToCharArray())
            {
                s.Append(c.ToString("X4"));
            }
            return s.ToString();
        }

        #endregion

        #region  StringToHexByte

        private static string ConvertChinese(string str)
        {
            StringBuilder s = new StringBuilder();
            foreach (short c in str.ToCharArray())
            {
                if (c <= 0 || c >= 127)
                {
                    s.Append(c.ToString("X4"));
                }
                else
                {
                    s.Append((char)c);
                }
            }
            return s.ToString();
        }

        private static string FilterChinese(string str)
        {
            StringBuilder s = new StringBuilder();
            foreach (short c in str.ToCharArray())
            {
                if (c > 0 && c < 127)
                {
                    s.Append((char)c);
                }
            }
            return s.ToString();
        }

        /// <summary>
        /// 字元串轉16進位字元數組
        /// </summary>
        /// <param name="hex"></param>
        /// <returns></returns>
        public static byte[] StringToHexByte(string str)
        {
            return StringToHexByte(str, false);
        }

        /// <summary>
        /// 字元串轉16進位字元數組
        /// </summary>
        /// <param name="str"></param>
        /// <param name="isFilterChinese">是否過濾掉中文字元</param>
        /// <returns></returns>
        public static byte[] StringToHexByte(string str, bool isFilterChinese)
        {
            string hex = isFilterChinese ? FilterChinese(str) : ConvertChinese(str);

            //清除所有空格
            hex = hex.Replace(" ", "");
            //若字元個數為奇數,補一個0
            hex += hex.Length % 2 != 0 ? "0" : "";

            byte[] result = new byte[hex.Length / 2];
            for (int i = 0, c = result.Length; i < c; i++)
            {
                result[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
            }
            return result;
        }
        #endregion

    }

WPF技術點:

1.自定義樣式按鈕

   <Style x:Key="CommonButtonBase" TargetType="{x:Type Button}">
                <Setter Property="BorderBrush" Value="Transparent"/>
                <Setter Property="BorderThickness" Value="0"/>
                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
                <Setter Property="HorizontalContentAlignment" Value="Center"/>
                <Setter Property="VerticalContentAlignment" Value="Center"/>
                <Setter Property="Padding" Value="1"/>
                <Setter Property="Cursor" Value="Hand"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type Button}">
                            <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" 
                                            CornerRadius="4"
                                            BorderThickness="{TemplateBinding BorderThickness}"
                                             Background="{TemplateBinding Background}"
                                             SnapsToDevicePixels="true">
                                <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                              Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" 
                                              SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsEnabled" Value="False">
                                    <Setter Property="Background" Value="#000000"/>
                                    <Setter Property="Opacity" Value="0.1"/>
                                </Trigger>
                                <Trigger Property="IsMouseOver" Value="true">
                                    <Setter Property="Foreground" Value="#FFFF00"/>
                                </Trigger>
                                <Trigger Property="IsMouseOver" Value="false">
                                    <Setter Property="Foreground" Value="White"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        
            <Style TargetType="{x:Type Button}" x:Key="Btn0093EABase" BasedOn="{StaticResource CommonButtonBase}">
                <Setter Property="Background" Value="#0093EA"/>
                <Setter Property="Foreground" Value="White"/>
                <Setter Property="FontSize" Value="22"/>
                <Setter Property="Height" Value="40"/>
                <Setter Property="Margin" Value="5"/>
            </Style>

2.轉換器用於綁定按鈕

 public class FontConverters : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value != null && bool.TryParse(value.ToString(), out bool result))
            {
                if (result)
                {
                    return "關閉串口";
                }
            }
            return "打開串口";
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    //用於綁定UI的顏色狀態顯示
public
class ColorConverters : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value != null && bool.TryParse(value.ToString(), out bool result)) { if (result) { return new SolidColorBrush((Color)System.Windows.Media.ColorConverter.ConvertFromString("#2E8B57")); } } return new SolidColorBrush((Color)System.Windows.Media.ColorConverter.ConvertFromString("#FF6347")); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }

3.引用字體

   <TextBlock Text="&#xe6e4;" Margin="20 5 0 5" FontFamily="pack://application:,,,/Font/#iconfont" 
                                   Foreground="White" FontSize="30" VerticalAlignment="Center"/>

4.綁定命令和元素

 

<TextBlock Text="端     口:"  Style="{DynamicResource TxtComStyle}"/>
                        <ComboBox  Grid.Row="0" Grid.Column="2" Style="{StaticResource ComboBoxStyle}" SelectedItem="{Binding CurrentParameter.Port}"
                                  ItemsSource="{Binding ComParameterConfig.Port}"
                                   />
                        <TextBlock Text="波 特 率:" Style="{DynamicResource TxtComStyle}"/>
                        <ComboBox  Grid.Row="0" Grid.Column="2" Style="{StaticResource ComboBoxStyle}" SelectedItem

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 0.更新源 sudo apt-get update 1.安裝依賴 sudo apt-get install curl libunwind8 gettext 2.下載 SDK 或者 RunTime https://www.microsoft.com/net/download/linux curl -s ...
  • 詳情請看《Dotnet core結合jquery的前後端加密解密密碼密文傳輸的實現》,正常來講,這個博客裡面的代碼是沒有問題的,但是我有時候卻會直接報錯,原因是後臺解密失敗:Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicExc ...
  • 深度拷貝指的是將一個引用類型(包含該類型里的引用類型)拷貝一份(在記憶體中完完全全是兩個對象,沒有任何引用關係).......... 直接上代碼: 1 /// <summary> 2 /// 對象的深度拷貝(序列化的方式) 3 /// </summary> 4 public static class ...
  • 解決方法:項目右鍵屬性 > 生成 > 找到最下麵的高級按鈕,點擊高級按鈕 > 常規 > 語言版本 > 選擇 C#最新次要版本,或者比當前版本更高的版本即可,點擊確定,然後保存即可。 ...
  • 貓頭鷹地圖下載器,支持多風格瓦片下載,開發個性化地圖利器,可以下載預設定義樣式,也可下載自定義樣式 以上圖片是常規的地圖下載,以下圖片是個性化地圖的下載 還有更多的請下載軟體體驗,軟體下載地址如下: http://api.jjszd.com:8081/apituiguang/%E7%8C%AB%E5 ...
  • 1. 2. 3 . ...
  • 由於捆綁壓縮會對所有包含的文件進行壓縮,無法設置忽略對某個js文件的壓縮。導致壓縮該js後,腳本出錯的問題。 解決方式: 重寫 ScriptBundle 的 GenerateBundleResponse 。代碼如下 用法: ...
  • 裝箱: 值類型轉為引用類型拆箱: 引用類型轉為值類型 裝箱 一般值類型存儲自棧中 轉為引用類型的時候 要在堆中 申請一個記憶體 存儲變數 拆箱 將堆中索引為0的變數 壓入到棧中 拆箱指令unbox.any 將引用類型轉為值類型 賦給值類型變數 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...