.NET MAUI跨平臺串口通訊方案

文章目錄

      • MAUI項目架構設計
      • 平臺特定實現
        • 接口定義
        • Windows平臺實現
        • Android平臺實現
      • MAUI主界面實現
      • 依賴注入配置
      • 相關學習資源
        • .NET MAUI開發
        • 移動端開發
        • 平臺特定實現
        • 依賴注入與架構
        • 移動應用發布
        • 跨平臺開發最佳實踐
        • 性能優化
        • 測試與調試
        • 開源項目參考

MAUI項目架構設計

MAUI App
共享業務邏輯
串口服務接口
Windows實現
Android實現
iOS實現
macOS實現
System.IO.Ports
Android USB/Serial
External Accessory
IOKit Framework

平臺特定實現

接口定義
/// <summary>
/// 跨平臺串口服務接口
/// </summary>
public interface ISerialPortService
{/// <summary>/// 獲取可用串口列表/// </summary>Task<string[]> GetAvailablePortsAsync();/// <summary>/// 連接到指定串口/// </summary>Task<bool> ConnectAsync(string portName, int baudRate);/// <summary>/// 斷開串口連接/// </summary>Task DisconnectAsync();/// <summary>/// 發送數據/// </summary>Task SendDataAsync(byte[] data);/// <summary>/// 發送文本數據/// </summary>Task SendTextAsync(string text);/// <summary>/// 數據接收事件/// </summary>event EventHandler<SerialDataEventArgs> DataReceived;/// <summary>/// 連接狀態變化事件/// </summary>event EventHandler<bool> ConnectionChanged;/// <summary>/// 是否已連接/// </summary>bool IsConnected { get; }
}/// <summary>
/// 串口數據事件參數
/// </summary>
public class SerialDataEventArgs : EventArgs
{public byte[] Data { get; set; }public string Text { get; set; }public DateTime Timestamp { get; set; }
}
Windows平臺實現
#if WINDOWS
using System.IO.Ports;/// <summary>
/// Windows平臺串口服務實現
/// </summary>
public class WindowsSerialPortService : ISerialPortService
{private SerialPort _serialPort;private bool _isConnected;public bool IsConnected => _isConnected;public event EventHandler<SerialDataEventArgs> DataReceived;public event EventHandler<bool> ConnectionChanged;public WindowsSerialPortService(){_serialPort = new SerialPort();_serialPort.DataReceived += OnDataReceived;}public async Task<string[]> GetAvailablePortsAsync(){return await Task.FromResult(SerialPort.GetPortNames());}public async Task<bool> ConnectAsync(string portName, int baudRate){try{if (_isConnected)await DisconnectAsync();_serialPort.PortName = portName;_serialPort.BaudRate = baudRate;_serialPort.DataBits = 8;_serialPort.Parity = Parity.None;_serialPort.StopBits = StopBits.One;_serialPort.Open();_isConnected = true;ConnectionChanged?.Invoke(this, true);return true;}catch (Exception ex){_isConnected = false;ConnectionChanged?.Invoke(this, false);return false;}}public async Task DisconnectAsync(){try{if (_serialPort?.IsOpen == true){_serialPort.Close();}_isConnected = false;ConnectionChanged?.Invoke(this, false);}catch (Exception){// 忽略關閉時的異常}}public async Task SendDataAsync(byte[] data){if (!_isConnected || !_serialPort.IsOpen)throw new InvalidOperationException("串口未連接");await Task.Run(() => _serialPort.Write(data, 0, data.Length));}public async Task SendTextAsync(string text){var data = System.Text.Encoding.UTF8.GetBytes(text);await SendDataAsync(data);}private void OnDataReceived(object sender, SerialDataReceivedEventArgs e){try{var buffer = new byte[_serialPort.BytesToRead];var bytesRead = _serialPort.Read(buffer, 0, buffer.Length);var eventArgs = new SerialDataEventArgs{Data = buffer.Take(bytesRead).ToArray(),Text = System.Text.Encoding.UTF8.GetString(buffer, 0, bytesRead),Timestamp = DateTime.Now};DataReceived?.Invoke(this, eventArgs);}catch (Exception){// 處理讀取異常}}
}
#endif
Android平臺實現
#if ANDROID
using Android.Hardware.Usb;
using AndroidX.Core.Content;/// <summary>
/// Android平臺串口服務實現
/// 基于USB Host模式
/// </summary>
public class AndroidSerialPortService : ISerialPortService
{private UsbManager _usbManager;private UsbDevice _usbDevice;private UsbDeviceConnection _connection;private UsbInterface _interface;private UsbEndpoint _endpointIn;private UsbEndpoint _endpointOut;private bool _isConnected;private CancellationTokenSource _readCancellation;public bool IsConnected => _isConnected;public event EventHandler<SerialDataEventArgs> DataReceived;public event EventHandler<bool> ConnectionChanged;public AndroidSerialPortService(){var context = Platform.CurrentActivity ?? Android.App.Application.Context;_usbManager = (UsbManager)context.GetSystemService(Android.Content.Context.UsbService);}public async Task<string[]> GetAvailablePortsAsync(){var deviceList = _usbManager.DeviceList;var portNames = new List<string>();foreach (var device in deviceList.Values){// 檢查是否為串口設備(根據VID/PID或設備類型判斷)if (IsSerialDevice(device)){portNames.Add($"USB-{device.DeviceName}");}}return portNames.ToArray();}public async Task<bool> ConnectAsync(string portName, int baudRate){try{// Android USB串口連接實現// 這里需要根據具體的USB轉串口芯片實現var deviceList = _usbManager.DeviceList;foreach (var device in deviceList.Values){if ($"USB-{device.DeviceName}" == portName){_usbDevice = device;break;}}if (_usbDevice == null)return false;// 請求USB權限if (!_usbManager.HasPermission(_usbDevice)){// 需要請求權限return false;}_connection = _usbManager.OpenDevice(_usbDevice);if (_connection == null)return false;// 配置USB設備_interface = _usbDevice.GetInterface(0);_connection.ClaimInterface(_interface, true);// 找到輸入輸出端點for (int i = 0; i < _interface.EndpointCount; i++){var endpoint = _interface.GetEndpoint(i);if (endpoint.Direction == UsbAddressing.In)_endpointIn = endpoint;else if (endpoint.Direction == UsbAddressing.Out)_endpointOut = endpoint;}_isConnected = true;ConnectionChanged?.Invoke(this, true);// 啟動數據讀取StartDataReading();return true;}catch (Exception){_isConnected = false;ConnectionChanged?.Invoke(this, false);return false;}}public async Task DisconnectAsync(){_isConnected = false;_readCancellation?.Cancel();_connection?.ReleaseInterface(_interface);_connection?.Close();ConnectionChanged?.Invoke(this, false);}public async Task SendDataAsync(byte[] data){if (!_isConnected || _connection == null || _endpointOut == null)throw new InvalidOperationException("設備未連接");await Task.Run(() =>{_connection.BulkTransfer(_endpointOut, data, data.Length, 1000);});}public async Task SendTextAsync(string text){var data = System.Text.Encoding.UTF8.GetBytes(text);await SendDataAsync(data);}private void StartDataReading(){_readCancellation = new CancellationTokenSource();Task.Run(async () =>{var buffer = new byte[1024];while (!_readCancellation.Token.IsCancellationRequested && _isConnected){try{var bytesRead = _connection.BulkTransfer(_endpointIn, buffer, buffer.Length, 100);if (bytesRead > 0){var eventArgs = new SerialDataEventArgs{Data = buffer.Take(bytesRead).ToArray(),Text = System.Text.Encoding.UTF8.GetString(buffer, 0, bytesRead),Timestamp = DateTime.Now};DataReceived?.Invoke(this, eventArgs);}await Task.Delay(10);}catch (Exception){// 處理讀取異常}}});}private bool IsSerialDevice(UsbDevice device){// 根據VID/PID或設備類型判斷是否為串口設備// 這里可以添加常見USB轉串口芯片的識別邏輯return device.DeviceClass == UsbClass.CdcData || device.DeviceClass == UsbClass.Comm;}
}
#endif

MAUI主界面實現

/// <summary>
/// MAUI主頁面
/// </summary>
public partial class MainPage : ContentPage
{private readonly ISerialPortService _serialService;private readonly ObservableCollection<string> _receivedMessages;public MainPage(ISerialPortService serialService){InitializeComponent();_serialService = serialService;_receivedMessages = new ObservableCollection<string>();MessagesCollectionView.ItemsSource = _receivedMessages;// 綁定事件_serialService.DataReceived += OnDataReceived;_serialService.ConnectionChanged += OnConnectionChanged;// 加載可用串口LoadAvailablePorts();}private async void LoadAvailablePorts(){try{var ports = await _serialService.GetAvailablePortsAsync();PortPicker.ItemsSource = ports;if (ports.Length > 0)PortPicker.SelectedIndex = 0;}catch (Exception ex){await DisplayAlert("錯誤", $"加載串口列表失敗: {ex.Message}", "確定");}}private async void OnConnectClicked(object sender, EventArgs e){try{if (_serialService.IsConnected){await _serialService.DisconnectAsync();}else{if (PortPicker.SelectedItem == null){await DisplayAlert("提示", "請選擇串口", "確定");return;}var portName = PortPicker.SelectedItem.ToString();var baudRate = int.Parse(BaudRatePicker.SelectedItem?.ToString() ?? "9600");var success = await _serialService.ConnectAsync(portName, baudRate);if (!success){await DisplayAlert("錯誤", "連接失敗", "確定");}}}catch (Exception ex){await DisplayAlert("錯誤", $"連接操作失敗: {ex.Message}", "確定");}}private async void OnSendClicked(object sender, EventArgs e){try{if (!_serialService.IsConnected){await DisplayAlert("提示", "請先連接串口", "確定");return;}var text = SendEntry.Text;if (string.IsNullOrWhiteSpace(text)){await DisplayAlert("提示", "請輸入要發送的內容", "確定");return;}await _serialService.SendTextAsync(text + "\r\n");SendEntry.Text = string.Empty;// 在消息列表中顯示發送的內容_receivedMessages.Add($"[發送] {DateTime.Now:HH:mm:ss} - {text}");}catch (Exception ex){await DisplayAlert("錯誤", $"發送失敗: {ex.Message}", "確定");}}private void OnDataReceived(object sender, SerialDataEventArgs e){// 在UI線程中更新界面MainThread.BeginInvokeOnMainThread(() =>{_receivedMessages.Add($"[接收] {e.Timestamp:HH:mm:ss} - {e.Text.Trim()}");// 自動滾動到最新消息if (_receivedMessages.Count > 0){MessagesCollectionView.ScrollTo(_receivedMessages.Last());}});}private void OnConnectionChanged(object sender, bool isConnected){MainThread.BeginInvokeOnMainThread(() =>{ConnectButton.Text = isConnected ? "斷開" : "連接";StatusLabel.Text = isConnected ? "已連接" : "未連接";StatusLabel.TextColor = isConnected ? Colors.Green : Colors.Red;// 控制界面元素的啟用狀態PortPicker.IsEnabled = !isConnected;BaudRatePicker.IsEnabled = !isConnected;SendButton.IsEnabled = isConnected;SendEntry.IsEnabled = isConnected;});}private async void OnRefreshPortsClicked(object sender, EventArgs e){await LoadAvailablePorts();}
}

依賴注入配置

/// <summary>
/// MAUI應用程序配置
/// </summary>
public static class MauiProgram
{public static MauiApp CreateMauiApp(){var builder = MauiApp.CreateBuilder();builder.UseMauiApp<App>().ConfigureFonts(fonts =>{fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");});// 注冊平臺特定的串口服務
#if WINDOWSbuilder.Services.AddSingleton<ISerialPortService, WindowsSerialPortService>();
#elif ANDROIDbuilder.Services.AddSingleton<ISerialPortService, AndroidSerialPortService>();
#elif IOSbuilder.Services.AddSingleton<ISerialPortService, iOSSerialPortService>();
#elif MACCATALYSTbuilder.Services.AddSingleton<ISerialPortService, MacCatalystSerialPortService>();
#endif// 注冊頁面builder.Services.AddTransient<MainPage>();return builder.Build();}
}

相關學習資源

.NET MAUI開發
  • .NET MAUI官方文檔 - 微軟官方MAUI開發指南
  • MAUI Community Toolkit - MAUI社區工具包
  • .NET MAUI Samples - 官方MAUI示例項目
  • MAUI Blazor - MAUI混合應用開發
移動端開發
  • Android開發者文檔 - Google官方Android開發指南
  • iOS開發文檔 - Apple官方iOS開發文檔
  • Xamarin.Forms指南 - Xamarin.Forms開發文檔
  • Mobile DevOps - 移動應用DevOps平臺
平臺特定實現
  • Android USB Host - Android USB主機模式
  • iOS External Accessory - iOS外部配件框架
  • Windows Runtime API - Windows Runtime API文檔
  • macOS IOKit - macOS硬件訪問框架
依賴注入與架構
  • Microsoft.Extensions.DependencyInjection - .NET依賴注入
  • MVVM Pattern - MVVM架構模式
  • CommunityToolkit.Mvvm - MVVM工具包
  • Prism Framework - 企業級MVVM框架
移動應用發布
  • Google Play Console - Android應用發布平臺
  • App Store Connect - iOS應用發布平臺
  • Microsoft Store - Windows應用商店
  • App Center Distribution - 應用分發服務
跨平臺開發最佳實踐
  • Platform Behaviors - 平臺集成最佳實踐
  • Conditional Compilation - 條件編譯指令
性能優化
  • MAUI Performance - MAUI性能優化指南
  • Memory Management - 內存管理最佳實踐
  • Battery Optimization - 電池優化策略
測試與調試
  • MAUI Unit Testing - MAUI單元測試
  • UI Testing - UI自動化測試
  • Remote Debugging - 遠程調試工具
  • App Center Analytics - 應用分析服務
開源項目參考
  • .NET Podcasts App - 微軟MAUI示例應用
  • Weather MAUI App - 天氣應用示例

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/87221.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/87221.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/87221.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

BUUCTF在線評測-練習場-WebCTF習題[MRCTF2020]你傳你[特殊字符]呢1-flag獲取、解析

解題思路 打開靶場&#xff0c;左邊是艾克&#xff0c;右邊是詩人&#xff0c;下面有個文件上傳按鈕 結合題目&#xff0c;是一個文件上傳漏洞&#xff0c;一鍵去世看源碼可知是提交按鈕&#xff0c;先上傳個一句話木馬.php試試 <?php eval($_POST[shell]); ?> 被過…

【容器】容器平臺初探 - k8s整體架構

目錄 K8s總攬 K8s主要組件 組件說明 一、Master組件 二、WokerNode組件 K8s是Kubernetes的簡稱&#xff0c;它是Google的開源容器集群管理系統&#xff0c;其提供應用部署、維護、擴展機制等功能&#xff0c;利用k8s能很方便地管理跨機器運行容器化的應用。 K8s總攬 K8s主…

C++--繼承

文章目錄 繼承1. 繼承的概念及定義1.1 繼承的概念1.2 繼承的定義1.2.1 定義格式1.2.2 繼承方式和訪問限定符1.2.3 繼承基類成員訪問方式的變化1.2.3.1 基類成員訪問方式的變化規則1.2.3.2 默認繼承方式 1.3 繼承類模版 2. 基類和派生類的轉化3. 繼承中的作用域3.1 隱藏3.2 經典…

無REPOSITORY、TAG的docker懸空鏡像究竟是什么?是否可刪除?

有時候&#xff0c;使用docker images指令我們可以發現大量的無REPOSITORY、TAG的docker鏡像&#xff0c;這些鏡像究竟是什么&#xff1f; 它們沒有REPOSITORY、TAG名稱&#xff0c;沒有辦法引用&#xff0c;那么它們還有什么用&#xff1f; [rootcdh-100 data]# docker image…

創建一個基于YOLOv8+PyQt界面的駕駛員疲勞駕駛檢測系統 實現對駕駛員疲勞狀態的打哈欠檢測,頭部下垂 疲勞眼睛檢測識別

如何使用Yolov8創建一個基于YOLOv8的駕駛員疲勞駕駛檢測系統 文章目錄 1. 數據集準備2. 安裝依賴3. 創建PyQt界面4. 模型訓練1. 數據集準備2. 模型訓練數據集配置文件 (data.yaml)訓練腳本 (train.py) 3. PyQt界面開發主程序 (MainProgram.py) 4. 運行項目5. 關鍵代碼解釋數據集…

使用FFmpeg將YUV編碼為H.264并封裝為MP4,通過api接口實現

YUV數據來源 攝像頭直接采集的原始視頻流通常為YUV格式&#xff08;如YUV420&#xff09;&#xff0c;尤其是安防攝像頭和網絡攝像頭智能手機、平板電腦的攝像頭通過硬件接口視頻會議軟件&#xff08;如Zoom、騰訊會議&#xff09;從攝像頭捕獲YUV幀&#xff0c;進行預處理&am…

tcpdump工具交叉編譯

本文默認系統已經安裝了交叉工具鏈環境。 下載相關版本源碼 涉及tcpdump源碼&#xff0c;以及tcpdump編譯過程依賴的pcap庫源碼。 網站&#xff1a;http://www.tcpdump.org/release wget http://www.tcpdump.org/release/libpcap-1.8.1.tar.gz wget http://www.tcpdump.org/r…

神經網絡中torch.nn的使用

卷積層 通過卷積核&#xff08;濾波器&#xff09;在輸入數據上滑動&#xff0c;卷積層能夠自動檢測和提取局部特征&#xff0c;如邊緣、紋理、顏色等。不同的卷積核可以捕捉不同類型的特征。 nn.conv2d() in_channels:輸入的通道數&#xff0c;彩色圖片一般為3通道 out_c…

在MATLAB中使用GPU加速計算及多GPU配置

文章目錄 在MATLAB中使用GPU加速計算及多GPU配置一、基本GPU加速使用1. 檢查GPU可用性2. 將數據傳輸到GPU3. 執行GPU計算 二、多GPU配置與使用1. 選擇特定GPU設備2. 并行計算工具箱中的多GPU支持3. 數據并行處理&#xff08;適用于深度學習&#xff09; 三、高級技巧1. 異步計算…

【unitrix】 4.12 通用2D仿射變換矩陣(matrix/types.rs)

一、源碼 這段代碼定義了一個通用的2D仿射變換矩陣結構&#xff0c;可用于表示二維空間中的各種線性變換。 /// 通用2D仿射變換矩陣&#xff08;元素僅需實現Copy trait&#xff09; /// /// 該矩陣可用于表示二維空間中的任意仿射變換&#xff0c;支持以下應用場景&#xff…

android RecyclerView隱藏整個Item后,該Item還占位留白問題

前言 android RecyclerView隱藏整個Item后,該Item還占位留白問題 思考了利用隱藏和現實來控制item 結果實現不了方案 解決方案 要依據 model 的第三個參數&#xff08;布爾值&#xff09;決定是否保留數據&#xff0c;可以通過 ?filter 高階函數結合 ?空安全操作符? 實…

地圖瓦片介紹與地圖瓦片編程下載

前沿 地圖瓦片指將一定范圍內的地圖按照一定的尺寸和格式&#xff0c;按縮放級別或者比例尺&#xff0c;切成若干行和列的正方形柵格圖片&#xff0c;對切片后的正方形柵格圖片被形象的稱為瓦片[。瓦片通常應用于B/S軟件架構下&#xff0c;瀏覽器從服務器獲取地圖數據&#xf…

手機屏亮點缺陷修復及相關液晶線路激光修復原理

摘要 手機屏亮點缺陷嚴重影響顯示品質&#xff0c;液晶線路短路、電壓異常是導致亮點的關鍵因素。激光修復技術憑借高能量密度與精準操控性&#xff0c;可有效修復液晶線路故障&#xff0c;消除亮點缺陷。本文分析亮點缺陷成因&#xff0c;深入探究液晶線路激光修復原理、工藝…

MySQL數據一鍵同步至ClickHouse數據庫

隨著數據量的爆炸式增長和業務場景的多樣化&#xff0c;傳統數據庫系統如MySQL雖然穩定可靠&#xff0c;但在海量數據分析場景下逐漸顯露出性能瓶頸。這時&#xff0c;ClickHouse憑借其列式存儲架構和卓越的OLAP&#xff08;在線分析處理&#xff09;能力脫穎而出&#xff0c;成…

Android中Compose常用組件以及布局使用方法

一、基礎控件詳解 1. Text - 文本控件 Text(text "Hello Compose", // 必填&#xff0c;顯示文本color Color.Blue, // 文字顏色fontSize 24.sp, // 字體大小&#xff08;注意使用.sp單位&#xff09;fontStyle FontStyle.Italic, // 字體樣式&…

SCI一區黑翅鳶優化算法+三模型光伏功率預測對比!BKA-CNN-GRU、CNN-GRU、GRU三模型多變量時間序列預測

SCI一區黑翅鳶優化算法三模型光伏功率預測對比&#xff01;BKA-CNN-GRU、CNN-GRU、GRU三模型多變量時間序列預測 目錄 SCI一區黑翅鳶優化算法三模型光伏功率預測對比&#xff01;BKA-CNN-GRU、CNN-GRU、GRU三模型多變量時間序列預測效果一覽基本介紹程序設計參考資料 效果一覽 …

創客匠人視角:創始人 IP 打造為何成為知識變現的核心競爭力

在互聯網流量成本高企的當下&#xff0c;知識變現行業正經歷從 “產品競爭” 到 “IP 競爭” 的范式遷移。創客匠人 CEO 老蔣指出&#xff0c;創始人 IP 已成為企業突破增長瓶頸的關鍵支點 —— 美特斯邦威創始人周成建首次直播即創下 1500 萬元成交額&#xff0c;印證了創始人…

類圖+案例+代碼詳解:軟件設計模式----生成器模式(建造者模式)

生成器模式&#xff08;建造者模式&#xff09; 把復雜對象的建造過程和表示分離&#xff0c;讓同樣的建造過程可以創建不同的表示。 假設你去快餐店買漢堡&#xff0c;漢堡由面包、肉餅、蔬菜、醬料等部分組成。 建造者模式的角色類比&#xff1a; 產品&#xff08;Product…

UI前端與數字孿生融合探索:為智慧物流提供可視化解決方案

hello寶子們...我們是艾斯視覺擅長ui設計、前端開發、數字孿生、大數據、三維建模、三維動畫10年經驗!希望我的分享能幫助到您!如需幫助可以評論關注私信我們一起探討!致敬感謝感恩! 在全球供應鏈數字化轉型的浪潮中&#xff0c;智慧物流正從概念走向落地 —— 據 MarketsandMa…

遠程辦公與協作新趨勢:從遠程桌面、VDI到邊緣計算,打造高效、安全的混合辦公環境

一、引言 隨著數字化轉型的加速&#xff0c;越來越多的企業開始采用遠程辦公和混合辦公模式&#xff0c;以提升員工的靈活性和企業的敏捷性。然而&#xff0c;異地辦公也帶來了諸如桌面環境不一致、安全風險增加、溝通協作效率降低等諸多挑戰。因此&#xff0c;如何打造一致、…