feat(ch934x_serial): 添加多端口并发轮询流功能
- 新增多端口并发轮询流 portDataStream 方法,支持每周期轮询 8 个端口 - 修改 read 方法增加 serialPortIndex 参数,支持指定端口读取数据 - 更新 Android 原生实现,支持通过 serialPortIndex 参数指定读取端口 - 移除 IDataCallback 注册逻辑,改用轮询方式拉取数据避免缓冲区冲突 - 添加主线程切换机制,确保异常推送在主线程执行 - 更新 pubspec.yaml 添加 ch934x_serial 本地插件依赖 - 更新项目路线图,标记 Phase 42 完成,进度更新至 50%
This commit is contained in:
@@ -10,6 +10,8 @@ import android.content.IntentFilter;
|
|||||||
import android.hardware.usb.UsbDevice;
|
import android.hardware.usb.UsbDevice;
|
||||||
import android.hardware.usb.UsbManager;
|
import android.hardware.usb.UsbManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -22,7 +24,6 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import cn.wch.ch934xlib.CH934XManager;
|
import cn.wch.ch934xlib.CH934XManager;
|
||||||
import cn.wch.ch934xlib.callback.IDataCallback;
|
|
||||||
import cn.wch.ch934xlib.callback.IModemStatus;
|
import cn.wch.ch934xlib.callback.IModemStatus;
|
||||||
import cn.wch.ch934xlib.callback.IUsbStateChange;
|
import cn.wch.ch934xlib.callback.IUsbStateChange;
|
||||||
import cn.wch.ch934xlib.chip.ChipType;
|
import cn.wch.ch934xlib.chip.ChipType;
|
||||||
@@ -74,9 +75,6 @@ public class Ch934xSerialPlugin implements FlutterPlugin, MethodCallHandler, Act
|
|||||||
/** 当前会话选中的串口索引(对应文档 5.1.1 中的 serialPortIndex)。 */
|
/** 当前会话选中的串口索引(对应文档 5.1.1 中的 serialPortIndex)。 */
|
||||||
private int activeSerialNumber = -1;
|
private int activeSerialNumber = -1;
|
||||||
|
|
||||||
/** 当前会话是否已注册了 IDataCallback,用于避免重复注册。 */
|
|
||||||
private boolean dataCallbackRegistered = false;
|
|
||||||
|
|
||||||
/** 当前会话是否已注册了 IModemStatus,用于避免重复注册。 */
|
/** 当前会话是否已注册了 IModemStatus,用于避免重复注册。 */
|
||||||
private boolean modemCallbackRegistered = false;
|
private boolean modemCallbackRegistered = false;
|
||||||
|
|
||||||
@@ -153,7 +151,6 @@ public class Ch934xSerialPlugin implements FlutterPlugin, MethodCallHandler, Act
|
|||||||
}
|
}
|
||||||
activeDevice = null;
|
activeDevice = null;
|
||||||
activeSerialNumber = -1;
|
activeSerialNumber = -1;
|
||||||
dataCallbackRegistered = false;
|
|
||||||
modemCallbackRegistered = false;
|
modemCallbackRegistered = false;
|
||||||
exceptionCallbackEnabled = false;
|
exceptionCallbackEnabled = false;
|
||||||
}
|
}
|
||||||
@@ -371,7 +368,9 @@ public class Ch934xSerialPlugin implements FlutterPlugin, MethodCallHandler, Act
|
|||||||
}
|
}
|
||||||
activeDevice = device;
|
activeDevice = device;
|
||||||
activeSerialNumber = serialPortIndex;
|
activeSerialNumber = serialPortIndex;
|
||||||
ensureDataCallback();
|
// 注意:不注册 IDataCallback — SDK 在有回调注册时会通过回调
|
||||||
|
// 投递数据并释放内部缓冲区,导致 readData() 返回空。
|
||||||
|
// 通过 dataStream 轮询 readData() 拉取数据。
|
||||||
ensureModemCallback();
|
ensureModemCallback();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -486,7 +485,6 @@ public class Ch934xSerialPlugin implements FlutterPlugin, MethodCallHandler, Act
|
|||||||
}
|
}
|
||||||
activeDevice = null;
|
activeDevice = null;
|
||||||
activeSerialNumber = -1;
|
activeSerialNumber = -1;
|
||||||
dataCallbackRegistered = false;
|
|
||||||
modemCallbackRegistered = false;
|
modemCallbackRegistered = false;
|
||||||
modemStatusCache = 0;
|
modemStatusCache = 0;
|
||||||
return true;
|
return true;
|
||||||
@@ -519,10 +517,13 @@ public class Ch934xSerialPlugin implements FlutterPlugin, MethodCallHandler, Act
|
|||||||
if (length == null || length <= 0) {
|
if (length == null || length <= 0) {
|
||||||
return new byte[0];
|
return new byte[0];
|
||||||
}
|
}
|
||||||
|
// 可选参数:指定读哪个端口的缓冲区。不传则读 activeSerialNumber(向后兼容)
|
||||||
|
Integer portIndex = call.argument("serialPortIndex");
|
||||||
|
int idx = (portIndex != null) ? portIndex : activeSerialNumber;
|
||||||
try {
|
try {
|
||||||
// SDK 返回内部缓冲的所有数据,与文档 read 语义一致。
|
// SDK 返回内部缓冲的所有数据,与文档 read 语义一致。
|
||||||
byte[] data = CH934XManager.getInstance()
|
byte[] data = CH934XManager.getInstance()
|
||||||
.readData(activeDevice, activeSerialNumber);
|
.readData(activeDevice, idx);
|
||||||
if (data == null || data.length == 0) {
|
if (data == null || data.length == 0) {
|
||||||
return new byte[0];
|
return new byte[0];
|
||||||
}
|
}
|
||||||
@@ -682,25 +683,6 @@ public class Ch934xSerialPlugin implements FlutterPlugin, MethodCallHandler, Act
|
|||||||
return ports;
|
return ports;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 注册 IDataCallback 推送数据上抛(可选,目前仅用作 SDK 内部缓冲预热)。 */
|
|
||||||
private void ensureDataCallback() {
|
|
||||||
if (dataCallbackRegistered || activeDevice == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
CH934XManager.getInstance().registerDataCallback(activeDevice,
|
|
||||||
new IDataCallback() {
|
|
||||||
@Override
|
|
||||||
public void onData(int serialNumber, byte[] buffer, int length) {
|
|
||||||
// 数据通过 readData 拉取即可,这里留空。
|
|
||||||
}
|
|
||||||
});
|
|
||||||
dataCallbackRegistered = true;
|
|
||||||
} catch (Throwable t) {
|
|
||||||
// 注册失败不影响主流程,readData 仍可工作。
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ensureModemCallback() {
|
private void ensureModemCallback() {
|
||||||
if (modemCallbackRegistered || activeDevice == null) {
|
if (modemCallbackRegistered || activeDevice == null) {
|
||||||
return;
|
return;
|
||||||
@@ -740,11 +722,15 @@ public class Ch934xSerialPlugin implements FlutterPlugin, MethodCallHandler, Act
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 主动上抛异常给 Dart 侧。 */
|
/** 主动上抛异常给 Dart 侧。若当前非主线程则自动切换到主线程再发送。 */
|
||||||
private void pushException(int type, String message, @Nullable String cause) {
|
private void pushException(int type, String message, @Nullable String cause) {
|
||||||
if (!exceptionCallbackEnabled || channel == null) {
|
if (!exceptionCallbackEnabled || channel == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||||
|
new Handler(Looper.getMainLooper()).post(() -> pushException(type, message, cause));
|
||||||
|
return;
|
||||||
|
}
|
||||||
Map<String, Object> payload = new HashMap<>();
|
Map<String, Object> payload = new HashMap<>();
|
||||||
payload.put("type", type);
|
payload.put("type", type);
|
||||||
payload.put("message", message);
|
payload.put("message", message);
|
||||||
|
|||||||
@@ -107,6 +107,29 @@ class Ch934xSerial {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 多端口并发轮询流:每周期轮询 8 个端口各一次,返回 (portIndex, data) 元组。
|
||||||
|
///
|
||||||
|
/// 各端口数据互不干扰,消费者端需要用每个端口独立的缓冲区拆分 \r\n 行。
|
||||||
|
/// 不传 [ports] 时默认轮询 0..7 全部端口。
|
||||||
|
Stream<(int, Uint8List)> portDataStream({
|
||||||
|
int chunkSize = 1024,
|
||||||
|
Duration interval = const Duration(milliseconds: 25),
|
||||||
|
List<int> ports = const [0, 1, 2, 3, 4, 5, 6, 7],
|
||||||
|
}) async* {
|
||||||
|
if (chunkSize <= 0) {
|
||||||
|
throw ArgumentError.value(chunkSize, 'chunkSize', '必须大于 0');
|
||||||
|
}
|
||||||
|
while (true) {
|
||||||
|
for (final port in ports) {
|
||||||
|
final chunk = await _platform.read(chunkSize, serialPortIndex: port);
|
||||||
|
if (chunk.isNotEmpty) {
|
||||||
|
yield (port, chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Future<void>.delayed(interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// GPIO
|
// GPIO
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -124,14 +124,15 @@ class MethodChannelCh934xSerial extends Ch934xSerialPlatform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List> read(int length) async {
|
Future<Uint8List> read(int length, {int? serialPortIndex}) async {
|
||||||
if (length <= 0) {
|
if (length <= 0) {
|
||||||
return Uint8List(0);
|
return Uint8List(0);
|
||||||
}
|
}
|
||||||
final raw = await methodChannel.invokeMethod<Uint8List>(
|
final args = <String, Object>{'length': length};
|
||||||
'read',
|
if (serialPortIndex != null) {
|
||||||
<String, Object>{'length': length},
|
args['serialPortIndex'] = serialPortIndex;
|
||||||
);
|
}
|
||||||
|
final raw = await methodChannel.invokeMethod<Uint8List>('read', args);
|
||||||
return raw ?? Uint8List(0);
|
return raw ?? Uint8List(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ abstract class Ch934xSerialPlatform extends PlatformInterface {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
/// 从串口读取数据(对应 6.1.1 `UsbSerial.read`)。
|
/// 从串口读取数据(对应 6.1.1 `UsbSerial.read`)。
|
||||||
Future<Uint8List> read(int length);
|
/// [serialPortIndex] 可选,指定读取哪个端口的缓冲区;不传则读上次 setActivePort 设置的端口。
|
||||||
|
Future<Uint8List> read(int length, {int? serialPortIndex});
|
||||||
|
|
||||||
/// 向串口写入数据(对应 6.2.1 `UsbSerial.write`)。
|
/// 向串口写入数据(对应 6.2.1 `UsbSerial.write`)。
|
||||||
Future<int> write(Uint8List data);
|
Future<int> write(Uint8List data);
|
||||||
|
|||||||
Reference in New Issue
Block a user