Xamarin.Forms-手機串列埠除錯程式開發文件

一头倔强的驴發表於2024-11-13

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

相關文章