• WPF项目实战布局--通用固件下载 C#


    每个作品都是产品

    C# WPF版效果:

     C# winForm版效果:

     

    一.布局设计UI

    1.主体:grid 2行 2列

    00 下载按钮 20%      01进度条 80% (同时显示百分比)

    10 11都是跨列 显示日志

    2.细节:百分比与进度条Value绑定。下载按钮默认获得焦点回车就能点击。日志只读等。各控件Name命名等。百分比水平垂直居中等。

    (&D)快捷方式设置不了?

    我想百分比后加%,绑定时如何设置?我知道用程序设置。

    1. <Window x:Class="WpfM20UpdateFW.MainWindow"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    6. xmlns:local="clr-namespace:WpfM20UpdateFW"
    7. mc:Ignorable="d"
    8. Title="M20 Update Firmware V1.0" Height="450" Width="800" Closed="Window_Closed">
    9. <Grid>
    10. <Grid ShowGridLines="false" MinWidth="20" FocusManager.FocusedElement="{Binding ElementName=download}">
    11. <Grid.RowDefinitions>
    12. <RowDefinition Height="0.2*">RowDefinition>
    13. <RowDefinition Height="0.8*">RowDefinition>
    14. Grid.RowDefinitions>
    15. <Grid.ColumnDefinitions>
    16. <ColumnDefinition Width="0.2*">ColumnDefinition>
    17. <ColumnDefinition Width="0.8*">ColumnDefinition>
    18. Grid.ColumnDefinitions>
    19. <Button x:Name="download" Grid.Row="0" Grid.Column="0" Click="Button_Click" >Download FWButton>
    20. <ProgressBar x:Name="Progress" Grid.Row="0" Grid.Column="1" />
    21. <Label Grid.Row="0" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" Content="{Binding ElementName=Progress,Path=Value}" FontSize="48"/>
    22. <TextBox x:Name="logText" Grid.Row="1" Grid.ColumnSpan="2" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" AcceptsReturn="True" IsReadOnly="True" />
    23. Grid>
    24. Grid>
    25. Window>

    二、程序代码

    1.生成各控件的方法 

    2.协议(固件下载),与我协议一致,都通用。

    07首包 4字节固件长度+32字节固件MD5大写字符串

    08中间包 1024字节每包,分包,分成多个中间包,最后一包发剩下(不剩就不发)

    09尾包 

    协议框架:头1字节+APDU长度2字节+APDU内容+CRC16 2字节

    APDU结构:FC +功能码一字节(07,08,09)+ 00 00 00 +DATA长度2字节+DATA内容

    3.流程

    开串口,准备数据等。

    线程一  清响应 发送   等信号或超时      有信号且结果正确,继续发送。发送完成显结果关串口等。

    线程二  接收响应 解析结果 发信号         持续接收。

    4.附加功能

     首次选择文件后,后续 插拨设备自动批量下载。监听USB插拨。

    拖放文件到界面。winForm版我做了。

    5.其他数据转换等功能API

    6.细节

    退出应用,关闭线程,关闭串口。

    再次点下载,变为停止。

    日志写入文件。

    下载成功或失败 日志区变为绿色或红色。

    支持全屏。

    1. using FT_Tools;
    2. using System;
    3. using System.Diagnostics;
    4. using System.Drawing;
    5. using System.IO.Ports;
    6. using System.Management;
    7. using System.Threading;
    8. using System.Windows;
    9. using System.Windows.Media;
    10. using static FT_Tools.MySerialPort;
    11. namespace WpfM20UpdateFW
    12. {
    13. ///
    14. /// MainWindow.xaml 的交互逻辑
    15. ///
    16. public partial class MainWindow : Window
    17. {
    18. private bool responseIsOK = false;
    19. private Stopwatch sw;
    20. private bool thread_isRunning = false;
    21. private Thread thread = null;
    22. public static string templatePathName = @""; //资源号或本地文件名
    23. private EventWaitHandle _waitHandle = new AutoResetEvent(false);
    24. public MainWindow()
    25. {
    26. InitializeComponent();
    27. //插入设备
    28. //Description = USB Composite Device
    29. //DeviceID = USB\VID_2C7C & PID_0901\5 & 352FD79 & 0 & 1
    30. WqlEventQuery insertQuery = new WqlEventQuery("SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_USBHub'");
    31. ManagementEventWatcher insertWatcher = new ManagementEventWatcher(insertQuery);
    32. insertWatcher.EventArrived += (s, e) =>
    33. {
    34. //Log("M20 Arrived");
    35. var instance = (ManagementBaseObject)e.NewEvent["TargetInstance"];
    36. //var description = instance.Properties["Description"];
    37. //Log(description.Name + " = " + description.Value);
    38. var deviceId = instance.Properties["DeviceID"];
    39. //Log(deviceId.Name + " = " + deviceId.Value);
    40. if (deviceId.Value.ToString().Contains("VID_2C7C&PID_0901"))
    41. {
    42. Log("M20 Arrived");
    43. if (templatePathName.Length > 0)
    44. {
    45. thread = new Thread(new ThreadStart(ThreadDownload));
    46. thread.Start();
    47. }
    48. }
    49. };
    50. insertWatcher.Start();
    51. WqlEventQuery removeQuery = new WqlEventQuery("SELECT * FROM __InstanceDeletionEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_USBHub'");
    52. ManagementEventWatcher removeWatcher = new ManagementEventWatcher(removeQuery);
    53. removeWatcher.EventArrived += (s, e) =>
    54. {
    55. //Log("M20 Removed");
    56. var instance = (ManagementBaseObject)e.NewEvent["TargetInstance"];
    57. //var description = instance.Properties["Description"];
    58. //Log(description.Name + " = " + description.Value);
    59. var deviceId = instance.Properties["DeviceID"];
    60. //Log(deviceId.Name + " = " + deviceId.Value);
    61. if (deviceId.Value.ToString().Contains("VID_2C7C&PID_0901"))
    62. {
    63. Log("M20 Removed");
    64. }
    65. };
    66. removeWatcher.Start();
    67. }
    68. private SerialPort serialPort = new SerialPort();
    69. private void SerialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
    70. {
    71. if (serialPort.BytesToRead > 0)
    72. {
    73. byte[] buffer = new byte[serialPort.BytesToRead];
    74. int length = serialPort.Read(buffer, 0, buffer.Length);
    75. responseIsOK=reponse_Check(buffer, length);
    76. _waitHandle.Set(); // 唤醒等待
    77. }
    78. }
    79. private void Button_Click(object sender, RoutedEventArgs e)
    80. {
    81. if (thread_isRunning)
    82. {
    83. Window_Closed(null,null);
    84. logText.Text="";
    85. Log("Stop Download");//显示在最后
    86. }
    87. else
    88. {
    89. var openFileDialog = new Microsoft.Win32.OpenFileDialog()
    90. {
    91. Filter = "FW File|*.bin",
    92. Multiselect = false
    93. };
    94. var result = openFileDialog.ShowDialog();
    95. if (result==true)
    96. {
    97. templatePathName = openFileDialog.FileName;
    98. thread = new Thread(new ThreadStart(ThreadDownload));
    99. thread.Start();
    100. }
    101. }
    102. }
    103. public void ThreadDownload()
    104. {
    105. if (thread_isRunning) { Log("There are unfinished tasks, please wait..."); return; }
    106. thread_isRunning = true;
    107. sw = new Stopwatch();//计时
    108. sw.Start();
    109. bool b = ThreadDownload2();///
    110. sw.Stop();
    111. Log((b ? "Download success---------------" : "Download fail ***************") + ".Time consumption:" + sw.ElapsedMilliseconds + "ms");
    112. Log(b);
    113. if (serialPort.IsOpen) { serialPort.Close(); }
    114. thread_isRunning = false;
    115. }
    116. public bool ThreadDownload2()
    117. {
    118. Log("FW File:" + templatePathName);
    119. byte[] fBuffer = MyAPI.readFile(Log, templatePathName);
    120. if (fBuffer == null || fBuffer.Length == 0) { Log("fBuffer is empty"); return false; }
    121. string md5 = MyAPI.ComputeMD5(fBuffer);
    122. Log("fBuffer Size:" + fBuffer.Length + " md5:" + md5);
    123. string md5_hex = "";
    124. foreach (byte b2 in md5.ToUpper())
    125. {
    126. md5_hex += (string.Format("{0:X2}", b2));
    127. }
    128. int packageLength = 1024;
    129. Dispatcher.Invoke((Action)(() => {
    130. //Progress.Maximum = fBuffer.Length / packageLength + 3;
    131. //Progress.Visibility = Visibility.Visible;
    132. Progress.Value = 0;
    133. logText.Background = System.Windows.SystemColors.ControlBrush;
    134. logText.Text = ""; //清空还原
    135. }));
    136. //right response:A5 00 02 90 00 A5 D9
    137. int quantity = ((fBuffer.Length % packageLength) == 0) ? (fBuffer.Length / packageLength) : (fBuffer.Length / packageLength + 1);//发送包数量
    138. if (!SendString("FC 07 00 00 00 " + String.Format("{0:X4} ", 4 + 32) + String.Format("{0:X8} ", fBuffer.Length) + md5_hex)) { return false; } //start
    139. int len = packageLength;
    140. for (int i = 0; i < fBuffer.Length / packageLength; i++)
    141. {
    142. Dispatcher.Invoke((Action)(() => { Progress.Value = 100*i/(fBuffer.Length / packageLength+3); }));
    143. if (!SendString("FC 08 00 00 00 " + String.Format("{0:X4} ", len) + byteArrayConvertToHexStr(fBuffer, i * packageLength, len))) { return false; }
    144. }
    145. if ((fBuffer.Length % packageLength) != 0) //最后一包
    146. {
    147. len = fBuffer.Length % packageLength;
    148. if (!SendString("FC 08 00 00 00 " + String.Format("{0:X4} ", len) + byteArrayConvertToHexStr(fBuffer, fBuffer.Length - len, len))) { return false; }
    149. }
    150. if (!SendString("FC 09 00 00 00 0000")) { return false; } //end
    151. Dispatcher.Invoke((Action)(() => { Progress.Value = Progress.Maximum; }));
    152. return true;
    153. }
    154. public Boolean SendString(string strHex)
    155. {
    156. strHex = strHex.Replace(" ", "");
    157. strHex = "5A " + String.Format("{0:X4} ", strHex.Length / 2) + strHex + " ";//加上框架头1+APDU长度2+APDU+CRC16
    158. strHex += Crc16(strHex);
    159. Byte[] package = hexConvertToByteArray(strHex);
    160. Log("send:" + strHex);
    161. if (!serialPort.IsOpen)
    162. {
    163. Log("The serial port is not opened, and automatically try to open the serial port!");
    164. if (!uartOpen()) { return false; }
    165. }
    166. responseIsOK = false;
    167. _waitHandle.Reset();
    168. //Log("before send time consumption:" + sw.ElapsedMilliseconds + "ms");
    169. serialPort.Write(package, 0, package.Length);//向串口发送一包(18字节)的数据
    170. //Log("after send time consumption:" + sw.ElapsedMilliseconds + "ms");
    171. if (!_waitHandle.WaitOne(10000)) { Log("Wait timeout 10s"); return false; }// 等待通知
    172. //Log("response time consumption:" + sw.ElapsedMilliseconds+"ms");
    173. return responseIsOK;
    174. //return false;
    175. }
    176. public bool reponse_Check(byte[] buffer,int length)
    177. {
    178. if (buffer == null) { Log("buffer is null"); return false; }
    179. Log("recv(" + length + "):" + byteArrayConvertToHexStr(buffer, 0, length));
    180. if (length > 6 && buffer[0] == 0xA5 && Crc16(buffer, (ushort)(length - 2)) == (buffer[length - 2]) * 256 + buffer[length - 1])
    181. {
    182. if (buffer[length - 2 - 2] == 0x90 && buffer[length - 1 - 2] == 0x00)
    183. {
    184. //Log("Response check OK");
    185. return true;
    186. }
    187. else
    188. {
    189. Log("Response XXXXXXXXXXXXXXXXXXXXXX fail\r\n"); return false;
    190. }
    191. }
    192. else { Log("Response protocol format error XXXXXXXXXXXXXXXXXXXXXX fail\r\n"); }
    193. return false;
    194. }
    195. public string GetComName()
    196. {
    197. string[] ports = SerialPort.GetPortNames();
    198. if (ports.Length < 1)
    199. {
    200. Log("No serial port available");
    201. return null;
    202. }
    203. //getDevice();
    204. string[] strArr = GetHarewareInfo(HardwareEnum.Win32_PnPEntity, "Name");
    205. foreach (string s in strArr)
    206. {
    207. //Quectel USB Serial - 1 Port(COM7)
    208. if (s.Contains("COM"))
    209. {
    210. Log(s);
    211. if (s.Contains("Quectel USB Serial-1"))
    212. {
    213. //Log(s);
    214. string com = s.Replace("Quectel USB Serial-1 Port", "");
    215. com = com.Replace(" ", "");
    216. com = com.Replace(")", "");
    217. com = com.Replace("(", "");
    218. Log("Identified M20 serial port:" + com);
    219. return com;
    220. }
    221. }
    222. }
    223. Log("Serial port not found,TEST MODE,return " + ports[0]);
    224. return ports[0];
    225. Log("Serial port not found, please check whether the driver is installed, whether the cable is plugged in, and whether the power is on");
    226. return null;
    227. }
    228. public bool uartOpen()
    229. {
    230. try
    231. {
    232. string portName = GetComName();
    233. if (portName == null) { return false; }
    234. serialPort.PortName = portName;
    235. serialPort.BaudRate = 2000000;
    236. serialPort.DataReceived += SerialPort_DataReceived;//添加事件注册
    237. serialPort.Close(); //先关再开
    238. serialPort.Open();
    239. serialPort.DiscardOutBuffer();
    240. serialPort.DiscardInBuffer();//清空缓冲
    241. Log("Serial port has been opened " + serialPort.PortName + " " + serialPort.BaudRate);
    242. return true;
    243. }
    244. catch (Exception e)
    245. {
    246. Log("The serial port has been occupied! " + serialPort.PortName + e.ToString() + "\r\n\r\n"); //+" " + e.ToString()
    247. }
    248. return false;
    249. }
    250. private void Log(string str)
    251. {
    252. Dispatcher.BeginInvoke((Action)(() => { //异步
    253. // Dispatcher.Invoke((Action)(() => { //同步
    254. MyLog.MyLog.WriteLogs(MyLog.MyLog.fileName, "", str);
    255. if (logText.Text.Length > 5000)
    256. {
    257. logText.Text = logText.Text.Substring(logText.Text.Length - 100); //clear
    258. }
    259. logText.Text += str+"\r\n";
    260. }));
    261. }
    262. private void Log(bool b)
    263. {
    264. Dispatcher.BeginInvoke((Action)(() => { //异步
    265. // Dispatcher.Invoke((Action)(() => { //同步
    266. if (b) { logText.Background = new SolidColorBrush(Colors.Green); }
    267. else { logText.Background = new SolidColorBrush(Colors.Red); }
    268. }));
    269. }
    270. private void Window_Closed(object sender, EventArgs e)
    271. {
    272. try
    273. {
    274. if (thread != null)
    275. {
    276. thread_isRunning = false;
    277. thread.Abort(); //终止线程
    278. }
    279. if (serialPort.IsOpen)
    280. {
    281. serialPort.Close();
    282. }
    283. }
    284. catch { }
    285. }
    286. }
    287. }

  • 相关阅读:
    TPS、QPS、吞吐量,的计算公式
    【树形DP】树上拓扑序计数
    Vue学习-基础入门篇(三万字收藏篇)
    SpringSecurity系列 - 13 SpringSecurity 密码加密认证 PasswordEncoder
    域自适应——Bidirectional Learning for Domain Adaptation of Semantic Segmentation
    unity 之参数类型之引用类型
    编译OpenWrt内核驱动
    【Linux】linux | linux安装安全狗 | safedog
    Tomcat 部署 war 包
    JavaScript学习(五)——首页跳转实现
  • 原文地址:https://blog.csdn.net/chenhao0568/article/details/128027865