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

48 篇文章 1 订阅

每个作品都是产品

C# WPF版效果:

 C# winForm版效果:

 

一.布局设计UI

1.主体:grid 2行 2列

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

10 11都是跨列 显示日志

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

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

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

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


    </Grid>
</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.细节

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

再次点下载,变为停止。

日志写入文件。

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

支持全屏。

using FT_Tools;
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO.Ports;
using System.Management;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using static FT_Tools.MySerialPort;

namespace WpfM20UpdateFW
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        private bool responseIsOK = false;
        private Stopwatch sw;
        private bool thread_isRunning = false;
        private Thread thread = null;
        public static string templatePathName = @""; //资源号或本地文件名
        private EventWaitHandle _waitHandle = new AutoResetEvent(false);

        public MainWindow()
        {
            InitializeComponent();

            //插入设备
            //Description = USB Composite Device
            //DeviceID = USB\VID_2C7C & PID_0901\5 & 352FD79 & 0 & 1
            WqlEventQuery insertQuery = new WqlEventQuery("SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_USBHub'");
            ManagementEventWatcher insertWatcher = new ManagementEventWatcher(insertQuery);
            insertWatcher.EventArrived += (s, e) =>
            {                
                //Log("M20 Arrived");
                var instance = (ManagementBaseObject)e.NewEvent["TargetInstance"];
                //var description = instance.Properties["Description"];
                //Log(description.Name + " = " + description.Value);

                var deviceId = instance.Properties["DeviceID"];
                //Log(deviceId.Name + " = " + deviceId.Value);
                if (deviceId.Value.ToString().Contains("VID_2C7C&PID_0901"))
                {
                    Log("M20 Arrived");
                    if (templatePathName.Length > 0)
                    {
                        thread = new Thread(new ThreadStart(ThreadDownload));
                        thread.Start();
                    }
                }

            };
            insertWatcher.Start();

            WqlEventQuery removeQuery = new WqlEventQuery("SELECT * FROM __InstanceDeletionEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_USBHub'");
            ManagementEventWatcher removeWatcher = new ManagementEventWatcher(removeQuery);
            removeWatcher.EventArrived += (s, e) =>
            {
                //Log("M20 Removed");
                var instance = (ManagementBaseObject)e.NewEvent["TargetInstance"];
                //var description = instance.Properties["Description"];
                //Log(description.Name + " = " + description.Value);

                var deviceId = instance.Properties["DeviceID"];
                //Log(deviceId.Name + " = " + deviceId.Value);
                if (deviceId.Value.ToString().Contains("VID_2C7C&PID_0901"))
                {
                    Log("M20 Removed");
                }
            };
            removeWatcher.Start();
        
        }
        private SerialPort serialPort = new SerialPort();
        private void SerialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            if (serialPort.BytesToRead > 0)
            {
                byte[] buffer = new byte[serialPort.BytesToRead];
                int length = serialPort.Read(buffer, 0, buffer.Length);
                responseIsOK=reponse_Check(buffer, length);
                _waitHandle.Set(); // 唤醒等待
            }
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {           
            if (thread_isRunning)
            {
                Window_Closed(null,null); 
                logText.Text="";
                Log("Stop Download");//显示在最后
            }
            else
            {
                var openFileDialog = new Microsoft.Win32.OpenFileDialog()
                {
                    Filter = "FW File|*.bin",
                    Multiselect = false
                };
                var result = openFileDialog.ShowDialog();
                if (result==true)
                {
                    templatePathName = openFileDialog.FileName;
                    thread = new Thread(new ThreadStart(ThreadDownload));
                    thread.Start();
                }
            }
        }
        public void ThreadDownload()
        {
            if (thread_isRunning) { Log("There are unfinished tasks, please wait..."); return; }
            thread_isRunning = true;

            sw = new Stopwatch();//计时
            sw.Start();
            bool b = ThreadDownload2();///
            sw.Stop();
            Log((b ? "Download success---------------" : "Download fail ***************") + ".Time consumption:" + sw.ElapsedMilliseconds + "ms");
            Log(b);
            if (serialPort.IsOpen) { serialPort.Close(); }
            thread_isRunning = false;
        }
        public bool ThreadDownload2()
        {
            Log("FW File:" + templatePathName);
            byte[] fBuffer = MyAPI.readFile(Log, templatePathName);
            if (fBuffer == null || fBuffer.Length == 0) { Log("fBuffer is empty"); return false; }
            string md5 = MyAPI.ComputeMD5(fBuffer);
            Log("fBuffer Size:" + fBuffer.Length + " md5:" + md5);
            string md5_hex = "";
            foreach (byte b2 in md5.ToUpper())
            {
                md5_hex += (string.Format("{0:X2}", b2));
            }
            int packageLength = 1024;

            Dispatcher.Invoke((Action)(() => {
                //Progress.Maximum = fBuffer.Length / packageLength + 3;
                //Progress.Visibility = Visibility.Visible;
                Progress.Value = 0;
                logText.Background = System.Windows.SystemColors.ControlBrush;
                logText.Text = ""; //清空还原
            }));

            //right response:A5 00 02 90 00 A5 D9

            int quantity = ((fBuffer.Length % packageLength) == 0) ? (fBuffer.Length / packageLength) : (fBuffer.Length / packageLength + 1);//发送包数量
            if (!SendString("FC 07 00 00 00 " + String.Format("{0:X4} ", 4 + 32) + String.Format("{0:X8} ", fBuffer.Length) + md5_hex)) { return false; } //start

            int len = packageLength;
            for (int i = 0; i < fBuffer.Length / packageLength; i++)
            {
                Dispatcher.Invoke((Action)(() => { Progress.Value = 100*i/(fBuffer.Length / packageLength+3); }));
                if (!SendString("FC 08 00 00 00 " + String.Format("{0:X4} ", len) + byteArrayConvertToHexStr(fBuffer, i * packageLength, len))) { return false; }
            }
            if ((fBuffer.Length % packageLength) != 0) //最后一包
            {
                len = fBuffer.Length % packageLength;
                if (!SendString("FC 08 00 00 00 " + String.Format("{0:X4} ", len) + byteArrayConvertToHexStr(fBuffer, fBuffer.Length - len, len))) { return false; }
            }
            if (!SendString("FC 09 00 00 00 0000")) { return false; } //end 

            Dispatcher.Invoke((Action)(() => { Progress.Value = Progress.Maximum; }));
            return true;
        }
        public Boolean SendString(string strHex)
        {
            strHex = strHex.Replace(" ", "");
            strHex = "5A " + String.Format("{0:X4} ", strHex.Length / 2) + strHex + " ";//加上框架头1+APDU长度2+APDU+CRC16
            strHex += Crc16(strHex);
            Byte[] package = hexConvertToByteArray(strHex);
     
            Log("send:" + strHex); 
            if (!serialPort.IsOpen)
            {
                Log("The serial port is not opened, and automatically try to open the serial port!");
                if (!uartOpen()) { return false; }
            }
            responseIsOK = false;
            _waitHandle.Reset();
            //Log("before send time consumption:" + sw.ElapsedMilliseconds + "ms");
            serialPort.Write(package, 0, package.Length);//向串口发送一包(18字节)的数据
            //Log("after send time consumption:" + sw.ElapsedMilliseconds + "ms");
            if (!_waitHandle.WaitOne(10000)) { Log("Wait timeout 10s"); return false; }// 等待通知
            //Log("response time consumption:" + sw.ElapsedMilliseconds+"ms");
            
            return responseIsOK;
            //return false;
        }
        public bool reponse_Check(byte[] buffer,int length)
        {
            if (buffer == null) { Log("buffer is null"); return false; }
            Log("recv(" + length + "):" + byteArrayConvertToHexStr(buffer, 0, length)); 
            if (length > 6 && buffer[0] == 0xA5 && Crc16(buffer, (ushort)(length - 2)) == (buffer[length - 2]) * 256 + buffer[length - 1])
            {
                if (buffer[length - 2 - 2] == 0x90 && buffer[length - 1 - 2] == 0x00)
                {
                    //Log("Response check OK");
                    return true;
                }
                else
                {
                    Log("Response XXXXXXXXXXXXXXXXXXXXXX fail\r\n"); return false;
                }
            }
            else { Log("Response protocol format error XXXXXXXXXXXXXXXXXXXXXX fail\r\n"); }
            return false;
        }
        public string GetComName()
        {
            string[] ports = SerialPort.GetPortNames();
            if (ports.Length < 1)
            {
                Log("No serial port available");
                return null;
            } 
            //getDevice();
            string[] strArr = GetHarewareInfo(HardwareEnum.Win32_PnPEntity, "Name");
            foreach (string s in strArr)
            {
                //Quectel USB Serial - 1 Port(COM7)
                if (s.Contains("COM"))
                {
                    Log(s);              
                    if (s.Contains("Quectel USB Serial-1"))
                    {
                        //Log(s);
                        string com = s.Replace("Quectel USB Serial-1 Port", "");
                        com = com.Replace(" ", "");
                        com = com.Replace(")", "");
                        com = com.Replace("(", "");
                        Log("Identified M20 serial port:" + com);
                        return com;
                    }
                }
            }
            Log("Serial port not found,TEST MODE,return " + ports[0]);
            return ports[0];

            Log("Serial port not found, please check whether the driver is installed, whether the cable is plugged in, and whether the power is on");
            return null;
        }
        public bool uartOpen()
        {
            try
            {
                string portName = GetComName();
                if (portName == null) { return false; }
                serialPort.PortName = portName;
                serialPort.BaudRate = 2000000;
                serialPort.DataReceived += SerialPort_DataReceived;//添加事件注册

                serialPort.Close(); //先关再开                             
                serialPort.Open();
                serialPort.DiscardOutBuffer();
                serialPort.DiscardInBuffer();//清空缓冲
                Log("Serial port has been opened " + serialPort.PortName + " " + serialPort.BaudRate);
                return true;
            }
            catch (Exception e)
            {
                Log("The serial port has been occupied! " + serialPort.PortName + e.ToString() + "\r\n\r\n"); //+" " + e.ToString()
            }
            return false;

        }


        private void Log(string str)
        {
           
            Dispatcher.BeginInvoke((Action)(() => { //异步
                                                    // Dispatcher.Invoke((Action)(() => {      //同步
                MyLog.MyLog.WriteLogs(MyLog.MyLog.fileName, "", str);
                if (logText.Text.Length > 5000)
                {
                    logText.Text = logText.Text.Substring(logText.Text.Length - 100); //clear
                }
                logText.Text += str+"\r\n";
            }));
        }
        private void Log(bool b)
        {
           
            Dispatcher.BeginInvoke((Action)(() => { //异步
                                                    // Dispatcher.Invoke((Action)(() => {      //同步        
                if (b) { logText.Background = new SolidColorBrush(Colors.Green);  }
                else { logText.Background = new SolidColorBrush(Colors.Red); }
            }));
        }

        private void Window_Closed(object sender, EventArgs e)
        {
            try
            {
                if (thread != null)
                {
                    thread_isRunning = false;
                    thread.Abort(); //终止线程
                }
                if (serialPort.IsOpen)
                {
                    serialPort.Close();
                }
            }
            catch { }
        }
    }
}

