Xamarin.Forms-手机串口调试程序开发文档

Xamarin.Forms 手机串口调试程序开发文档
1.开发背景:
因工作性质特殊,需要通过手持设备与电力设备进行报文通讯,达到设备状态、地址码等数据的下发及查询功能。但因为后期手持设备厂家停产,维护不及时,造成设备稀缺,无法满足正常工作需要,特制作此手机APP,通过串口驱动连接串口转红外设备,使之发送与接收来自终端的报文信息,达到数据交互的目的,以下是软件主界面:

2.开发框架:

开发工具:VisualStudio2022,框架采用Maui框架进行开发。
3.开发所用设备及接口:
开发过程中,引用Android.UsbSerial 程序集;设备采用Type_C 转USB转换器与USB红外设备进行连接,根据设备类型在程序集中匹配合适驱动,通过报文的编码逻辑组织报文,以达到数据发送和接收的目的。
4.界面功能设计:
界面共设计两个,第一个是设备搜寻页面及安全提示内容,插入串口设备后,如果驱动合适,则会显示设备的详细信息,包括设备型号、驱动信息、生产厂家等。如果驱动不合适,则不会显示设备的详细信息等内容。
第二个页面则是数据功能页面,包括端口号、波特率、起止位、校验位的设置,以及相关的操作选项设置等。通过设置相关的参数及功能,将报文进行重组,发送报文到终端设备,终端设备根据报文的内容进行动作。
5.主要技术问题:
本软件开发过程中遇到的主要问题,还是对串口驱动的应用知识方面了解太少;第一次做Maui方面的开发,不太熟练;手机APP相关软件的开发涉及的项目太少,经验不足。
6.部分代码:
using Android.Icu.Text;
using Android.Telephony.Euicc;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Hoho.Android.UsbSerial.Driver;
using MauiUsbSerialForAndroid.Converter;
using MauiUsbSerialForAndroid.Helper;
using MauiUsbSerialForAndroid.Model;
using Microsoft.Maui.Controls.Compatibility.Platform.Android;
using System.Collections.ObjectModel;
using System.Text;
using System.Text.RegularExpressions;

namespace MauiUsbSerialForAndroid.ViewModel
{
[ObservableObject]
public partial class SerialDataViewModel : IQueryAttributable
{
[ObservableProperty]
bool isOpen = false;
public UsbDeviceInfo DeviceInfo { get; set; }
public string[] AllEncoding { get; } = new string[] { "HEX", "ASCII", "UTF-8", "GBK", "GB2312", "Unicode" };
public int[] AllBaudRate { get; } = new[] { 300, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 43000, 57600, 76800, 115200, 128000, 230400, 256000, 460800, 921600, 1382400 };
public int[] AllDataBits { get; } = new[] { 5, 6, 7, 8 };
public string[] AllParity { get; } = Enum.GetNames(typeof(Parity));
public string[] AllStopBits { get; } = Enum.GetNames(typeof(StopBits));

