From 669d1fd461233f8f02c966c4a2216c88a1c8423e Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Fri, 18 Oct 2024 13:49:13 +0800 Subject: [PATCH 01/35] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E4=B8=B2?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/Serial/AutoFrameBreakType.cs | 37 +++++++++ .../Services/Serial/DefaultSerialService.cs | 81 +++++++++++++++++++ .../Services/Serial/ISerialPort.cs | 41 ++++++++++ .../Services/Serial/ISerialService.cs | 22 +++++ .../Services/Serial/SerialFlowControlType.cs | 23 ++++++ .../Services/Serial/SerialOptions.cs | 74 +++++++++++++++++ .../Services/Serial/SerialParityType.cs | 31 +++++++ .../Services/Serial/SerialPort.cs | 54 +++++++++++++ .../Services/Serial/SerialSignals.cs | 53 ++++++++++++ .../Services/Serial/SerialSignalsSetting.cs | 33 ++++++++ src/BootstrapBlazor/wwwroot/modules/serial.js | 38 +++++++++ 11 files changed, 487 insertions(+) create mode 100644 src/BootstrapBlazor/Services/Serial/AutoFrameBreakType.cs create mode 100644 src/BootstrapBlazor/Services/Serial/DefaultSerialService.cs create mode 100644 src/BootstrapBlazor/Services/Serial/ISerialPort.cs create mode 100644 src/BootstrapBlazor/Services/Serial/ISerialService.cs create mode 100644 src/BootstrapBlazor/Services/Serial/SerialFlowControlType.cs create mode 100644 src/BootstrapBlazor/Services/Serial/SerialOptions.cs create mode 100644 src/BootstrapBlazor/Services/Serial/SerialParityType.cs create mode 100644 src/BootstrapBlazor/Services/Serial/SerialPort.cs create mode 100644 src/BootstrapBlazor/Services/Serial/SerialSignals.cs create mode 100644 src/BootstrapBlazor/Services/Serial/SerialSignalsSetting.cs create mode 100644 src/BootstrapBlazor/wwwroot/modules/serial.js diff --git a/src/BootstrapBlazor/Services/Serial/AutoFrameBreakType.cs b/src/BootstrapBlazor/Services/Serial/AutoFrameBreakType.cs new file mode 100644 index 00000000000..84b63e986b8 --- /dev/null +++ b/src/BootstrapBlazor/Services/Serial/AutoFrameBreakType.cs @@ -0,0 +1,37 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +namespace BootstrapBlazor.Components; + +/// +/// 自动断帧方式 +/// +public enum AutoFrameBreakType +{ + /// + /// 未启用自动断帧 + /// + None, + + /// + /// 字符断帧 + /// + Character, + + /// + /// 空闲中断 (未完成) + /// + Timeout, + + /// + /// 帧头、帧尾 (未实现) + /// 例如: 帧头(AA 、BB) + 数据长度 + 数据 + CRC校验 + 帧尾(CC、DD) + /// + FrameTail, + + /// + /// 字符间隔 (未实现) + /// + CharacterInterval, +} diff --git a/src/BootstrapBlazor/Services/Serial/DefaultSerialService.cs b/src/BootstrapBlazor/Services/Serial/DefaultSerialService.cs new file mode 100644 index 00000000000..c4aef68effe --- /dev/null +++ b/src/BootstrapBlazor/Services/Serial/DefaultSerialService.cs @@ -0,0 +1,81 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +namespace BootstrapBlazor.Components; + +class DefaultSerialService : ISerialService, IAsyncDisposable +{ + /// + /// + /// + public bool IsSupport { get; set; } + + private JSModule? _module; + + private IJSRuntime _runtime; + + private string _serialPortId; + + private SerialPort? _serialPort; + + public DefaultSerialService(IJSRuntime jsRuntime) + { + _runtime = jsRuntime; + _serialPortId = $"bb_serial_{GetHashCode()}"; + } + + private async Task LoadModule() + { + var module = await _runtime.LoadModule("./_content/BootstrapBlazor/modules/serial.js") + ?? throw new InvalidOperationException("Load serial module failed"); + + IsSupport = await module.InvokeAsync("init", _serialPortId); + return module; + } + + /// + /// get the current position of the device + /// + /// + public async Task GetPort() + { + _module ??= await LoadModule(); + + if (IsSupport) + { + var ret = await _module.InvokeAsync("getPort", _serialPortId); + if (ret) + { + _serialPort = new SerialPort(_module, _serialPortId); + } + } + return _serialPort; + } + + /// + /// DisposeAsync 方法 + /// + /// + protected virtual async ValueTask DisposeAsync(bool disposing) + { + if (disposing) + { + if (_module != null) + { + await _module.InvokeVoidAsync("dispose", _serialPortId); + await _module.DisposeAsync(); + _module = null; + } + } + } + + /// + /// + /// + public async ValueTask DisposeAsync() + { + await DisposeAsync(true); + GC.SuppressFinalize(this); + } +} diff --git a/src/BootstrapBlazor/Services/Serial/ISerialPort.cs b/src/BootstrapBlazor/Services/Serial/ISerialPort.cs new file mode 100644 index 00000000000..9bac7c77666 --- /dev/null +++ b/src/BootstrapBlazor/Services/Serial/ISerialPort.cs @@ -0,0 +1,41 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +namespace BootstrapBlazor.Components; + +/// +/// ISerialPort 接口 +/// +public interface ISerialPort +{ + /// + /// 获得 端口是否打开 + /// + bool IsOpen { get; } + + /// + /// 关闭端口方法 + /// + /// + Task Close(); + + /// + /// 打开端口方法 + /// + /// + Task Open(); + + /// + /// 读取数据方法 + /// + /// + byte[] Read(); + + /// + /// 写入数据方法 + /// + /// + /// + Task Write(byte[] data); +} diff --git a/src/BootstrapBlazor/Services/Serial/ISerialService.cs b/src/BootstrapBlazor/Services/Serial/ISerialService.cs new file mode 100644 index 00000000000..aa5fbeb72e3 --- /dev/null +++ b/src/BootstrapBlazor/Services/Serial/ISerialService.cs @@ -0,0 +1,22 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +namespace BootstrapBlazor.Components; + +/// +/// ISerialService 串口通讯服务 +/// +public interface ISerialService +{ + /// + /// 获得/设置 是否支持串口通讯 + /// + public bool IsSupport { get; } + + /// + /// 获得所有可用串口 + /// + /// + Task GetPort(); +} diff --git a/src/BootstrapBlazor/Services/Serial/SerialFlowControlType.cs b/src/BootstrapBlazor/Services/Serial/SerialFlowControlType.cs new file mode 100644 index 00000000000..4adf6988556 --- /dev/null +++ b/src/BootstrapBlazor/Services/Serial/SerialFlowControlType.cs @@ -0,0 +1,23 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using System.ComponentModel; + +namespace BootstrapBlazor.Components; + +/// +/// 流量控制方法 +/// +public enum SerialFlowControlType +{ + /// + /// 未启用流量控制 + /// + None, + + /// + /// 启用使用 RTS 和 CTS 信号的硬件流控制 + /// + Hardware, +} diff --git a/src/BootstrapBlazor/Services/Serial/SerialOptions.cs b/src/BootstrapBlazor/Services/Serial/SerialOptions.cs new file mode 100644 index 00000000000..f43f78fb4db --- /dev/null +++ b/src/BootstrapBlazor/Services/Serial/SerialOptions.cs @@ -0,0 +1,74 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using System.ComponentModel; +using System.Text.Json.Serialization; + +namespace BootstrapBlazor.Components; + +/// +/// 串口通讯参数 +/// +public class SerialOptions +{ + /// + /// 波特率 默认 9600 + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? BaudRate { get; set; } + + /// + /// 数据位 7 或 8 默认 8 + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? DataBits { get; set; } + + /// + /// 停止位 1 或 2 默认为 1 + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? StopBits { get; set; } + + /// + /// 流控制 none、even、odd 默认 "none" + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public SerialFlowControlType? ParityType { get; set; } + + /// + /// 读写缓冲区 默认 255 + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? BufferSize { get; set; } = 255; + + /// + /// 校验位 "none"或"hardware" 默认值为"none" + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public SerialParityType? FlowControlType { get; set; } + + /// + /// 自动连接设备 + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? AutoConnect { get; set; } + + /// + /// 自动断帧方式 + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public AutoFrameBreakType? AutoFrameBreakType { get; set; } + + /// + /// 断帧字符(默认\n) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? FrameBreakChar { get; set; } + + /// + /// 自动检查状态 + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool AutoGetSignals { get; set; } +} diff --git a/src/BootstrapBlazor/Services/Serial/SerialParityType.cs b/src/BootstrapBlazor/Services/Serial/SerialParityType.cs new file mode 100644 index 00000000000..5d4813f39d1 --- /dev/null +++ b/src/BootstrapBlazor/Services/Serial/SerialParityType.cs @@ -0,0 +1,31 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using System.ComponentModel; + +namespace BootstrapBlazor.Components; + +/// +/// 校验位枚举 +/// +public enum SerialParityType +{ + /// + /// 每个数据字不发送奇偶校验位 + /// + [Description("未启用")] + None, + + /// + /// 数据字加上奇偶校验位具有偶奇偶校验 + /// + [Description("偶校验")] + Even, + + /// + /// 数据字加奇偶校验位具有奇校验 + /// + [Description("奇校验")] + Odd +} diff --git a/src/BootstrapBlazor/Services/Serial/SerialPort.cs b/src/BootstrapBlazor/Services/Serial/SerialPort.cs new file mode 100644 index 00000000000..837b35a890d --- /dev/null +++ b/src/BootstrapBlazor/Services/Serial/SerialPort.cs @@ -0,0 +1,54 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +namespace BootstrapBlazor.Components; + +/// +/// 串口通讯类 +/// +class SerialPort(JSModule jsModule, string serialPortId) : ISerialPort +{ + /// + /// + /// + public bool IsOpen { get; } + + /// + /// + /// + /// + public byte[] Read() + { + + return new byte[0]; + } + + /// + /// + /// + /// + /// + public Task Write(byte[] data) + { + return Task.CompletedTask; + } + + /// + /// + /// + /// + public async Task Open() + { + await jsModule.InvokeVoidAsync("open", serialPortId); + } + + /// + /// + /// + /// + public async Task Close() + { + await jsModule.InvokeVoidAsync("close", serialPortId); + } +} diff --git a/src/BootstrapBlazor/Services/Serial/SerialSignals.cs b/src/BootstrapBlazor/Services/Serial/SerialSignals.cs new file mode 100644 index 00000000000..509e1ab6776 --- /dev/null +++ b/src/BootstrapBlazor/Services/Serial/SerialSignals.cs @@ -0,0 +1,53 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BootstrapBlazor.Components; + +//RS-232C接口定义(DB9) +//引脚 定义 符号 +//1 载波检测 DCD(Data Carrier Detect) +//2 接收数据 RXD(Received Data) +//3 发送数据 TXD(Transmit Data) +//4 数据终端准备就绪 DTR(Data Terminal Ready) +//5 信号地 SG(Signal Ground) +//6 数据准备就绪 DSR(Data Set Ready) +//7 请求发送 RTS(Request To Send) +//8 清除发送 CTS(Clear To Send) +//9 振铃提示 RI(Ring Indicator) + +/// +/// 串口信号 +/// +public class SerialSignals +{ + /// + /// 振铃提示 RI(Ring Indicator) + /// 如果 RI 为 true,则表示已检测到振铃。如果 RI 为 false,则表示未检测到振铃。Pin 9 + /// + public bool RING { get; set; } + + /// + /// 数据准备就绪 DSR(Data Set Ready) + /// 如果 DSR 为 true,则表示已准备好接收数据。如果 DSR 为 false,则表示未准备好接收数据。Pin 6 + /// + public bool DSR { get; set; } + + /// + /// 清除发送 CTS(Clear To Send) + /// 如果 CTS 为 true,则表示已准备好发送数据。如果 CTS 为 false,则表示未准备好发送数据。Pin 8 + /// + public bool CTS { get; set; } + + /// + /// 载波检测 DCD(Data Carrier Detect) + /// 如果 DCD 为 true,则表示已检测到载波。如果 DCD 为 false,则表示未检测到载波。Pin 1 + /// + public bool DCD { get; set; } +} diff --git a/src/BootstrapBlazor/Services/Serial/SerialSignalsSetting.cs b/src/BootstrapBlazor/Services/Serial/SerialSignalsSetting.cs new file mode 100644 index 00000000000..656390677ea --- /dev/null +++ b/src/BootstrapBlazor/Services/Serial/SerialSignalsSetting.cs @@ -0,0 +1,33 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using System.Text.Json.Serialization; + +namespace BootstrapBlazor.Components; + +/// +/// 串口信号设置 +/// +public class SerialSignalsSetting +{ + /// + /// 中断 + /// 如果 Break 为 true,则表示已中断。如果 Break 为 false,则表示未中断。 + /// + public bool? Break { get; set; } + + /// + /// 数据终端准备就绪 DTR(Data Terminal Ready) + /// 如果 DTR 为 true,则表示已准备好接收数据。如果 DTR 为 false,则表示未准备好接收数据。Pin 4 + /// + [JsonPropertyName("DTR")] + public bool? DTR { get; set; } + + /// + /// 请求发送 RTS(Request To Send) + /// 如果 RTS 为 true,则表示已准备好发送数据。如果 RTS 为 false,则表示未准备好发送数据。Pin 7 + /// + [JsonPropertyName("RTS")] + public bool? RTS { get; set; } +} diff --git a/src/BootstrapBlazor/wwwroot/modules/serial.js b/src/BootstrapBlazor/wwwroot/modules/serial.js new file mode 100644 index 00000000000..3d6635acca0 --- /dev/null +++ b/src/BootstrapBlazor/wwwroot/modules/serial.js @@ -0,0 +1,38 @@ +import Data from "./data.js" +import EventHandler from "./event-handler.js" + +export async function init(id) { + Data.set(id, { serialPort: null }); + return navigator.serial !== void 0; +} + +export async function getPort(id) { + try { + const port = await navigator.serial.requestPort(); + close(id); + const data = Data.get(id); + data.serialPort = port; + return true; + } + catch { + return false; + } +} + +export async function open(id) { + const data = Data.get(id); + if (data.serialPort !== null) { + console.log(`open serial port: ${id}`) + } +} + +export async function close(id) { + const data = Data.get(id); + if (data.serialPort !== null) { + console.log(`close serial port: ${id}`) + } +} + +export function dispose(id) { + +} From f9c1cd5cd0680aa503aaf90e1028d29cefd59080 Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Fri, 18 Oct 2024 13:49:24 +0800 Subject: [PATCH 02/35] =?UTF-8?q?chore:=20=E5=A2=9E=E5=8A=A0=E4=B8=B2?= =?UTF-8?q?=E5=8F=A3=E9=80=9A=E8=AE=AF=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/BootstrapBlazorServiceCollectionExtensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs b/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs index 7933660e88e..7b0645bf1a9 100644 --- a/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs +++ b/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs @@ -68,6 +68,7 @@ public static IServiceCollection AddBootstrapBlazor(this IServiceCollection serv services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); + services.TryAddScoped(); services.AddScoped(); services.AddScoped(); From dd411ebb1afa24a8a073ac0284f2b20c5a28accc Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Fri, 18 Oct 2024 13:49:33 +0800 Subject: [PATCH 03/35] =?UTF-8?q?doc:=20=E6=9B=B4=E6=96=B0=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Samples/WebSerials.razor | 47 +++++++++------- .../Components/Samples/WebSerials.razor.cs | 56 ++++++++++++++----- 2 files changed, 68 insertions(+), 35 deletions(-) diff --git a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor index f85729ea48e..1b72b6aec5c 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor @@ -7,28 +7,35 @@