WPF是一种创建Windows应用程序的框架,它提供了许多可视化组件和工具,使得应用程序的创建变得更加容易和快捷。本次实例的项目是一个简单的点餐系统,它包含了以下几个主要模块: 1. 登录界面 用户需要输入用户名和密码才能成功登录,登录成功后,程序将跳转到订单界面。如果用户输入的用户名或密码不正确,则会提示错误信息。 2. 菜单界面 在菜单界面,用户可以查看所有可供选择的菜品列表,并可以选择需要点的菜品。每个菜品都可以显示它的描述、价格以及图片,用户可以在右侧的“购物车”中查看他们已经点的菜品以及总价。 3. 订单界面 订单界面用于显示用户已经点的菜品信息以及订单的总价。用户在确认购物车中的菜品信息无误后,可以点击“去结算”按钮,进入支付界面。 4. 支付界面 在支付界面,用户需要选择支付方式并输入对应的支付信息,如信用卡号、有效期和验证码等。支付成功后,程序将返回订单界面并提示用户支付成功。 通过使用WPF的鲁棒性、可扩展性和易用性,开发者可以在编写这样的小项目时得到很大的好处。开发者可以使用大量的分层和组件化方法,使他们的代码变得易于管理和扩展。此外,通过使用WPF的数据绑定和样式,开发者可以使程序的UI更加一致和响应式。总的来说,使用WPF编写点餐系统可以使开发者轻松地创建一个功能齐全、易于使用并具有良好用户体验的应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小黄人软件

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值