    [ObservableProperty]
    string encodingSend = "HEX";
    [ObservableProperty]
    string encodingReceive = "HEX";
    [ObservableProperty]
    int intervalReceive = 50;
    [ObservableProperty]
    int intervalSend = 1000;
    [ObservableProperty]
    bool cycleToSend = false;
    [ObservableProperty]
    bool showTimeStamp = true;
    [ObservableProperty]
    bool autoScroll = true;
    [ObservableProperty]
    string sendData = "";
    [ObservableProperty]
    SerialOption serialOption = new SerialOption();
    [ObservableProperty]
    string softName = "";
    // 终端地址;
    [ObservableProperty]
    string terminalAddr = "20785593";
    //终端操作;
    [ObservableProperty]
    string terminalOperation = "";
    //测量点操作;
    [ObservableProperty]
    string pointOperation = "";
    //测量点编号;
    [ObservableProperty]
    string pointIndex = "";
    //终端操作;
    [ObservableProperty]
    bool terminalChecked = true;
    //测量点操作;
    [ObservableProperty]
    bool pointChecked;
    //信息操作;
    [ObservableProperty]
    bool msgChecked;
    public ObservableCollection<SerialLog> Datas { get; } = new();
    System.Timers.Timer timerSend;
    [ObservableProperty]
    string receivedText;
    public SerialDataViewModel()
    {
        SerialPortHelper.WhenDataReceived().Subscribe(data =>
        {
            ReceivedText = SerialPortHelper.GetData(data, EncodingReceive);
            //Shell.Current.DisplayAlert("TooFast", ReceivedText, "ok");
            if (receivedText.Length > 0)
            {
                AddLog(new SerialLog($"{TerminalAddr}: {TerminalOperation} 成功", false));
            }
            else
            {
                AddLog(new SerialLog($"{TerminalAddr}: {TerminalOperation} 失败", false));
            }
            //AddLog(new SerialLog($"终端地址:{convertedText}", false));
            //AddLog(new SerialLog($"终端地址:{convertedText}", false));
        });
        timerSend = new System.Timers.Timer(intervalSend);
        timerSend.Elapsed += TimerSend_Elapsed;
        timerSend.Enabled = false;

        SerialPortHelper.WhenUsbDeviceAttached((usbDevice) =>
        {
            if (usbDevice.DeviceId == DeviceInfo.Device.DeviceId)
            {
                AddLog(new SerialLog("Usb device attached", false));
                Open();
            }
        });

        SerialPortHelper.WhenUsbDeviceDetached((usbDevice) =>
        {
            if (usbDevice.DeviceId == DeviceInfo.Device.DeviceId)
            {
                AddLog(new SerialLog("Usb device detached", false));
                Close();
            }
        });
    }
    private void TimerSend_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        Send();
    }
    partial void OnIntervalReceiveChanged(int value)
    {
        SerialPortHelper.IntervalChange(value);
    }
    async partial void OnIntervalSendChanged(int value)
    {
        if (value < 5)
        {
            await Shell.Current.DisplayAlert("TooFast", "Set at least 5 milliseconds", "ok");
            IntervalSend = 5;
        }
        timerSend.Interval = IntervalSend;
    }
    partial void OnCycleToSendChanged(bool value)
    {
        timerSend.Enabled = value;
    }
    Regex regHexRemove = new Regex("[^a-fA-F0-9 ]");
    partial void OnSendDataChanged(string value)
    {
        if (EncodingSend == "HEX")
        {
            string temp = regHexRemove.Replace(value, "");
            if (SendData != temp)
            {
                Shell.Current.DisplayAlert("Only hex", "Only HEX characters can be entered", "Ok");
                SendData = temp;
            }
        }
    }
    public void ApplyQueryAttributes(IDictionary<string, object> query)
    {
        if (query.ContainsKey("Serial"))
        {
            DeviceInfo = (UsbDeviceInfo)query["Serial"];
            Open();
        }
    }
    [RelayCommand]
    public void Toggle()
    {
        if (IsOpen)
        {
            Close();
        }
        else
        {
            Open();
        }
    }
    [RelayCommand]
    public async void Open()
    {
        if (!IsOpen)
        {
            string r = await SerialPortHelper.RequestPermissionAsync(DeviceInfo);
            if (SerialPortHelper.CheckError(r, showDialog: false))
            {
                r = SerialPortHelper.Open(DeviceInfo, SerialOption);
                if (SerialPortHelper.CheckError(r, showDialog: false))
                {
                    IsOpen = true;
                }
                else
                {
                    AddLog(new SerialLog(r, false));
                }
            }
            else
            {
                AddLog(new SerialLog(r, false));
            }
        }
    }
    [RelayCommand]
    public void Close()
    {
        try
        {
            SerialPortHelper.Close();
            CycleToSend = false;
            IsOpen = false;
        }
        catch (Exception)
        {
        }
    }
    [RelayCommand]
    public void Clear()
    {
        Datas.Clear();
    }
    /// <summary>
    /// 发送信息,先判断是何种操作;
    /// </summary>
    [RelayCommand]
    public void Send()
    {
        if (SoftName.Length == 0)
        {
            Shell.Current.DisplayAlert("Choose right softname", "请选择合适的操作软件!", "Ok");
        }
        else
        {
            if ("二层集抄".Equals(SoftName))
            {
                EcjcConverter ecjc = new EcjcConverter();
                if (terminalChecked is true) //如果是终端操作;
                {
                    if (TerminalOperation.Equals("读终端地址"))
                    {
                        SendData = ecjc.ReadAddressMethod(TerminalAddr);
                       
                    }
                    else if ("写终端地址".Equals(TerminalOperation))
                    {
                        SendData=ecjc.WriteAddressMethod(TerminalAddr);
                      
                    }
                    else if ("硬件初始化".Equals(TerminalOperation))
                    {
                        SendData = ecjc.HardwareFormatingMethod(TerminalAddr);
                     
                    }
                    else if ("数据区初始化".Equals(TerminalOperation))
                    {
                        SendData = ecjc.DataAreaFormatingMethod(TerminalAddr);
                       
                    }
                }
            }
           // 不是二层集抄
            else if ("GW376".Equals(SoftName))
            {
                Gw376Converter gw376 = new Gw376Converter();
                if (terminalChecked is true) //如果是终端操作;
                {
                    if (TerminalOperation.Equals("读终端地址"))
                    {
                       SendData=gw376.ReadAddressMethod(TerminalAddr);
                       
                    }
                    else if ("写终端地址".Equals(TerminalOperation))
                    {
                       SendData=gw376.WriteAddressMethod(this.terminalAddr);
                       
                    }
                    else if ("硬件初始化".Equals(TerminalOperation))
                    {
                        SendData = gw376.HardwareFormatingMethod(TerminalAddr);
                       
                    }
                    else if ("数据区初始化".Equals(TerminalOperation))
                    {
                        SendData=gw376.DataAreaFormatingMethod(TerminalAddr);
                    }
                }
            }
            byte[] send = SerialPortHelper.GetBytes(SendData, EncodingSend);
            if (send.Length == 0)
            {
                return;
            }
            string s = SerialPortHelper.Write(send);

            if (SerialPortHelper.CheckError(s))
            {
                if (EncodingSend == "HEX")
                {
                    //AddLog(new SerialLog(SendData.ToUpper(), true));
                    AddLog(new SerialLog($"{terminalOperation}:{TerminalAddr}", true));
                }
                else
                {
                    AddLog(new SerialLog(SendData, true));
                }
            }
            else
            {
                AddLog(new SerialLog(s, true));
            }
        }
    }
    [RelayCommand]
    public async void Back()
    {
        Close();
        await Shell.Current.GoToAsync("..");
    }
    void AddLog(SerialLog serialLog)
    {
        Datas.Add(serialLog);

        //fix VirtualView cannot be null here
        Task.Delay(50);
    }
    [RelayCommand]
    void SerialOptionChange()
    {
        SerialPortHelper.SetOption(serialOption);
    }
}

}
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MauiUsbSerialForAndroid.Helper;
using System.Collections.ObjectModel;
using Hoho.Android.UsbSerial.Driver;
using Android.Hardware.Usb;
using CommunityToolkit.Mvvm.Input;
using MauiUsbSerialForAndroid.Model;
using MauiUsbSerialForAndroid.View;
using Android.OS;
using Android.Content;
using Android.Util;