@((MarkupString)Localizer["WebSerialDescription"].Value)

- + +
+
+ +
+
+ +
+
+ +
+
+ + + +
+
+
- -
-
- -
-
- + + + + + + -
-
- -
-
- -
-
-
-
-
-
- +
+
-
- +
+
-
- +
+ + + +
+
+ +
+
+ +
+
+ + + + + + + +
+
+
- - -
- @if (Flag) - { - - -
-
-
- -
-
-
-
-
-
- - - +
+ + +
+
+ - +
diff --git a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs index 628a605d7e1..4490acb4199 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs @@ -76,7 +76,11 @@ private async Task OpenPort() { _serialPort.DataReceive = async data => { - _messages.Add(new ConsoleMessageItem() { Message = $"{DateTime.Now}: -->\nText: {Encoding.ASCII.GetString(data)}\nHEX: {Convert.ToHexString(data)}" }); + _messages.Add(new ConsoleMessageItem() + { + IsHtml = true, + Message = $"{DateTime.Now}: -->
Text: {Encoding.ASCII.GetString(data)}
HEX: {Convert.ToHexString(data)}" + }); await InvokeAsync(StateHasChanged); }; await _serialPort.Open(_serialOptions); diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index 9de78b1f57d..b4a3a8a2e21 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -6312,7 +6312,11 @@ "ClosePortText": "Close", "WriteButtonText": "Write", "WriteDataText": "Send Data", - "ReadDataText": "Receive Data" + "ReadDataText": "Receive Data", + "CRLFText": "CRLF", + "HEXText": "HEX", + "LoopSendText": "Loop", + "LoopIntervalText": "Interval(ms)" }, "BootstrapBlazor.Server.Components.Samples.MindMaps": { "MindMapTitle": "Mind Map", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index a2fd5fdf10d..12da1f3f2f9 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -6312,7 +6312,11 @@ "ClosePortText": "关闭串口", "WriteButtonText": "写入", "WriteDataText": "发送数据", - "ReadDataText": "接收数据" + "ReadDataText": "接收数据", + "CRLFText": "末尾加回车换行", + "HEXText": "HEX 发送", + "LoopSendText": "循环发送", + "LoopIntervalText": "发送间隔(ms)" }, "BootstrapBlazor.Server.Components.Samples.MindMaps": { "MindMapTitle": "Mind Map 思维导图", From 528e9bc43cdc29eacb0bd6a8151f924a0b2566c9 Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Sat, 19 Oct 2024 09:18:45 +0800 Subject: [PATCH 18/35] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=20console?= =?UTF-8?q?=20=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/wwwroot/modules/serial.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/BootstrapBlazor/wwwroot/modules/serial.js b/src/BootstrapBlazor/wwwroot/modules/serial.js index 9b7129c3f9a..07feebe9d3d 100644 --- a/src/BootstrapBlazor/wwwroot/modules/serial.js +++ b/src/BootstrapBlazor/wwwroot/modules/serial.js @@ -72,7 +72,6 @@ export async function read(serial, invoke, method) { if (done) { break } - console.log([...value]); invoke.invokeMethodAsync(method, value); } } catch (error) { From c498cd60ff67cdc40ed2379a687da388f25c284d Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Sat, 19 Oct 2024 09:43:17 +0800 Subject: [PATCH 19/35] =?UTF-8?q?doc:=20=E5=AE=9E=E7=8E=B0=E5=BE=AA?= =?UTF-8?q?=E7=8E=AF=E5=8F=91=E9=80=81=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Samples/WebSerials.razor | 18 +++++- .../Components/Samples/WebSerials.razor.cs | 58 +++++++++++++++---- src/BootstrapBlazor.Server/Locales/en-US.json | 7 ++- src/BootstrapBlazor.Server/Locales/zh-CN.json | 7 ++- 4 files changed, 74 insertions(+), 16 deletions(-) diff --git a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor index 39b9d54af3f..24aca97ae94 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor @@ -7,6 +7,17 @@

@((MarkupString)Localizer["WebSerialDescription"].Value)

+
[Inject, NotNull]
+private ISerialService? SerialService { get; set; }
+ + +
    +
  • @((MarkupString)Localizer["WebSerialTipsLi1"].Value)
  • +
  • @((MarkupString)Localizer["WebSerialTipsLi2"].Value)
  • +
+
@((MarkupString)Localizer["WebSerialTipsTitle"].Value)
+
+
@@ -34,10 +45,13 @@
- + +
+
+
@Localizer["WriteDataText"]
- +
diff --git a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs index 4490acb4199..0231189a388 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs @@ -7,9 +7,9 @@ namespace BootstrapBlazor.Server.Components.Samples; /// -/// WebSerials +/// WebSerials 组件 /// -public partial class WebSerials +public partial class WebSerials : IDisposable { private string _sendData = ""; private int _sendInterval = 1000; @@ -95,6 +95,8 @@ private async Task ClosePort() } } + private CancellationTokenSource? _loopSendTokenSource; + private async Task Write() { if (_serialPort == null) @@ -108,10 +110,32 @@ private async Task Write() data += "\r\n"; } + if (_isLoop) + { + _loopSendTokenSource ??= new CancellationTokenSource(); + while (_loopSendTokenSource is { IsCancellationRequested: false } && _sendInterval > 500) + { + try + { + await InternalSend(_serialPort, data); + await Task.Delay(_sendInterval, _loopSendTokenSource.Token); + } + catch { } + } + } + else + { + await InternalSend(_serialPort, data); + } + } + + private async Task InternalSend(ISerialPort serialPort, string data) + { var buffer = _isHEX ? ConvertToHex(data) : Encoding.ASCII.GetBytes(data); - await _serialPort.Write(buffer); + await serialPort.Write(buffer); + } private static byte[] ConvertToHex(string data) @@ -129,13 +153,6 @@ private static byte[] ConvertToHex(string data) return [.. ret]; } - private Task OnReceive(string? message) - { - _message = $"{DateTime.Now:hh:mm:ss} 收到数据: {message}{Environment.NewLine}" + _message; - StateHasChanged(); - return Task.CompletedTask; - } - private Task OnSignals(WebSerialSignals? signals) { if (signals is null) return Task.CompletedTask; @@ -403,4 +420,25 @@ private static AttributeItem[] GetWebSerialOptionsAttributes() => DefaultValue ="false" } ]; + + private void Dispose(bool disposing) + { + if (disposing) + { + if (_loopSendTokenSource != null) + { + _loopSendTokenSource.Cancel(); + _loopSendTokenSource = null; + } + } + } + + /// + /// + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } } diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index b4a3a8a2e21..fef7313b7fb 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -1687,7 +1687,7 @@ "PdfReaderText": "PDF Reader", "VideoPlayerText": "VideoPlayer", "FileViewerText": "FileViewer", - "WebSerialText": "WebSerial", + "WebSerialText": "SerialService", "MindMapText": "MindMap", "WebSpeechText": "WebSpeech", "ImageCropperText": "ImageCropper", @@ -6316,7 +6316,10 @@ "CRLFText": "CRLF", "HEXText": "HEX", "LoopSendText": "Loop", - "LoopIntervalText": "Interval(ms)" + "LoopIntervalText": "Interval(ms)", + "WebSerialTipsLi1": "This feature is available only in secure contexts (HTTPS)", + "WebSerialTipsLi2": "This is an experimental technology Check the Browser compatibility table carefully before using this in production", + "WebSerialTipsTitle": "" }, "BootstrapBlazor.Server.Components.Samples.MindMaps": { "MindMapTitle": "Mind Map", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index 12da1f3f2f9..5f48a05284c 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -1687,7 +1687,7 @@ "PdfReaderText": "PDF阅读器 PDF Reader", "VideoPlayerText": "视频播放器 VideoPlayer", "FileViewerText": "文件预览器 FileViewer", - "WebSerialText": "串口读写 WebSerial", + "WebSerialText": "串口服务 ISerialService", "MindMapText": "思维导图 Mind Map", "WebSpeechText": "语音识别/合成 WebSpeech", "ImageCropperText": "图像裁剪 ImageCropper", @@ -6316,7 +6316,10 @@ "CRLFText": "末尾加回车换行", "HEXText": "HEX 发送", "LoopSendText": "循环发送", - "LoopIntervalText": "发送间隔(ms)" + "LoopIntervalText": "发送间隔(ms)", + "WebSerialTipsLi1": "该功能仅在部分或所有支持浏览器的安全上下文(HTTPS)中可用", + "WebSerialTipsLi2": "这是一项实验性技术,在生产中使用之前请仔细,检查 浏览器兼容性表", + "WebSerialTipsTitle": "" }, "BootstrapBlazor.Server.Components.Samples.MindMaps": { "MindMapTitle": "Mind Map 思维导图", From 0411996575089e9c1954e9db0a4e16508622da09 Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Sat, 19 Oct 2024 09:43:29 +0800 Subject: [PATCH 20/35] =?UTF-8?q?doc:=20=E5=88=86=E7=B1=BB=E6=9B=B4?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/MenusLocalizerExtensions.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs b/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs index 2a88f3f71f4..14ae22d6677 100644 --- a/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs +++ b/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs @@ -755,11 +755,6 @@ void AddData(DemoMenuItem item) { Text = Localizer["Waterfall"], Url = "tutorials/waterfall" - }, - new() - { - Text = Localizer["WebSerial"], - Url = "web-serial" } }; AddBadge(item); @@ -1491,6 +1486,11 @@ void AddServices(DemoMenuItem item) Url = "title" }, new() + { + Text = Localizer["WebSerial"], + Url = "web-serial" + }, + new() { Text = Localizer["ZipArchive"], Url = "zip-archive" From 59276423b55482931140ec0b023ba9bc46cde500 Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Sat, 19 Oct 2024 09:53:55 +0800 Subject: [PATCH 21/35] =?UTF-8?q?doc:=20=E5=AE=8C=E5=96=84=E5=8F=91?= =?UTF-8?q?=E9=80=81=E6=95=B0=E6=8D=AE=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Samples/WebSerials.razor.cs | 20 +++++++++---------- src/BootstrapBlazor.Server/Locales/en-US.json | 2 +- src/BootstrapBlazor.Server/Locales/zh-CN.json | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs index 0231189a388..c3270a9b5b9 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs @@ -104,20 +104,14 @@ private async Task Write() return; } - var data = _sendData; - if (_appendCRLF) - { - data += "\r\n"; - } - if (_isLoop) { _loopSendTokenSource ??= new CancellationTokenSource(); - while (_loopSendTokenSource is { IsCancellationRequested: false } && _sendInterval > 500) + while (_isLoop && _loopSendTokenSource is { IsCancellationRequested: false } && _sendInterval > 500) { try { - await InternalSend(_serialPort, data); + await InternalSend(_serialPort); await Task.Delay(_sendInterval, _loopSendTokenSource.Token); } catch { } @@ -125,17 +119,21 @@ private async Task Write() } else { - await InternalSend(_serialPort, data); + await InternalSend(_serialPort); } } - private async Task InternalSend(ISerialPort serialPort, string data) + private async Task InternalSend(ISerialPort serialPort) { + var data = _sendData; + if (_appendCRLF) + { + data += "\r\n"; + } var buffer = _isHEX ? ConvertToHex(data) : Encoding.ASCII.GetBytes(data); await serialPort.Write(buffer); - } private static byte[] ConvertToHex(string data) diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index fef7313b7fb..96fa53f9073 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -4746,7 +4746,7 @@ "CountButton": "CountButton", "Splitting": "Splitting", "QueryBuilder": "QueryBuilder", - "WebSerial": "Web Serial", + "SerialService": "ISerialService", "MindMap": "Mind Map", "Marquee": "Marquee", "Stack": "Stack", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index 5f48a05284c..e0f83f6cb8b 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -4746,7 +4746,7 @@ "CountButton": "倒计时按钮 CountButton", "Splitting": "动画组件 Splitting", "QueryBuilder": "条件生成器 QueryBuilder", - "WebSerial": "串口读写 WebSerial", + "WebSerial": "串口服务 ISerialService", "MindMap": "思维导图 MindMap", "Marquee": "文字滚动 Marquee", "Stack": "堆叠布局 Stack", From 0e34f12b3065da655dd66136941d96d8f3281dd0 Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Sat, 19 Oct 2024 10:39:25 +0800 Subject: [PATCH 22/35] =?UTF-8?q?feat:=20=E7=BB=A7=E6=89=BF=20IAsyncDispos?= =?UTF-8?q?e=20=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/Serial/ISerialPort.cs | 2 +- .../Services/Serial/SerialPort.cs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/Services/Serial/ISerialPort.cs b/src/BootstrapBlazor/Services/Serial/ISerialPort.cs index f2e795fc1ba..2a543ea2fad 100644 --- a/src/BootstrapBlazor/Services/Serial/ISerialPort.cs +++ b/src/BootstrapBlazor/Services/Serial/ISerialPort.cs @@ -7,7 +7,7 @@ namespace BootstrapBlazor.Components; /// /// ISerialPort 接口 /// -public interface ISerialPort +public interface ISerialPort : IAsyncDisposable { /// /// 获得 端口是否打开 diff --git a/src/BootstrapBlazor/Services/Serial/SerialPort.cs b/src/BootstrapBlazor/Services/Serial/SerialPort.cs index d4395b23d7e..63cd3da5f82 100644 --- a/src/BootstrapBlazor/Services/Serial/SerialPort.cs +++ b/src/BootstrapBlazor/Services/Serial/SerialPort.cs @@ -77,4 +77,22 @@ public async Task DataReceiveCallback(byte[] data) await DataReceive(data); } } + + private async ValueTask DisposeAsync(bool disposing) + { + if (disposing) + { + await jsModule.InvokeVoidAsync("dispose", serialPortId); + } + } + + /// + /// + /// + /// + public async ValueTask DisposeAsync() + { + await DisposeAsync(true); + GC.SuppressFinalize(this); + } } From a720030a8bb44d55c835f882933faf2955c111ee Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Sat, 19 Oct 2024 10:39:36 +0800 Subject: [PATCH 23/35] =?UTF-8?q?refactor:=20=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/wwwroot/modules/serial.js | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/BootstrapBlazor/wwwroot/modules/serial.js b/src/BootstrapBlazor/wwwroot/modules/serial.js index 07feebe9d3d..3e2958afbfc 100644 --- a/src/BootstrapBlazor/wwwroot/modules/serial.js +++ b/src/BootstrapBlazor/wwwroot/modules/serial.js @@ -1,5 +1,4 @@ import Data from "./data.js" -import EventHandler from "./event-handler.js" export async function init(id) { Data.set(id, { serialPort: null }); @@ -9,30 +8,31 @@ export async function init(id) { export async function getPort(id) { let ret = false; try { - const port = await navigator.serial.requestPort(); + const serialPort = await navigator.serial.requestPort(); close(id); const data = Data.get(id); - data.serialPort = port; + data.serialPort = serialPort; ret = true; } catch (err) { - console.log(err); + console.error(err); } return ret; } export async function open(id, invoke, method, options) { let ret = false; - const data = Data.get(id); - if (data.serialPort !== null) { - console.log(`open serial port: ${id}`); + const serial = Data.get(id); + const { serialPort } = serial; + if (serialPort !== null) { try { - await data.serialPort.open(options); - read(data, invoke, method); + await close(id); + await serialPort.open(options); + read(serial, invoke, method); ret = true; } catch (err) { - console.log(err); + console.error(err); } } return ret; @@ -40,19 +40,21 @@ export async function open(id, invoke, method, options) { export async function close(id) { let ret = false; - const data = Data.get(id); - const { reader, serialPort } = data; + const serial = Data.get(id); + const { reader, serialPort } = serial; if (serialPort !== null) { - console.log(`close serial port: ${id}`) try { if (reader) { await reader.cancel(); + delete serial.reader; + } + if (serialPort.readable || serialPort.writable) { + await serialPort.close(); } - await serialPort.close(); ret = true; } catch (err) { - console.log(err); + console.error(err); } } return ret; @@ -75,7 +77,7 @@ export async function read(serial, invoke, method) { invoke.invokeMethodAsync(method, value); } } catch (error) { - console.log(error); + console.error(error); } finally { serial.reader.releaseLock() } @@ -84,8 +86,8 @@ export async function read(serial, invoke, method) { export async function write(id, data) { let ret = false; - const port = Data.get(id); - const { serialPort } = port; + const serial = Data.get(id); + const { serialPort } = serial; if (serialPort && serialPort.writable) { const writer = serialPort.writable.getWriter() const payload = new Uint8Array([...data]) @@ -96,6 +98,7 @@ export async function write(id, data) { return ret; } -export function dispose(id) { - +export async function dispose(id) { + await close(id); + Data.remove(id); } From 48ccb393b548995a77a7dd21ec24027c06164a74 Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Sat, 19 Oct 2024 11:19:11 +0800 Subject: [PATCH 24/35] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=AD=A3=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E4=B8=B2=E5=8F=A3=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Services/Serial/SerialPort.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/BootstrapBlazor/Services/Serial/SerialPort.cs b/src/BootstrapBlazor/Services/Serial/SerialPort.cs index 63cd3da5f82..609091a8b3d 100644 --- a/src/BootstrapBlazor/Services/Serial/SerialPort.cs +++ b/src/BootstrapBlazor/Services/Serial/SerialPort.cs @@ -44,11 +44,7 @@ public async Task Open(SerialOptions options, CancellationToken token = default) { interop = DotNetObjectReference.Create(this); } - var ret = await jsModule.InvokeAsync("open", token, serialPortId, interop, nameof(DataReceiveCallback), options); - if (ret) - { - IsOpen = true; - } + IsOpen = await jsModule.InvokeAsync("open", token, serialPortId, interop, nameof(DataReceiveCallback), options); } /// From d9bddcb0c14873559af7ee25a233cf9d373e50ce Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Sat, 19 Oct 2024 11:19:49 +0800 Subject: [PATCH 25/35] =?UTF-8?q?refactor:=20=E7=94=B3=E8=AF=B7=E4=B8=B2?= =?UTF-8?q?=E5=8F=A3=E5=A2=9E=E5=8A=A0=E4=BF=9D=E6=8A=A4=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/wwwroot/modules/serial.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/BootstrapBlazor/wwwroot/modules/serial.js b/src/BootstrapBlazor/wwwroot/modules/serial.js index 3e2958afbfc..d30b1f9f88d 100644 --- a/src/BootstrapBlazor/wwwroot/modules/serial.js +++ b/src/BootstrapBlazor/wwwroot/modules/serial.js @@ -8,11 +8,13 @@ export async function init(id) { export async function getPort(id) { let ret = false; try { - const serialPort = await navigator.serial.requestPort(); - close(id); - const data = Data.get(id); - data.serialPort = serialPort; - ret = true; + if (navigator.serial) { + close(id); + const serialPort = await navigator.serial.requestPort(); + const data = Data.get(id); + data.serialPort = serialPort; + ret = true; + } } catch (err) { console.error(err); From 5c89f411dbf688c183d97f718758ca6205ee0dd9 Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Sat, 19 Oct 2024 11:21:45 +0800 Subject: [PATCH 26/35] =?UTF-8?q?doc:=20=E6=9B=B4=E6=96=B0=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Samples/WebSerials.razor | 51 +++- .../Components/Samples/WebSerials.razor.cs | 223 ++---------------- src/BootstrapBlazor.Server/Locales/en-US.json | 6 +- src/BootstrapBlazor.Server/Locales/zh-CN.json | 6 +- 4 files changed, 80 insertions(+), 206 deletions(-) diff --git a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor index 24aca97ae94..6156783b10e 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor @@ -71,8 +71,55 @@ private ISerialService? SerialService { get; set; } +

1. 服务注入

- +
[Inject]
+[NotNull]
+private ISerialService? SerialService { get; set; }
+ +

2. 申请串口权限

+

调用 SerialService 实例方法 GetPort 即可,通过 IsSupport 进行浏览器是否支持判断

+ +
_serialPort = await SerialService.GetPort();
+if (SerialService.IsSupport == false)
+{
+    await ToastService.Error(Localizer["NotSupportSerialTitle"], Localizer["NotSupportSerialContent"]);
+}
+ +

3. 打开串口

- +
    +
  • 如果需要读取数据,请先设置 ISerialPort 实例 DataReceive 参数
  • +
  • 调用 ISerialPort 实例方法 Open 打开串口,可通过参数设置 波特率 等信息
  • +
+
private async Task OpenPort()
+{
+    if (_serialPort != null)
+    {
+        _serialPort.DataReceive = async data =>
+        {
+            _messages.Add(new ConsoleMessageItem()
+            {
+                IsHtml = true,
+                Message = $"{DateTime.Now}: --> Text: {Encoding.ASCII.GetString(data)} HEX: {Convert.ToHexString(data)}"
+            });
+            await InvokeAsync(StateHasChanged);
+        };
+        await _serialPort.Open(_serialOptions);
+    }
+}
+ +

4. 关闭串口

+ +

调用 ISerialPort 实例方法 Close 关闭串口,请注意路由切换时,请调用其 DisposeAsync 方法释放资源

+ +
private async Task ClosePort()
+{
+    if (_serialPort != null)
+    {
+        await _serialPort.Close();
+    }
+}
+ + diff --git a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs index c3270a9b5b9..2142e27a684 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs @@ -9,19 +9,14 @@ namespace BootstrapBlazor.Server.Components.Samples; /// /// WebSerials 组件 /// -public partial class WebSerials : IDisposable +public partial class WebSerials : IAsyncDisposable { private string _sendData = ""; private int _sendInterval = 1000; private bool _appendCRLF; private bool _isHEX; private bool _isLoop; - private ConsoleMessageCollection _messages = new(8); - - private string? _message; - private string? _statusMessage; - private string? _errorMessage; - private readonly WebSerialOptions options = new() { BaudRate = 115200, AutoGetSignals = true }; + private readonly ConsoleMessageCollection _messages = new(8); private readonly List _baudRateList = [ @@ -45,21 +40,15 @@ public partial class WebSerials : IDisposable private readonly List _stopBits = [new("1", "1"), new("2", "2")]; - private bool Flag { get; set; } - - private bool IsConnected { get; set; } - - /// - /// 收到的信号数据 - /// - public WebSerialSignals Signals { get; set; } = new WebSerialSignals(); - [Inject, NotNull] private ISerialService? SerialService { get; set; } + [Inject, NotNull] + private ToastService? ToastService { get; set; } + private ISerialPort? _serialPort; - private SerialOptions _serialOptions = new(); + private readonly SerialOptions _serialOptions = new(); private bool CheckOpen => _serialPort is not { IsOpen: false }; @@ -68,6 +57,10 @@ public partial class WebSerials : IDisposable private async Task GetPort() { _serialPort = await SerialService.GetPort(); + if (SerialService.IsSupport == false) + { + await ToastService.Error(Localizer["NotSupportSerialTitle"], Localizer["NotSupportSerialContent"]); + } } private async Task OpenPort() @@ -84,6 +77,11 @@ private async Task OpenPort() await InvokeAsync(StateHasChanged); }; await _serialPort.Open(_serialOptions); + + if (_serialPort.IsOpen == false) + { + await ToastService.Error(Localizer["OpenPortSerialTitle"], Localizer["OpenPortSerialContent"]); + } } } @@ -151,60 +149,6 @@ private static byte[] ConvertToHex(string data) return [.. ret]; } - private Task OnSignals(WebSerialSignals? signals) - { - if (signals is null) return Task.CompletedTask; - - Signals = signals; - - if (!options.AutoGetSignals) - { - // 仅在不自动获取信号时才显示 - _message = $"{DateTime.Now:hh:mm:ss} 收到信号数据: {Environment.NewLine}" + - $"RING: {signals.RING}{Environment.NewLine}" + - $"DSR: {signals.DSR}{Environment.NewLine}" + - $"CTS: {signals.CTS}{Environment.NewLine}" + - $"DCD: {signals.DCD}{Environment.NewLine}" + - $"{_message}{Environment.NewLine}"; - } - - StateHasChanged(); - return Task.CompletedTask; - } - - private Task OnConnect(bool flag) - { - IsConnected = flag; - if (flag) - { - _message = null; - _statusMessage = null; - _errorMessage = null; - } - StateHasChanged(); - return Task.CompletedTask; - } - - private Task OnLog(string message) - { - _statusMessage = message; - StateHasChanged(); - return Task.CompletedTask; - } - - private Task OnError(string message) - { - _errorMessage = message; - StateHasChanged(); - return Task.CompletedTask; - } - - private void OnApply() - { - //options.BaudRate = SelectedBaudRate; - //Flag = !Flag; - } - /// /// 获得属性方法 /// @@ -292,151 +236,26 @@ private static AttributeItem[] GetAttributes() => } ]; - /// s - /// 获得WebSerialOptions属性方法 - /// - /// - private static AttributeItem[] GetWebSerialOptionsAttributes() => - [ - new() - { - Name = "BaudRate", - Description = "波特率", - Type = "int", - ValueList = "-", - DefaultValue = "9600" - }, - new() - { - Name = "DataBits", - Description = "数据位", - Type = "int", - ValueList = "7|8", - DefaultValue = "8" - }, - new() - { - Name = "StopBits", - Description = "停止位", - Type = "int", - ValueList = "1|2", - DefaultValue = "1" - }, - new() - { - Name = "ParityType", - Description = "流控制", - Type = "WebSerialFlowControlType", - ValueList = "none|even|odd", - DefaultValue = "none" - }, - new() - { - Name = "BufferSize", - Description = "读写缓冲区", - Type = "int", - ValueList = "-", - DefaultValue = "255" - }, - new() - { - Name = "FlowControlType", - Description = "校验", - Type = "WebSerialParityType", - ValueList = "none|hardware", - DefaultValue = "none" - }, - new() - { - Name =nameof(WebSerialOptions.InputWithHex), - Description = "HEX发送", - Type = "bool", - ValueList = "-", - DefaultValue = "false" - }, - new() - { - Name =nameof(WebSerialOptions.OutputInHex), - Description = "HEX接收", - Type = "bool", - ValueList = "-", - DefaultValue ="false" - }, - new() - { - Name =nameof(WebSerialOptions.AutoConnect), - Description = "自动连接设备", - Type = "bool", - ValueList = "-", - DefaultValue ="true" - }, - new() - { - Name =nameof(WebSerialOptions.AutoFrameBreakType), - Description = "自动断帧方式", - Type = "AutoFrameBreakType", - ValueList = "-", - DefaultValue ="Character" - }, - new(){ - Name =nameof(WebSerialOptions.FrameBreakChar), - Description = "断帧字符", - Type = "string", - ValueList = "-", - DefaultValue ="\\n" - }, - new() - { - Name = nameof(WebSerialOptions.ConnectBtnTitle), - Description = "获得/设置 连接按钮文本", - Type = "string", - ValueList = "", - DefaultValue = "连接" - }, - new() - { - Name = nameof(WebSerialOptions.DisconnectBtnTitle), - Description = "获得/设置 断开连接按钮文本", - Type = "string", - ValueList = "", - DefaultValue = "连接" - }, - new() - { - Name = nameof(WebSerialOptions.WriteBtnTitle), - Description = "获得/设置 写入按钮文本", - Type = "string", - ValueList = "", - DefaultValue = "写入" - }, - new() - { - Name = nameof(WebSerialOptions.AutoGetSignals), - Description = "获得/设置 自动检查状态", - Type = "bool", - ValueList = "-", - DefaultValue ="false" - } - ]; - - private void Dispose(bool disposing) + private async ValueTask DisposeAsync(bool disposing) { if (disposing) - { if (_loopSendTokenSource != null) { _loopSendTokenSource.Cancel(); _loopSendTokenSource = null; } + if (_serialPort != null) + { + await _serialPort.DisposeAsync(); } } /// /// /// - public void Dispose() + public async ValueTask DisposeAsync() { - Dispose(true); + await DisposeAsync(true); GC.SuppressFinalize(this); } } diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index 96fa53f9073..f1023d4639c 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -6319,7 +6319,11 @@ "LoopIntervalText": "Interval(ms)", "WebSerialTipsLi1": "This feature is available only in secure contexts (HTTPS)", "WebSerialTipsLi2": "This is an experimental technology Check the Browser compatibility table carefully before using this in production", - "WebSerialTipsTitle": "" + "WebSerialTipsTitle": "Note: The ISerialPort interface instance inherits IAsyncDisposable. When switching routes, you need to release its resources by calling its DisposeAsync", + "NotSupportSerialTitle": "Get Port", + "NotSupportSerialContent": "The current browser does not support serial port operations. Please change to Edge or Chrome browser.", + "OpenPortSerialTitle": "Open Port", + "OpenPortSerialContent": "Failed to open the serial port" }, "BootstrapBlazor.Server.Components.Samples.MindMaps": { "MindMapTitle": "Mind Map", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index e0f83f6cb8b..675692b2e86 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -6319,7 +6319,11 @@ "LoopIntervalText": "发送间隔(ms)", "WebSerialTipsLi1": "该功能仅在部分或所有支持浏览器的安全上下文(HTTPS)中可用", "WebSerialTipsLi2": "这是一项实验性技术,在生产中使用之前请仔细,检查 浏览器兼容性表", - "WebSerialTipsTitle": "" + "WebSerialTipsTitle": "注意:ISerialPort 接口实例继承 IAsyncDisposable 路由切换时需要对其进行资源释放,调用其 DisposeAsync 即可", + "NotSupportSerialTitle": "申请串口权限", + "NotSupportSerialContent": "当前浏览器不支持串口操作,请更换 Edge 或者 Chrome 浏览器", + "OpenPortSerialTitle": "打开串口操作", + "OpenPortSerialContent": "打开串口失败" }, "BootstrapBlazor.Server.Components.Samples.MindMaps": { "MindMapTitle": "Mind Map 思维导图", From 5060aedd1f9ad33a5682d3234df46e369bb9f3f2 Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Sat, 19 Oct 2024 11:22:46 +0800 Subject: [PATCH 27/35] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Samples/WebSerials.razor | 2 - .../Components/Samples/WebSerials.razor.cs | 87 ------------------- 2 files changed, 89 deletions(-) diff --git a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor index 6156783b10e..148673aad7d 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor @@ -121,5 +121,3 @@ if (SerialService.IsSupport == false) await _serialPort.Close(); } } - - diff --git a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs index 2142e27a684..50e4cf55916 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs @@ -149,93 +149,6 @@ private static byte[] ConvertToHex(string data) return [.. ret]; } - /// - /// 获得属性方法 - /// - /// - private static AttributeItem[] GetAttributes() => - [ - new() - { - Name = "OnReceive", - Description = "收到数据回调方法", - Type = "Func?", - ValueList = "-", - DefaultValue = "-" - }, - new AttributeItem() { - Name = "OnSignals", - Description = "收到信号数据回调方法", - Type = "Func?", - ValueList = "-", - DefaultValue = "-" - }, - new() - { - Name = "OnLog", - Description = "Log回调方法", - Type = "Func?", - ValueList = "-", - DefaultValue = "-" - }, - new() - { - Name = "OnError", - Description = "错误回调方法", - Type = "Func?", - ValueList = "-", - DefaultValue = "-" - }, - new() - { - Name = "Element", - Description = "UI界面元素的引用对象,为空则使用整个页面", - Type = "ElementReference", - ValueList = "-", - DefaultValue = "-" - }, - new() - { - Name = "ConnectBtnTitle", - Description = "获得/设置 连接按钮文本", - Type = "string", - ValueList = "", - DefaultValue = "连接" - }, - new() - { - Name = "DisconnectBtnTitle", - Description = "获得/设置 断开连接按钮文本", - Type = "string", - ValueList = "", - DefaultValue = "断开连接" - }, - new() - { - Name = "WriteBtnTitle", - Description = "获得/设置 写入按钮文本", - Type = "string", - ValueList = "", - DefaultValue = "写入" - }, - new() - { - Name = "ShowUI", - Description = "获得/设置 显示内置 UI", - Type = "bool", - ValueList = "True|False", - DefaultValue = "False" - }, - new() - { - Name = "Debug", - Description = "获得/设置 显示 log", - Type = "bool", - ValueList = "True|False", - DefaultValue = "False" - } - ]; - private async ValueTask DisposeAsync(bool disposing) { if (disposing) From b902f3697a4a6be9bb623bdd7a04d24870a01f99 Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Sat, 19 Oct 2024 11:23:38 +0800 Subject: [PATCH 28/35] =?UTF-8?q?doc:=20=E6=9B=B4=E6=96=B0=E6=96=87?= =?UTF-8?q?=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor.Server/Locales/zh-CN.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index 675692b2e86..6bcd1a0553c 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -6307,9 +6307,9 @@ "ParityTypeText": "校验位", "BufferSizeText": "缓冲区", "FlowControlTypeText": "流控制", - "RequestPortText": "选择串口", - "OpenPortText": "打开串口", - "ClosePortText": "关闭串口", + "RequestPortText": "选择", + "OpenPortText": "打开", + "ClosePortText": "关闭", "WriteButtonText": "写入", "WriteDataText": "发送数据", "ReadDataText": "接收数据", From a3fbb1ccf28e6cead67e9c107462c754ffbd3b32 Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Sat, 19 Oct 2024 11:26:10 +0800 Subject: [PATCH 29/35] =?UTF-8?q?doc:=20=E5=A2=9E=E5=8A=A0=E6=96=B0?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E6=A0=87=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/MenusLocalizerExtensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs b/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs index 14ae22d6677..ef631fb4efc 100644 --- a/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs +++ b/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs @@ -1487,6 +1487,7 @@ void AddServices(DemoMenuItem item) }, new() { + IsNew = true, Text = Localizer["WebSerial"], Url = "web-serial" }, From 410f8b87709e70b10199ffb8fea0ad624dc6a7d9 Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Sat, 19 Oct 2024 11:30:27 +0800 Subject: [PATCH 30/35] chore: bump version 8.10.4-beta03 --- src/BootstrapBlazor/BootstrapBlazor.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index af2de39c1ab..53074d70cd1 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@ - 8.10.4-beta02 + 8.10.4-beta03 From 8d788738ef925c402280f9cf4de60d9e1f17f95d Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Sat, 19 Oct 2024 12:02:44 +0800 Subject: [PATCH 31/35] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/Serial/DefaultSerialService.cs | 4 +- .../Services/Serial/SerialSignals.cs | 6 -- test/UnitTest/Services/SerialServiceTest.cs | 58 +++++++++++++++++++ 3 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 test/UnitTest/Services/SerialServiceTest.cs diff --git a/src/BootstrapBlazor/Services/Serial/DefaultSerialService.cs b/src/BootstrapBlazor/Services/Serial/DefaultSerialService.cs index c4aef68effe..0037cbd821e 100644 --- a/src/BootstrapBlazor/Services/Serial/DefaultSerialService.cs +++ b/src/BootstrapBlazor/Services/Serial/DefaultSerialService.cs @@ -13,9 +13,9 @@ class DefaultSerialService : ISerialService, IAsyncDisposable private JSModule? _module; - private IJSRuntime _runtime; + private readonly IJSRuntime _runtime; - private string _serialPortId; + private readonly string _serialPortId; private SerialPort? _serialPort; diff --git a/src/BootstrapBlazor/Services/Serial/SerialSignals.cs b/src/BootstrapBlazor/Services/Serial/SerialSignals.cs index 509e1ab6776..6333fbd259f 100644 --- a/src/BootstrapBlazor/Services/Serial/SerialSignals.cs +++ b/src/BootstrapBlazor/Services/Serial/SerialSignals.cs @@ -2,12 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Website: https://www.blazor.zone or https://argozhang.github.io/ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace BootstrapBlazor.Components; //RS-232C接口定义(DB9) diff --git a/test/UnitTest/Services/SerialServiceTest.cs b/test/UnitTest/Services/SerialServiceTest.cs new file mode 100644 index 00000000000..a9959a1c6c6 --- /dev/null +++ b/test/UnitTest/Services/SerialServiceTest.cs @@ -0,0 +1,58 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +namespace UnitTest.Services; + +public class SerialServiceTest : BootstrapBlazorTestBase +{ + [Fact] + public async Task GetPort_Ok() + { + Context.JSInterop.Setup("init", matcher => matcher.Arguments.Count == 1 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_serial_") ?? false)).SetResult(true); + Context.JSInterop.Setup("getPort", matcher => matcher.Arguments.Count == 1 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_serial_") ?? false)).SetResult(true); + Context.JSInterop.Setup("open", matcher => matcher.Arguments.Count == 4 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_serial_") ?? false)).SetResult(true); + Context.JSInterop.Setup("close", matcher => matcher.Arguments.Count == 1 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_serial_") ?? false)).SetResult(true); + Context.JSInterop.Setup("write", matcher => matcher.Arguments.Count == 2 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_serial_") ?? false)).SetResult(true); + var serialService = Context.Services.GetRequiredService(); + + var serialPort = await serialService.GetPort(); + Assert.True(serialService.IsSupport); + Assert.NotNull(serialPort); + + // DataReceive + serialPort.DataReceive = d => + { + return Task.CompletedTask; + }; + + var option = new SerialOptions() + { + BaudRate = 9600, + BufferSize = 1024, + DataBits = 8, + FlowControlType = SerialFlowControlType.Hardware, + ParityType = SerialParityType.Odd, + StopBits = 1 + }; + await serialPort.Open(option); + Assert.True(serialPort.IsOpen); + Assert.Equal(9600, option.BaudRate); + Assert.Equal(1024, option.BufferSize); + Assert.Equal(8, option.DataBits); + Assert.Equal(SerialFlowControlType.Hardware, option.FlowControlType); + Assert.Equal(SerialParityType.Odd, option.ParityType); + Assert.Equal(1, option.StopBits); + + await serialPort.Write([0x31, 0x32]); + + var mi = serialPort.GetType().GetMethod("DataReceiveCallback"); + Assert.NotNull(mi); + mi.Invoke(serialPort, ["12"u8.ToArray()]); + + await serialPort.Close(); + Assert.False(serialPort.IsOpen); + + await serialPort.DisposeAsync(); + } +} From c80b64ca58398f2d072757108e7e38b991e181ec Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Sat, 19 Oct 2024 12:57:37 +0800 Subject: [PATCH 32/35] =?UTF-8?q?refactor:=20=E9=87=8D=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/Serial/ISerialPort.cs | 24 ++++++++++++++++- .../Services/Serial/SerialPort.cs | 25 ++++++++++++++++- ...olType.cs => SerialPortFlowControlType.cs} | 2 +- ...{SerialOptions.cs => SerialPortOptions.cs} | 6 ++--- ...lParityType.cs => SerialPortParityType.cs} | 2 +- ...{SerialSignals.cs => SerialPortSignals.cs} | 10 +++++-- ...Setting.cs => SerialPortSignalsOptions.cs} | 13 ++++----- .../Services/Serial/SerialPortUsbInfo.cs | 21 +++++++++++++++ src/BootstrapBlazor/wwwroot/modules/serial.js | 27 +++++++++++++++++++ test/UnitTest/Services/SerialServiceTest.cs | 10 +++---- 10 files changed, 120 insertions(+), 20 deletions(-) rename src/BootstrapBlazor/Services/Serial/{SerialFlowControlType.cs => SerialPortFlowControlType.cs} (94%) rename src/BootstrapBlazor/Services/Serial/{SerialOptions.cs => SerialPortOptions.cs} (88%) rename src/BootstrapBlazor/Services/Serial/{SerialParityType.cs => SerialPortParityType.cs} (95%) rename src/BootstrapBlazor/Services/Serial/{SerialSignals.cs => SerialPortSignals.cs} (87%) rename src/BootstrapBlazor/Services/Serial/{SerialSignalsSetting.cs => SerialPortSignalsOptions.cs} (79%) create mode 100644 src/BootstrapBlazor/Services/Serial/SerialPortUsbInfo.cs diff --git a/src/BootstrapBlazor/Services/Serial/ISerialPort.cs b/src/BootstrapBlazor/Services/Serial/ISerialPort.cs index 2a543ea2fad..bf983fcbf88 100644 --- a/src/BootstrapBlazor/Services/Serial/ISerialPort.cs +++ b/src/BootstrapBlazor/Services/Serial/ISerialPort.cs @@ -24,7 +24,7 @@ public interface ISerialPort : IAsyncDisposable /// 打开端口方法 ///
/// - Task Open(SerialOptions options, CancellationToken token = default); + Task Open(SerialPortOptions options, CancellationToken token = default); /// /// 接收数据回调方法 @@ -37,4 +37,26 @@ public interface ISerialPort : IAsyncDisposable /// /// Task Write(byte[] data, CancellationToken token = default); + + /// + /// 获得 Usb 设备信息 + /// + /// + /// + Task GetUsbInfo(CancellationToken token = default); + + /// + /// 获得设备参数 + /// + /// + /// + Task GetSignals(CancellationToken token = default); + + /// + /// 设置设备参数 + /// + /// + /// + /// + Task SetSignals(SerialPortSignalsOptions options, CancellationToken token = default); } diff --git a/src/BootstrapBlazor/Services/Serial/SerialPort.cs b/src/BootstrapBlazor/Services/Serial/SerialPort.cs index 609091a8b3d..3ff78f84fa8 100644 --- a/src/BootstrapBlazor/Services/Serial/SerialPort.cs +++ b/src/BootstrapBlazor/Services/Serial/SerialPort.cs @@ -37,7 +37,7 @@ public async Task Write(byte[] data, CancellationToken token = default) /// ///
/// - public async Task Open(SerialOptions options, CancellationToken token = default) + public async Task Open(SerialPortOptions options, CancellationToken token = default) { DotNetObjectReference? interop = null; if (DataReceive != null) @@ -74,6 +74,29 @@ public async Task DataReceiveCallback(byte[] data) } } + /// + /// + /// + /// + /// + public async Task GetUsbInfo(CancellationToken token = default) => await jsModule.InvokeAsync("getInfo", token, serialPortId); + + /// + /// + /// + /// + /// + public async Task GetSignals(CancellationToken token = default) => await jsModule.InvokeAsync("getSignals", token, serialPortId); + + /// + /// + /// + /// + /// + /// + /// + public async Task SetSignals(SerialPortSignalsOptions options, CancellationToken token = default) => await jsModule.InvokeAsync("setSignals", token, serialPortId, options); + private async ValueTask DisposeAsync(bool disposing) { if (disposing) diff --git a/src/BootstrapBlazor/Services/Serial/SerialFlowControlType.cs b/src/BootstrapBlazor/Services/Serial/SerialPortFlowControlType.cs similarity index 94% rename from src/BootstrapBlazor/Services/Serial/SerialFlowControlType.cs rename to src/BootstrapBlazor/Services/Serial/SerialPortFlowControlType.cs index 83842677f91..d4960ce4ae6 100644 --- a/src/BootstrapBlazor/Services/Serial/SerialFlowControlType.cs +++ b/src/BootstrapBlazor/Services/Serial/SerialPortFlowControlType.cs @@ -10,7 +10,7 @@ namespace BootstrapBlazor.Components; /// 流量控制方法 /// [JsonEnumConverter(true)] -public enum SerialFlowControlType +public enum SerialPortFlowControlType { /// /// 未启用流量控制 diff --git a/src/BootstrapBlazor/Services/Serial/SerialOptions.cs b/src/BootstrapBlazor/Services/Serial/SerialPortOptions.cs similarity index 88% rename from src/BootstrapBlazor/Services/Serial/SerialOptions.cs rename to src/BootstrapBlazor/Services/Serial/SerialPortOptions.cs index 5d7e8594871..f0c67de46ff 100644 --- a/src/BootstrapBlazor/Services/Serial/SerialOptions.cs +++ b/src/BootstrapBlazor/Services/Serial/SerialPortOptions.cs @@ -9,7 +9,7 @@ namespace BootstrapBlazor.Components; /// /// 串口通讯参数 /// -public class SerialOptions +public class SerialPortOptions { /// /// 波特率 默认 9600 @@ -30,7 +30,7 @@ public class SerialOptions /// 校验位 none、even、odd 默认 "none" /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public SerialParityType ParityType { get; set; } + public SerialPortParityType ParityType { get; set; } /// /// 读写缓冲区 默认 255 @@ -41,5 +41,5 @@ public class SerialOptions /// 流控制 "none"或"hardware" 默认值为"none" /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public SerialFlowControlType FlowControlType { get; set; } + public SerialPortFlowControlType FlowControlType { get; set; } } diff --git a/src/BootstrapBlazor/Services/Serial/SerialParityType.cs b/src/BootstrapBlazor/Services/Serial/SerialPortParityType.cs similarity index 95% rename from src/BootstrapBlazor/Services/Serial/SerialParityType.cs rename to src/BootstrapBlazor/Services/Serial/SerialPortParityType.cs index 2687cd908f2..d46e5214539 100644 --- a/src/BootstrapBlazor/Services/Serial/SerialParityType.cs +++ b/src/BootstrapBlazor/Services/Serial/SerialPortParityType.cs @@ -10,7 +10,7 @@ namespace BootstrapBlazor.Components; /// 校验位枚举 /// [JsonEnumConverter(true)] -public enum SerialParityType +public enum SerialPortParityType { /// /// 每个数据字不发送奇偶校验位 diff --git a/src/BootstrapBlazor/Services/Serial/SerialSignals.cs b/src/BootstrapBlazor/Services/Serial/SerialPortSignals.cs similarity index 87% rename from src/BootstrapBlazor/Services/Serial/SerialSignals.cs rename to src/BootstrapBlazor/Services/Serial/SerialPortSignals.cs index 6333fbd259f..1fdf6e98d6c 100644 --- a/src/BootstrapBlazor/Services/Serial/SerialSignals.cs +++ b/src/BootstrapBlazor/Services/Serial/SerialPortSignals.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Website: https://www.blazor.zone or https://argozhang.github.io/ +using System.Text.Json.Serialization; + namespace BootstrapBlazor.Components; //RS-232C接口定义(DB9) @@ -19,29 +21,33 @@ namespace BootstrapBlazor.Components; /// /// 串口信号 /// -public class SerialSignals +public class SerialPortSignals { /// /// 振铃提示 RI(Ring Indicator) /// 如果 RI 为 true,则表示已检测到振铃。如果 RI 为 false,则表示未检测到振铃。Pin 9 - /// + /// + [JsonPropertyName("ringIndicator")] public bool RING { get; set; } /// /// 数据准备就绪 DSR(Data Set Ready) /// 如果 DSR 为 true,则表示已准备好接收数据。如果 DSR 为 false,则表示未准备好接收数据。Pin 6 /// + [JsonPropertyName("dataSetReady")] public bool DSR { get; set; } /// /// 清除发送 CTS(Clear To Send) /// 如果 CTS 为 true,则表示已准备好发送数据。如果 CTS 为 false,则表示未准备好发送数据。Pin 8 /// + [JsonPropertyName("clearToSend")] public bool CTS { get; set; } /// /// 载波检测 DCD(Data Carrier Detect) /// 如果 DCD 为 true,则表示已检测到载波。如果 DCD 为 false,则表示未检测到载波。Pin 1 /// + [JsonPropertyName("dataCarrierDetect")] public bool DCD { get; set; } } diff --git a/src/BootstrapBlazor/Services/Serial/SerialSignalsSetting.cs b/src/BootstrapBlazor/Services/Serial/SerialPortSignalsOptions.cs similarity index 79% rename from src/BootstrapBlazor/Services/Serial/SerialSignalsSetting.cs rename to src/BootstrapBlazor/Services/Serial/SerialPortSignalsOptions.cs index 656390677ea..6d9e3ae258a 100644 --- a/src/BootstrapBlazor/Services/Serial/SerialSignalsSetting.cs +++ b/src/BootstrapBlazor/Services/Serial/SerialPortSignalsOptions.cs @@ -9,25 +9,26 @@ namespace BootstrapBlazor.Components; /// /// 串口信号设置 /// -public class SerialSignalsSetting +public class SerialPortSignalsOptions { /// /// 中断 /// 如果 Break 为 true,则表示已中断。如果 Break 为 false,则表示未中断。 /// - public bool? Break { get; set; } + [JsonPropertyName("break")] + public bool Break { get; set; } /// /// 数据终端准备就绪 DTR(Data Terminal Ready) /// 如果 DTR 为 true,则表示已准备好接收数据。如果 DTR 为 false,则表示未准备好接收数据。Pin 4 /// - [JsonPropertyName("DTR")] - public bool? DTR { get; set; } + [JsonPropertyName("dataTerminalReady")] + public bool DTR { get; set; } /// /// 请求发送 RTS(Request To Send) /// 如果 RTS 为 true,则表示已准备好发送数据。如果 RTS 为 false,则表示未准备好发送数据。Pin 7 /// - [JsonPropertyName("RTS")] - public bool? RTS { get; set; } + [JsonPropertyName("requestToSend")] + public bool RTS { get; set; } } diff --git a/src/BootstrapBlazor/Services/Serial/SerialPortUsbInfo.cs b/src/BootstrapBlazor/Services/Serial/SerialPortUsbInfo.cs new file mode 100644 index 00000000000..779d35c5ce5 --- /dev/null +++ b/src/BootstrapBlazor/Services/Serial/SerialPortUsbInfo.cs @@ -0,0 +1,21 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +namespace BootstrapBlazor.Components; + +/// +/// Usb 串口设备信息类 +/// +public class SerialPortUsbInfo +{ + /// + /// 厂商 Id + /// + public string? UsbVendorId { get; internal set; } + + /// + /// 产品 Id + /// + public string? UsbProductId { get; internal set; } +} diff --git a/src/BootstrapBlazor/wwwroot/modules/serial.js b/src/BootstrapBlazor/wwwroot/modules/serial.js index d30b1f9f88d..5e321a3f06a 100644 --- a/src/BootstrapBlazor/wwwroot/modules/serial.js +++ b/src/BootstrapBlazor/wwwroot/modules/serial.js @@ -100,6 +100,33 @@ export async function write(id, data) { return ret; } +export async function getInfo(id) { + const serial = Data.get(id); + const { serialPort } = serial; + if (serialPort) { + const info = await serialPort.getInfo(); + console.log(info); + } +} + +export async function getSignals(id) { + const serial = Data.get(id); + const { serialPort } = serial; + if (serialPort) { + const info = await serialPort.getSignals(); + return info; + } +} + +export async function setSignals(id, options) { + const serial = Data.get(id); + const { serialPort } = serial; + if (serialPort) { + const info = await serialPort.setSignals(options); + return info; + } +} + export async function dispose(id) { await close(id); Data.remove(id); diff --git a/test/UnitTest/Services/SerialServiceTest.cs b/test/UnitTest/Services/SerialServiceTest.cs index a9959a1c6c6..d4125f08b2a 100644 --- a/test/UnitTest/Services/SerialServiceTest.cs +++ b/test/UnitTest/Services/SerialServiceTest.cs @@ -26,13 +26,13 @@ public async Task GetPort_Ok() return Task.CompletedTask; }; - var option = new SerialOptions() + var option = new SerialPortOptions() { BaudRate = 9600, BufferSize = 1024, DataBits = 8, - FlowControlType = SerialFlowControlType.Hardware, - ParityType = SerialParityType.Odd, + FlowControlType = SerialPortFlowControlType.Hardware, + ParityType = SerialPortParityType.Odd, StopBits = 1 }; await serialPort.Open(option); @@ -40,8 +40,8 @@ public async Task GetPort_Ok() Assert.Equal(9600, option.BaudRate); Assert.Equal(1024, option.BufferSize); Assert.Equal(8, option.DataBits); - Assert.Equal(SerialFlowControlType.Hardware, option.FlowControlType); - Assert.Equal(SerialParityType.Odd, option.ParityType); + Assert.Equal(SerialPortFlowControlType.Hardware, option.FlowControlType); + Assert.Equal(SerialPortParityType.Odd, option.ParityType); Assert.Equal(1, option.StopBits); await serialPort.Write([0x31, 0x32]); From 8eed2b81032a1ef54114ba90f0fb4a557d664997 Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Sat, 19 Oct 2024 13:03:15 +0800 Subject: [PATCH 33/35] =?UTF-8?q?doc:=20=E6=9B=B4=E6=96=B0=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Samples/WebSerials.razor | 22 +++++++++++- .../Components/Samples/WebSerials.razor.cs | 34 ++++++++++++++++++- src/BootstrapBlazor.Server/Locales/en-US.json | 4 ++- src/BootstrapBlazor.Server/Locales/zh-CN.json | 4 ++- 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor index 148673aad7d..7914e7e6a86 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor @@ -67,6 +67,26 @@ private ISerialService? SerialService { get; set; }
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
@@ -90,7 +110,7 @@ if (SerialService.IsSupport == false)
  • 如果需要读取数据,请先设置 ISerialPort 实例 DataReceive 参数
  • -
  • 调用 ISerialPort 实例方法 Open 打开串口,可通过参数设置 波特率 等信息
  • +
  • 调用 ISerialPort 实例方法 Open 打开串口,可通过 SerialPortOptions 参数设置 波特率 等信息
private async Task OpenPort()
diff --git a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs
index 50e4cf55916..26264bee7ed 100644
--- a/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs
+++ b/src/BootstrapBlazor.Server/Components/Samples/WebSerials.razor.cs
@@ -48,7 +48,7 @@ public partial class WebSerials : IAsyncDisposable
 
     private ISerialPort? _serialPort;
 
-    private readonly SerialOptions _serialOptions = new();
+    private readonly SerialPortOptions _serialOptions = new();
 
     private bool CheckOpen => _serialPort is not { IsOpen: false };
 
@@ -149,6 +149,38 @@ private static byte[] ConvertToHex(string data)
         return [.. ret];
     }
 
+    private SerialPortSignals? _signals;
+
+    private bool _ring => _signals?.RING ?? false;
+
+    private bool _dsr => _signals?.DSR ?? false;
+
+    private bool _dcd => _signals?.DCD ?? false;
+
+    private bool _cts => _signals?.CTS ?? false;
+
+    private async Task GetSignals()
+    {
+        if (_serialPort == null)
+        {
+            return;
+        }
+
+        _signals = await _serialPort.GetSignals();
+    }
+
+    private SerialPortUsbInfo? _usbInfo;
+
+    private async Task GetInfo()
+    {
+        if (_serialPort == null)
+        {
+            return;
+        }
+
+        _usbInfo = await _serialPort.GetUsbInfo();
+    }
+
     private async ValueTask DisposeAsync(bool disposing)
     {
         if (disposing)
diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json
index f1023d4639c..e14050c5a3f 100644
--- a/src/BootstrapBlazor.Server/Locales/en-US.json
+++ b/src/BootstrapBlazor.Server/Locales/en-US.json
@@ -6323,7 +6323,9 @@
     "NotSupportSerialTitle": "Get Port",
     "NotSupportSerialContent": "The current browser does not support serial port operations. Please change to Edge or Chrome browser.",
     "OpenPortSerialTitle": "Open Port",
-    "OpenPortSerialContent": "Failed to open the serial port"
+    "OpenPortSerialContent": "Failed to open the serial port",
+    "GetSignalsButtonText": "Signals",
+    "GetInfoButtonText": "Infos"
   },
   "BootstrapBlazor.Server.Components.Samples.MindMaps": {
     "MindMapTitle": "Mind Map",
diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json
index 6bcd1a0553c..95c943a0580 100644
--- a/src/BootstrapBlazor.Server/Locales/zh-CN.json
+++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json
@@ -6323,7 +6323,9 @@
     "NotSupportSerialTitle": "申请串口权限",
     "NotSupportSerialContent": "当前浏览器不支持串口操作,请更换 Edge 或者 Chrome 浏览器",
     "OpenPortSerialTitle": "打开串口操作",
-    "OpenPortSerialContent": "打开串口失败"
+    "OpenPortSerialContent": "打开串口失败",
+    "GetSignalsButtonText": "串口参数",
+    "GetInfoButtonText": "USB 信息"
   },
   "BootstrapBlazor.Server.Components.Samples.MindMaps": {
     "MindMapTitle": "Mind Map 思维导图",

From d4654b0e37357657f3374246ca8931b1cc61bad0 Mon Sep 17 00:00:00 2001
From: Argo-AsicoTech 
Date: Sat, 19 Oct 2024 13:09:01 +0800
Subject: [PATCH 34/35] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?=
 =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 test/UnitTest/Services/SerialServiceTest.cs | 36 +++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/test/UnitTest/Services/SerialServiceTest.cs b/test/UnitTest/Services/SerialServiceTest.cs
index d4125f08b2a..25c89bd83e5 100644
--- a/test/UnitTest/Services/SerialServiceTest.cs
+++ b/test/UnitTest/Services/SerialServiceTest.cs
@@ -14,6 +14,15 @@ public async Task GetPort_Ok()
         Context.JSInterop.Setup("open", matcher => matcher.Arguments.Count == 4 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_serial_") ?? false)).SetResult(true);
         Context.JSInterop.Setup("close", matcher => matcher.Arguments.Count == 1 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_serial_") ?? false)).SetResult(true);
         Context.JSInterop.Setup("write", matcher => matcher.Arguments.Count == 2 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_serial_") ?? false)).SetResult(true);
+        Context.JSInterop.Setup("getInfo", matcher => matcher.Arguments.Count == 1 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_serial_") ?? false)).SetResult(new SerialPortUsbInfo());
+        Context.JSInterop.Setup("getSignals", matcher => matcher.Arguments.Count == 1 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_serial_") ?? false)).SetResult(new SerialPortSignals()
+        {
+            RING = false,
+            CTS = true,
+            DCD = true,
+            DSR = true
+        });
+        Context.JSInterop.Setup("setSignals", matcher => matcher.Arguments.Count == 2 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_serial_") ?? false)).SetResult(true);
         var serialService = Context.Services.GetRequiredService();
 
         var serialPort = await serialService.GetPort();
@@ -50,6 +59,33 @@ public async Task GetPort_Ok()
         Assert.NotNull(mi);
         mi.Invoke(serialPort, ["12"u8.ToArray()]);
 
+        // getInfo
+        var info = await serialPort.GetUsbInfo();
+        Assert.NotNull(info);
+        Assert.Null(info.UsbVendorId);
+        Assert.Null(info.UsbProductId);
+
+        // getSignals
+        var signals = await serialPort.GetSignals();
+        Assert.NotNull(signals);
+        Assert.False(signals.RING);
+        Assert.True(signals.DSR);
+        Assert.True(signals.CTS);
+        Assert.True(signals.CTS);
+
+        // setSignals
+        var signalOption = new SerialPortSignalsOptions()
+        {
+            Break = true,
+            DTR = true,
+            RTS = true
+        };
+        var ret = await serialPort.SetSignals(signalOption);
+        Assert.True(ret);
+        Assert.True(signalOption.Break);
+        Assert.True(signalOption.DTR);
+        Assert.True(signalOption.RTS);
+
         await serialPort.Close();
         Assert.False(serialPort.IsOpen);
 

From d99bdc2130a6f28d8d1b607fd92c3371705ad3f9 Mon Sep 17 00:00:00 2001
From: Argo-AsicoTech 
Date: Sat, 19 Oct 2024 13:14:30 +0800
Subject: [PATCH 35/35] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?=
 =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/BootstrapBlazor/Services/Serial/SerialPortUsbInfo.cs | 4 ++--
 test/UnitTest/Services/SerialServiceTest.cs              | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/BootstrapBlazor/Services/Serial/SerialPortUsbInfo.cs b/src/BootstrapBlazor/Services/Serial/SerialPortUsbInfo.cs
index 779d35c5ce5..e911e92982b 100644
--- a/src/BootstrapBlazor/Services/Serial/SerialPortUsbInfo.cs
+++ b/src/BootstrapBlazor/Services/Serial/SerialPortUsbInfo.cs
@@ -12,10 +12,10 @@ public class SerialPortUsbInfo
     /// 
     /// 厂商 Id
     /// 
-    public string? UsbVendorId { get; internal set; }
+    public string? UsbVendorId { get; set; }
 
     /// 
     /// 产品 Id
     /// 
-    public string? UsbProductId { get; internal set; }
+    public string? UsbProductId { get; set; }
 }
diff --git a/test/UnitTest/Services/SerialServiceTest.cs b/test/UnitTest/Services/SerialServiceTest.cs
index 25c89bd83e5..625f8ed5c9c 100644
--- a/test/UnitTest/Services/SerialServiceTest.cs
+++ b/test/UnitTest/Services/SerialServiceTest.cs
@@ -14,7 +14,7 @@ public async Task GetPort_Ok()
         Context.JSInterop.Setup("open", matcher => matcher.Arguments.Count == 4 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_serial_") ?? false)).SetResult(true);
         Context.JSInterop.Setup("close", matcher => matcher.Arguments.Count == 1 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_serial_") ?? false)).SetResult(true);
         Context.JSInterop.Setup("write", matcher => matcher.Arguments.Count == 2 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_serial_") ?? false)).SetResult(true);
-        Context.JSInterop.Setup("getInfo", matcher => matcher.Arguments.Count == 1 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_serial_") ?? false)).SetResult(new SerialPortUsbInfo());
+        Context.JSInterop.Setup("getInfo", matcher => matcher.Arguments.Count == 1 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_serial_") ?? false)).SetResult(new SerialPortUsbInfo() { UsbVendorId = "Test", UsbProductId = "Test123" });
         Context.JSInterop.Setup("getSignals", matcher => matcher.Arguments.Count == 1 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_serial_") ?? false)).SetResult(new SerialPortSignals()
         {
             RING = false,
@@ -62,8 +62,8 @@ public async Task GetPort_Ok()
         // getInfo
         var info = await serialPort.GetUsbInfo();
         Assert.NotNull(info);
-        Assert.Null(info.UsbVendorId);
-        Assert.Null(info.UsbProductId);
+        Assert.Equal("Test", info.UsbVendorId);
+        Assert.Equal("Test123", info.UsbProductId);
 
         // getSignals
         var signals = await serialPort.GetSignals();
@@ -71,7 +71,7 @@ public async Task GetPort_Ok()
         Assert.False(signals.RING);
         Assert.True(signals.DSR);
         Assert.True(signals.CTS);
-        Assert.True(signals.CTS);
+        Assert.True(signals.DCD);
 
         // setSignals
         var signalOption = new SerialPortSignalsOptions()