C#串口调试工具 (基于WPF史无前例/MVVM结构完整示例版)

2021-03-28 23:24

阅读:429

标签:grid   its   property   text   .com   tab   center   epo   没有   

前文

由于经常用到串口调试, 尽管有现成的软件, 因为前端时间涉及一个二次开发, 就因为一个RtsEnable设置, 折腾半天,  网上各种版本的也很多, 功能扩展的很开也多。所以现在自己做了一个够用版,基于自己的需求,简单的实现发送接收功能, 至于那些扩展功能可以自己根据需求添加。

正文

先上个运行效果图:

技术分享图片

 

项目架构

该实例用的GalaSoft.Mvvm, 该插件可以直接在NuGet中并且添加。

技术分享图片

1.串口参数 , 为了方便, 端口号并没有用动态加载的方式, 如下枚举结构:

    /// 
    /// 端口号
    /// 
    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
    }

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

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

2.串口参数配置类 , 

作用: 主要用于绑定界面的参数选项。

    /// 
    /// 串口参数设置类
    /// 
    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 Listint>() { 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200, };
            DataBit = new Listint>() { 6, 7, 8 };
        }

        private Array port;
        private Array checkMode;
        private Array stopBit;
        private Listint> dataBit;
        private Listint> baudRate;

        /// 
        /// 端口
        /// 
        public Array Port
        {
            get { return port; }
            set { port = value; RaisePropertyChanged(); }
        }

        /// 
        /// 校验模式
        /// 
        public Array CheckMode
        {
            get { return checkMode; }
            set { checkMode = value; RaisePropertyChanged(); }
        }

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

        /// 
        /// 波特率
        /// 
        public Listint> BaudRate
        {
            get { return baudRate; }
            set { baudRate = value; RaisePropertyChanged(); }
        }

        /// 
        /// 数据位
        /// 
        public Listint> DataBit
        {
            get { return dataBit; }
            set { dataBit = value; RaisePropertyChanged(); }
        }

    }

3.当前配置参数类

作用: 用于保存当前的串口参数、串口功能开关接收数据等业务。

    /// 
    /// 当前配置参数
    /// 
    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绑定参数

        /// 
        /// 接收区数据
        /// 
        public string DataReceiveInfo
        {
            get { return dataReceiveInfo; }
            set { dataReceiveInfo = value; RaisePropertyChanged(); }
        }

        /// 
        /// 发送数据
        /// 
        public string SendData
        {
            get
            {
                return sendData;
            }
            set { sendData = value; RaisePropertyChanged(); }
        }

        #endregion

        #region 串口参数信息

        /// 
        /// 开关
        /// 
        public bool IsOpen
        {
            get { return isOpen; }
            set { isOpen = value; RaisePropertyChanged(); }
        }


        /// 
        /// 数据位
        /// 
        public int DataBit
        {
            get { return dataBit; }
            set { dataBit = value; RaisePropertyChanged(); }
        }

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

        /// 
        /// 端口
        /// 
        public Port Port
        {
            get { return port; }
            set { port = value; RaisePropertyChanged(); }
        }

        /// 
        /// 校验
        /// 
        public CheckMode CheckMode
        {
            get { return checkMode; }
            set { checkMode = value; RaisePropertyChanged(); }
        }

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

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


        #endregion

        #region 串口操作方法

        /// 
        /// 开启串口
        /// 
        /// 
        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;
        }

        /// 
        /// 关闭串口
        /// 
        /// 
        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;
            }
        }

        /// 
        /// 发送数据
        /// 
        public void Send()
        {

        }

        #endregion

        /// 
        /// 返回事件
        /// 
        /// 
        /// 
        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
    {
        /// 
        /// Initializes a new instance of the MainViewModel class.
        /// 
        public MainViewModel()
        {
            ComParameterConfig = new ComParameterConfig();
            CurrentParameter = new CurrentParameter();
        }

        private ComParameterConfig comParameter;

        /// 
        /// 参数类
        /// 
        public ComParameterConfig ComParameterConfig
        {
            get { return comParameter; }
            set { comParameter = value; RaisePropertyChanged(); }
        }

        private CurrentParameter currentParameter;
   
        /// 
        /// 当前配置参数
        /// 
        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;
            }
        }
        /// 
        /// 根据配置打开端口
        /// 
        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;
            }
        }

        /// 
        /// 发送数据
        /// 
        public void Click()
        {
            this.currentParameter.Send();
        }

        #endregion
    }

 

5.CRC校验核心类

作用:主要实现数据校验, 含ModbusCR标准校验

/// 
    /// CRC校验
    /// 
    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 )
                {
                    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();
        }

        /// 
        /// 字符串转16进制字符数组
        /// 
        /// 
        /// 
        public static byte[] StringToHexByte(string str)
        {
            return StringToHexByte(str, false);
        }

        /// 
        /// 字符串转16进制字符数组
        /// 
        /// 
        /// 是否过滤掉中文字符
        /// 
        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 )
            {
                result[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
            }
            return result;
        }
        #endregion

    }

WPF技术点:

1.自定义样式按钮

   

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.引用字体

   "" Margin="20 5 0 5" FontFamily="pack://application:,,,/Font/#iconfont" 
                                   Foreground="White" FontSize="30" VerticalAlignment="Center"/>

4.绑定命令和元素

 

"端     口:"  Style="{DynamicResource TxtComStyle}"/>
                        "0" Grid.Column="2" Style="{StaticResource ComboBoxStyle}" SelectedItem="{Binding CurrentParameter.Port}"
                                  ItemsSource="{Binding ComParameterConfig.Port}"
                                   />
                        "波 特 率:" Style="{DynamicResource TxtComStyle}"/>
                        "0" Grid.Column="2" Style="{StaticResource ComboBoxStyle}" SelectedItem="{Binding CurrentParameter.BaudRdate}"
                                   ItemsSource="{Binding ComParameterConfig.BaudRate}"
                                   />
                        "数 据 位:" Style="{DynamicResource TxtComStyle}"/>
                        "0" Grid.Column="2" Style="{StaticResource ComboBoxStyle}"  SelectedItem="{Binding CurrentParameter.DataBit}"
                                   ItemsSource="{Binding ComParameterConfig.DataBit}"
                                   />
                        "校 验 位:" Style="{DynamicResource TxtComStyle}"/>
                        "0" Grid.Column="2" Style="{StaticResource ComboBoxStyle}"  SelectedItem="{Binding CurrentParameter.CheckMode}"
                                   ItemsSource="{Binding ComParameterConfig.CheckMode}"
                                   />
                        "停 止 位:" Style="{DynamicResource TxtComStyle}"/>
                        "0" Grid.Column="2" Style="{StaticResource ComboBoxStyle}"  SelectedItem="{Binding CurrentParameter.StopBit}"
                                   ItemsSource="{Binding ComParameterConfig.StopBit}"
                                   />
                        "状     态:" Style="{DynamicResource TxtComStyle}"/>
                        "" Style="{DynamicResource TxtComStyle1}"  
Foreground="{Binding CurrentParameter.IsOpen,Converter={StaticResource ColorConverters}}" />

写在最后

主项目的结构图 , 如下:

技术分享图片

 

 

如有疑问或需要, 请添加如下微信:

技术分享图片

 

C#串口调试工具 (基于WPF史无前例/MVVM结构完整示例版)

标签:grid   its   property   text   .com   tab   center   epo   没有   

原文地址:https://www.cnblogs.com/zh7791/p/9317042.html


评论


亲,登录后才可以留言!