namespace MauiUsbSerialForAndroid.ViewModel
{
[ObservableObject]
public partial class SerialPortViewModel : IQueryAttributable
{

    bool openIng = false;
    public ObservableCollection<UsbDeviceInfo> UsbDevices { get; } = new();
    public SerialPortViewModel()
    {
        SerialPortHelper.WhenUsbDeviceAttached((usbDevice) =>
        {
            GetUsbDevices();
        });

        SerialPortHelper.WhenUsbDeviceDetached((usbDevice) =>
        {
            GetUsbDevices();
        });
    }
    public void ApplyQueryAttributes(IDictionary<string, object> query)
    {
        GetUsbDevices();
    }
    [RelayCommand]
    public async Task GetUsbDevices()
    {
        UsbDevices.Clear();
        var list = SerialPortHelper.GetUsbDevices();
        foreach (var item in list)
        {
            UsbDevices.Add(item);
            //fix VirtualView cannot be null here
            await Task.Delay(50);
        }
    }
    [RelayCommand]
    async Task Open(UsbDeviceInfo usbDeviceInfo)
    {
        if (openIng) { return; }
        openIng = true;
        await Shell.Current.GoToAsync(nameof(SerialDataPage), new Dictionary<string, object> {
                { "Serial",usbDeviceInfo}
            });
        openIng = false;
    }

}

}

以上代码,仅作参考,QQ:1009714648

posted @ 2024-11-13 10:09  一头倔强的驴  阅读(1)  评论(0编辑  收藏  举报