feat(android): 添加 CH934X USB 转串口芯片支持
- 在 AndroidManifest.xml 中添加 USB Host 权限声明 - 集成 CH934X Android SDK 并通过反射调用原生功能 - 实现设备查找、串口读写、GPIO 和 Modem 控制功能 - 添加异常回调机制处理设备拔出等情况 - 提供 Stream 数据流支持实时串口数据监听 - 完善单元测试覆盖所有核心功能模块
This commit is contained in:
+127
-2
@@ -1,8 +1,133 @@
|
||||
export 'src/models/models.dart';
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'ch934x_serial_method_channel.dart';
|
||||
import 'ch934x_serial_platform_interface.dart';
|
||||
import 'src/models/models.dart';
|
||||
|
||||
/// CH934X 插件的对外门面类。
|
||||
///
|
||||
/// 内部委托 [Ch934xSerialPlatform] 真正执行平台调用,
|
||||
/// 在 Android 上默认走 [MethodChannelCh934xSerial]。
|
||||
///
|
||||
/// 命名/语义与官方 Android SDK 文档保持一致;若希望
|
||||
/// 监听串口数据流,可使用 [dataStream] 配合 [read]。
|
||||
class Ch934xSerial {
|
||||
Future<String?> getPlatformVersion() {
|
||||
return Ch934xSerialPlatform.instance.getPlatformVersion();
|
||||
/// 使用默认平台实现构造。
|
||||
Ch934xSerial() : _platform = Ch934xSerialPlatform.instance;
|
||||
|
||||
/// 注入自定义平台实现,常用于单元测试。
|
||||
Ch934xSerial.withPlatform(Ch934xSerialPlatform platform)
|
||||
: _platform = platform;
|
||||
|
||||
final Ch934xSerialPlatform _platform;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 设备查找
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// 获取所有已连接的 CH934X 设备信息。
|
||||
Future<List<Ch934xDeviceInfo>> getDeviceList() =>
|
||||
_platform.getDeviceList();
|
||||
|
||||
/// 获取指定设备序列号;非 CH934X 设备时返回 null。
|
||||
Future<String?> getSerialNumber(int deviceId) =>
|
||||
_platform.getSerialNumber(deviceId);
|
||||
|
||||
/// 获取指定设备类型,取值见 [Ch934xDeviceType]。
|
||||
Future<int> getDeviceType(int deviceId) =>
|
||||
_platform.getDeviceType(deviceId);
|
||||
|
||||
/// 获取指定设备的串口列表。
|
||||
Future<List<Ch934xSerialPortInfo>> getSerialPortList(
|
||||
int deviceId, {
|
||||
required int interfaceNumber,
|
||||
}) =>
|
||||
_platform.getSerialPortList(
|
||||
deviceId,
|
||||
interfaceNumber: interfaceNumber,
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 设备打开 / 关闭
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// 打开指定串口。
|
||||
Future<bool> openPort(Ch934xPortTarget target) => _platform.openPort(target);
|
||||
|
||||
/// 关闭当前会话最近一次打开的串口。
|
||||
Future<bool> closePort() => _platform.closePort();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 串口读写
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// 阻塞式读取,直到拿到 [length] 字节或缓冲区被填满。
|
||||
///
|
||||
/// 返回值为实际读到的字节;若底层无数据或读取失败,返回空。
|
||||
Future<Uint8List> read(int length) => _platform.read(length);
|
||||
|
||||
/// 写入数据,返回实际写入的字节数。
|
||||
Future<int> write(Uint8List data) => _platform.write(data);
|
||||
|
||||
/// 构造一个持续从串口拉取数据的 `Stream<Uint8List>`。
|
||||
///
|
||||
/// 内部以 [interval] 为周期反复调用 [read];当底层无数据
|
||||
/// 时返回空缓冲区,消费者可据此判定是否需要结束订阅。
|
||||
Stream<Uint8List> dataStream({
|
||||
int chunkSize = 1024,
|
||||
Duration interval = const Duration(milliseconds: 20),
|
||||
}) async* {
|
||||
if (chunkSize <= 0) {
|
||||
throw ArgumentError.value(chunkSize, 'chunkSize', '必须大于 0');
|
||||
}
|
||||
while (true) {
|
||||
final chunk = await _platform.read(chunkSize);
|
||||
if (chunk.isNotEmpty) {
|
||||
yield chunk;
|
||||
}
|
||||
await Future<void>.delayed(interval);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GPIO
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// 设置 GPIO 输出电平(0 或 1)。
|
||||
Future<bool> setGpioOutput({required int gpioNumber, required int level}) =>
|
||||
_platform.setGpioOutput(gpioNumber: gpioNumber, level: level);
|
||||
|
||||
/// 读取 GPIO 输入电平;负值表示读取失败。
|
||||
Future<int> getGpioInput(int gpioNumber) =>
|
||||
_platform.getGpioInput(gpioNumber);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Modem
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// 设置 DTR / RTS 信号。
|
||||
Future<bool> setModemControl({required int dtr, required int rts}) =>
|
||||
_platform.setModemControl(dtr: dtr, rts: rts);
|
||||
|
||||
/// 获取 Modem 状态位,可通过 [ModemStatus] 工具类解析。
|
||||
Future<int> getModemStatus() => _platform.getModemStatus();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 异常回调
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// 注册异常回调(例如设备拔出)。
|
||||
///
|
||||
/// 返回一个 [StreamSubscription],可在外层 dispose 时取消。
|
||||
Future<StreamSubscription<Ch934xException>> setExceptionCallback(
|
||||
void Function(Ch934xException exception) onException,
|
||||
) async {
|
||||
final controller = StreamController<Ch934xException>();
|
||||
final subscription = controller.stream.listen(onException);
|
||||
await _platform.setExceptionCallback(controller.add);
|
||||
return subscription;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,183 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'ch934x_serial_platform_interface.dart';
|
||||
import 'src/models/models.dart';
|
||||
|
||||
/// An implementation of [Ch934xSerialPlatform] that uses method channels.
|
||||
/// MethodChannel 实现的 [Ch934xSerialPlatform]。
|
||||
///
|
||||
/// 在 Android 端通过同一 MethodChannel 与原生层通信,
|
||||
/// 命名规则:方法名使用下划线小写,字段名使用驼峰式以便
|
||||
/// 直接对应 Java 侧 Map 的 key。
|
||||
class MethodChannelCh934xSerial extends Ch934xSerialPlatform {
|
||||
/// The method channel used to interact with the native platform.
|
||||
/// 测试时可被替换的 MethodChannel。
|
||||
@visibleForTesting
|
||||
final methodChannel = const MethodChannel('ch934x_serial');
|
||||
final MethodChannel methodChannel =
|
||||
const MethodChannel('ch934x_serial');
|
||||
|
||||
/// 通知 Dart 侧的异常事件流,用于支持 `setExceptionCallback`。
|
||||
final StreamController<Ch934xException> _exceptionController =
|
||||
StreamController<Ch934xException>.broadcast();
|
||||
|
||||
@override
|
||||
Future<String?> getPlatformVersion() async {
|
||||
final version = await methodChannel.invokeMethod<String>(
|
||||
'getPlatformVersion',
|
||||
Future<List<Ch934xDeviceInfo>> getDeviceList() async {
|
||||
final raw = await methodChannel.invokeMethod<List<dynamic>>('getDeviceList');
|
||||
if (raw == null) {
|
||||
return const <Ch934xDeviceInfo>[];
|
||||
}
|
||||
return raw
|
||||
.whereType<Map>()
|
||||
.map((e) => Ch934xDeviceInfo.fromMap(Map<dynamic, dynamic>.from(e)))
|
||||
.toList(growable: false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> getSerialNumber(int deviceId) {
|
||||
return methodChannel.invokeMethod<String>(
|
||||
'getSerialNumber',
|
||||
<String, Object>{'deviceId': deviceId},
|
||||
);
|
||||
return version;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getDeviceType(int deviceId) async {
|
||||
final result = await methodChannel.invokeMethod<int>(
|
||||
'getDeviceType',
|
||||
<String, Object>{'deviceId': deviceId},
|
||||
);
|
||||
return result ?? Ch934xDeviceType.unknown;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Ch934xSerialPortInfo>> getSerialPortList(
|
||||
int deviceId, {
|
||||
required int interfaceNumber,
|
||||
}) async {
|
||||
final raw = await methodChannel.invokeMethod<List<dynamic>>(
|
||||
'getSerialPortList',
|
||||
<String, Object>{
|
||||
'deviceId': deviceId,
|
||||
'interfaceNumber': interfaceNumber,
|
||||
},
|
||||
);
|
||||
if (raw == null) {
|
||||
return const <Ch934xSerialPortInfo>[];
|
||||
}
|
||||
return raw
|
||||
.whereType<Map>()
|
||||
.map((e) => Ch934xSerialPortInfo.fromMap(Map<dynamic, dynamic>.from(e)))
|
||||
.toList(growable: false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> openPort(Ch934xPortTarget target) async {
|
||||
final result = await methodChannel.invokeMethod<bool>(
|
||||
'openPort',
|
||||
target.toMap(),
|
||||
);
|
||||
return result ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> closePort() async {
|
||||
final result = await methodChannel.invokeMethod<bool>('closePort');
|
||||
return result ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> read(int length) async {
|
||||
if (length <= 0) {
|
||||
return Uint8List(0);
|
||||
}
|
||||
final raw = await methodChannel.invokeMethod<Uint8List>(
|
||||
'read',
|
||||
<String, Object>{'length': length},
|
||||
);
|
||||
return raw ?? Uint8List(0);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> write(Uint8List data) async {
|
||||
if (data.isEmpty) {
|
||||
return 0;
|
||||
}
|
||||
final result = await methodChannel.invokeMethod<int>(
|
||||
'write',
|
||||
<String, Object>{'data': data},
|
||||
);
|
||||
return result ?? 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> setGpioOutput({
|
||||
required int gpioNumber,
|
||||
required int level,
|
||||
}) async {
|
||||
final result = await methodChannel.invokeMethod<bool>(
|
||||
'setGpioOutput',
|
||||
<String, Object>{
|
||||
'gpioNumber': gpioNumber,
|
||||
'level': level,
|
||||
},
|
||||
);
|
||||
return result ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getGpioInput(int gpioNumber) async {
|
||||
final result = await methodChannel.invokeMethod<int>(
|
||||
'getGpioInput',
|
||||
<String, Object>{'gpioNumber': gpioNumber},
|
||||
);
|
||||
return result ?? -1;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> setModemControl({required int dtr, required int rts}) async {
|
||||
final result = await methodChannel.invokeMethod<bool>(
|
||||
'setModemControl',
|
||||
<String, Object>{
|
||||
'dtr': dtr,
|
||||
'rts': rts,
|
||||
},
|
||||
);
|
||||
return result ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getModemStatus() async {
|
||||
final result = await methodChannel.invokeMethod<int>('getModemStatus');
|
||||
return result ?? 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setExceptionCallback(
|
||||
void Function(Ch934xException exception) onException,
|
||||
) async {
|
||||
_exceptionController.stream.listen(onException);
|
||||
await methodChannel.invokeMethod<void>('setExceptionCallback');
|
||||
}
|
||||
|
||||
/// 由原生层主动调用的入口,对应文档 7.3.1 中的 `onException` 回调。
|
||||
///
|
||||
/// 必须在原生层将 `MethodChannel` 的 `setMethodCallHandler` 调通后
|
||||
/// 才会被触发;此方法在测试中也可直接调用,用于模拟异常事件。
|
||||
@visibleForTesting
|
||||
void dispatchException({
|
||||
required int type,
|
||||
String? message,
|
||||
String? cause,
|
||||
}) {
|
||||
_exceptionController.add(
|
||||
Ch934xException(type: type, message: message, cause: cause),
|
||||
);
|
||||
}
|
||||
|
||||
/// 释放内部资源,通常仅在测试或热重载场景下使用。
|
||||
@visibleForTesting
|
||||
void dispose() {
|
||||
_exceptionController.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,100 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
|
||||
import 'ch934x_serial_method_channel.dart';
|
||||
import 'src/models/models.dart';
|
||||
|
||||
/// CH934X 插件的平台无关抽象接口。
|
||||
///
|
||||
/// Dart 侧应面向此接口编程,具体实现由 Android 平台
|
||||
/// (MethodChannel) 提供;`set mockMethodCallHandler` 的
|
||||
/// 单元测试可以替换该实现以验证上层逻辑。
|
||||
abstract class Ch934xSerialPlatform extends PlatformInterface {
|
||||
/// Constructs a Ch934xSerialPlatform.
|
||||
/// 构造 [Ch934xSerialPlatform]。
|
||||
Ch934xSerialPlatform() : super(token: _token);
|
||||
|
||||
static final Object _token = Object();
|
||||
|
||||
static Ch934xSerialPlatform _instance = MethodChannelCh934xSerial();
|
||||
|
||||
/// The default instance of [Ch934xSerialPlatform] to use.
|
||||
///
|
||||
/// Defaults to [MethodChannelCh934xSerial].
|
||||
/// 平台无关实现当前持有的具体后端。
|
||||
static Ch934xSerialPlatform get instance => _instance;
|
||||
|
||||
/// Platform-specific implementations should set this with their own
|
||||
/// platform-specific class that extends [Ch934xSerialPlatform] when
|
||||
/// they register themselves.
|
||||
/// 替换具体实现,常用于单元测试。
|
||||
static set instance(Ch934xSerialPlatform instance) {
|
||||
PlatformInterface.verifyToken(instance, _token);
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
Future<String?> getPlatformVersion() {
|
||||
throw UnimplementedError('platformVersion() has not been implemented.');
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
// 设备查找
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// 获取所有已连接的 CH934X 设备信息(对应 4.1.4 `getCH934XDeviceList`)。
|
||||
Future<List<Ch934xDeviceInfo>> getDeviceList();
|
||||
|
||||
/// 获取指定设备的 CH934X 序列号(对应 4.1.1 `CH934XSerialNum`)。
|
||||
Future<String?> getSerialNumber(int deviceId);
|
||||
|
||||
/// 获取指定设备类型(对应 4.1.2 `CH934XDeviceType`)。
|
||||
Future<int> getDeviceType(int deviceId);
|
||||
|
||||
/// 获取指定设备的串口列表(对应 4.1.3 `getCH934XSerialPortList`)。
|
||||
Future<List<Ch934xSerialPortInfo>> getSerialPortList(
|
||||
int deviceId, {
|
||||
required int interfaceNumber,
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 设备打开 / 关闭
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// 初始化并打开指定串口(对应 5.1.1 `UsbSerial.init`)。
|
||||
Future<bool> openPort(Ch934xPortTarget target);
|
||||
|
||||
/// 关闭当前线程/会话最近一次打开的串口(对应 5.2.1 `UsbSerial.close`)。
|
||||
Future<bool> closePort();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 串口读写
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// 从串口读取数据(对应 6.1.1 `UsbSerial.read`)。
|
||||
Future<Uint8List> read(int length);
|
||||
|
||||
/// 向串口写入数据(对应 6.2.1 `UsbSerial.write`)。
|
||||
Future<int> write(Uint8List data);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GPIO
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// 设置 GPIO 输出(对应 7.1.1 `UsbSerial.setGpioOutput`)。
|
||||
Future<bool> setGpioOutput({required int gpioNumber, required int level});
|
||||
|
||||
/// 读取 GPIO 输入(对应 7.1.2 `UsbSerial.getGpioInput`)。
|
||||
Future<int> getGpioInput(int gpioNumber);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Modem
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// 设置 Modem 控制(对应 7.2.1 `UsbSerial.setModemControl`)。
|
||||
Future<bool> setModemControl({required int dtr, required int rts});
|
||||
|
||||
/// 获取 Modem 状态(对应 7.2.2 `UsbSerial.getModemStatus`)。
|
||||
Future<int> getModemStatus();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 异常回调
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// 注册异常回调(对应 7.3.1 `UsbSerial.setExceptionCallback`)。
|
||||
///
|
||||
/// 当原生层触发异常(例如设备拔出)时,会通过
|
||||
/// [onException] 中传入的回调通知调用方。
|
||||
Future<void> setExceptionCallback(
|
||||
void Function(Ch934xException exception) onException,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import 'ch934x_device_type.dart';
|
||||
|
||||
/// 单个 CH934X 设备所挂载串口的信息。
|
||||
///
|
||||
/// 对应 Android 端 `UsbHelper.getCH934XSerialPortList` 返
|
||||
/// 回的 `UsbSerial` 数组中每个元素的 Dart 描述,通常足以
|
||||
/// 用来调用 `UsbSerial.init` 打开对应的串口通道。
|
||||
class Ch934xSerialPortInfo {
|
||||
const Ch934xSerialPortInfo({
|
||||
required this.portIndex,
|
||||
this.devicePath,
|
||||
this.driverName,
|
||||
});
|
||||
|
||||
/// 串口在所属设备上的索引(从 0 开始)。
|
||||
final int portIndex;
|
||||
|
||||
/// 底层串口节点路径(若原生层提供)。
|
||||
final String? devicePath;
|
||||
|
||||
/// 驱动或端口名(若原生层提供)。
|
||||
final String? driverName;
|
||||
|
||||
/// 从原生层返回的 Map 还原对象,字段缺失时使用安全默认值。
|
||||
factory Ch934xSerialPortInfo.fromMap(Map<dynamic, dynamic> map) {
|
||||
final indexValue = map['portIndex'] ?? map['serialPortIndex'];
|
||||
return Ch934xSerialPortInfo(
|
||||
portIndex: indexValue is int ? indexValue : 0,
|
||||
devicePath: map['devicePath'] as String?,
|
||||
driverName: map['driverName'] as String?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 文档 4.1.4 中 `UsbHelper.getCH934XDeviceList` 返回的设备信息。
|
||||
///
|
||||
/// 字段命名遵循 Java 端的驼峰式命名,通过 `fromMap` 解析原生层结果。
|
||||
class Ch934xDeviceInfo {
|
||||
const Ch934xDeviceInfo({
|
||||
required this.deviceId,
|
||||
required this.vendorId,
|
||||
required this.productId,
|
||||
required this.deviceType,
|
||||
this.serialNumber,
|
||||
this.productName,
|
||||
this.manufacturerName,
|
||||
this.interfaceCount = 0,
|
||||
this.serialPorts = const <Ch934xSerialPortInfo>[],
|
||||
});
|
||||
|
||||
/// Android `UsbDevice.getDeviceId()`。
|
||||
final int deviceId;
|
||||
|
||||
/// USB 厂商 ID。
|
||||
final int vendorId;
|
||||
|
||||
/// USB 产品 ID。
|
||||
final int productId;
|
||||
|
||||
/// 通过 [Ch934xDeviceType] 中的常量值标识。
|
||||
final int deviceType;
|
||||
|
||||
/// 通过 `UsbHelper.CH934XSerialNum` 获取的序列号;非 CH934X 设备时为 null。
|
||||
final String? serialNumber;
|
||||
|
||||
/// 设备产品名(若原生层提供)。
|
||||
final String? productName;
|
||||
|
||||
/// 设备厂商名(若原生层提供)。
|
||||
final String? manufacturerName;
|
||||
|
||||
/// 该设备暴露的 USB 接口数量。
|
||||
final int interfaceCount;
|
||||
|
||||
/// 关联的串口列表,部分设备可能为空。
|
||||
final List<Ch934xSerialPortInfo> serialPorts;
|
||||
|
||||
/// 判断当前设备是否被原生层识别为 CH934X 系列。
|
||||
bool get isCh934x => deviceType >= Ch934xDeviceType.ch9344 &&
|
||||
deviceType <= Ch934xDeviceType.ch934xOther;
|
||||
|
||||
/// 从原生层返回值反序列化,容错处理缺失字段。
|
||||
factory Ch934xDeviceInfo.fromMap(Map<dynamic, dynamic> map) {
|
||||
final rawPorts = map['serialPorts'];
|
||||
final ports = <Ch934xSerialPortInfo>[];
|
||||
if (rawPorts is List) {
|
||||
for (final entry in rawPorts) {
|
||||
if (entry is Map) {
|
||||
ports.add(
|
||||
Ch934xSerialPortInfo.fromMap(Map<dynamic, dynamic>.from(entry)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ch934xDeviceInfo(
|
||||
deviceId: (map['deviceId'] as int?) ?? 0,
|
||||
vendorId: (map['vendorId'] as int?) ?? 0,
|
||||
productId: (map['productId'] as int?) ?? 0,
|
||||
deviceType: (map['deviceType'] as int?) ?? Ch934xDeviceType.unknown,
|
||||
serialNumber: map['serialNumber'] as String?,
|
||||
productName: map['productName'] as String?,
|
||||
manufacturerName: map['manufacturerName'] as String?,
|
||||
interfaceCount: (map['interfaceCount'] as int?) ?? 0,
|
||||
serialPorts: ports,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/// CH934X 设备类型常量。
|
||||
///
|
||||
/// 来自文档 4.1.2 `UsbHelper.CH934XDeviceType` 的返回值,描述
|
||||
/// 枚举到的 USB 设备属于沁恒 CH934X 家族中的哪一颗芯片。
|
||||
class Ch934xDeviceType {
|
||||
const Ch934xDeviceType._();
|
||||
|
||||
/// CH9344 芯片。
|
||||
static const int ch9344 = 0;
|
||||
|
||||
/// CH9344L 芯片。
|
||||
static const int ch9344L = 1;
|
||||
|
||||
/// CH9350 芯片。
|
||||
static const int ch9350 = 2;
|
||||
|
||||
/// CH9348Q 芯片。
|
||||
static const int ch9348Q = 3;
|
||||
|
||||
/// CH9342 芯片。
|
||||
static const int ch9342 = 4;
|
||||
|
||||
/// 其他 CH934X 设备。
|
||||
static const int ch934xOther = 5;
|
||||
|
||||
/// 未知或非 CH934X 设备。
|
||||
static const int unknown = -1;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/// 设备拔出等异常事件类型常量。
|
||||
///
|
||||
/// 透传自 Android 端 `UsbSerial.ExceptionCallback.onException`
|
||||
/// 的 `type` 参数,插件使用者可根据此值进行不同处理。
|
||||
class Ch934xExceptionType {
|
||||
const Ch934xExceptionType._();
|
||||
|
||||
/// 未知异常。
|
||||
static const int unknown = 0;
|
||||
|
||||
/// 设备被拔出。
|
||||
static const int deviceDetached = 1;
|
||||
|
||||
/// 读写过程中发生 IO 错误。
|
||||
static const int ioError = 2;
|
||||
|
||||
/// 原生 SDK 主动抛出的其他异常。
|
||||
static const int sdk = 3;
|
||||
}
|
||||
|
||||
/// `setExceptionCallback` 回调中的载荷,描述一次异常事件。
|
||||
class Ch934xException {
|
||||
const Ch934xException({required this.type, this.message, this.cause});
|
||||
|
||||
/// 异常类型,取值见 [Ch934xExceptionType] 常量。
|
||||
final int type;
|
||||
|
||||
/// 异常的文本描述(若原生层提供)。
|
||||
final String? message;
|
||||
|
||||
/// 底层异常类名(若原生层提供)。
|
||||
final String? cause;
|
||||
|
||||
/// 便于在日志/UI 中显示的描述,自动将类型转换为常量名。
|
||||
@override
|
||||
String toString() {
|
||||
final typeName = switch (type) {
|
||||
Ch934xExceptionType.deviceDetached => 'deviceDetached',
|
||||
Ch934xExceptionType.ioError => 'ioError',
|
||||
Ch934xExceptionType.sdk => 'sdk',
|
||||
_ => 'unknown',
|
||||
};
|
||||
final buffer = StringBuffer('Ch934xException(type: $typeName');
|
||||
if (message != null && message!.isNotEmpty) {
|
||||
buffer.write(', message: $message');
|
||||
}
|
||||
if (cause != null && cause!.isNotEmpty) {
|
||||
buffer.write(', cause: $cause');
|
||||
}
|
||||
buffer.write(')');
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/// 用于打开 CH934X 串口的目标描述,封装 init 接口需要的全部参数。
|
||||
///
|
||||
/// 取代文档示例代码中散落的 `device`、`interfaceNum`、
|
||||
/// `serialPortIndex`,便于在异步链中安全传递。
|
||||
class Ch934xPortTarget {
|
||||
const Ch934xPortTarget({
|
||||
required this.deviceId,
|
||||
required this.interfaceNumber,
|
||||
required this.serialPortIndex,
|
||||
});
|
||||
|
||||
/// Android `UsbDevice.getDeviceId()`。
|
||||
final int deviceId;
|
||||
|
||||
/// CH934X 设备的接口号(文档 4.1.3 中的 `interfaceNum`)。
|
||||
final int interfaceNumber;
|
||||
|
||||
/// 串口索引(文档 5.1.1 中的 `serialPortIndex`)。
|
||||
final int serialPortIndex;
|
||||
|
||||
/// 等价的可序列化 Map,用于在 MethodChannel 中传递。
|
||||
Map<String, Object> toMap() => <String, Object>{
|
||||
'deviceId': deviceId,
|
||||
'interfaceNumber': interfaceNumber,
|
||||
'serialPortIndex': serialPortIndex,
|
||||
};
|
||||
|
||||
/// 从 MethodChannel 回传的 Map 还原对象,字段缺失时回退到 0。
|
||||
factory Ch934xPortTarget.fromMap(Map<dynamic, dynamic> map) {
|
||||
return Ch934xPortTarget(
|
||||
deviceId: (map['deviceId'] as int?) ?? 0,
|
||||
interfaceNumber: (map['interfaceNumber'] as int?) ?? 0,
|
||||
serialPortIndex: (map['serialPortIndex'] as int?) ?? 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export 'ch934x_device_info.dart';
|
||||
export 'ch934x_device_type.dart';
|
||||
export 'ch934x_exception.dart';
|
||||
export 'ch934x_port_target.dart';
|
||||
export 'modem_status.dart';
|
||||
@@ -0,0 +1,21 @@
|
||||
/// Modem 状态位掩码常量,对应文档 7.2.2 中 `getModemStatus` 的返回值。
|
||||
///
|
||||
/// 可与 `getModemStatus()` 返回值按位与来判定具体引脚电平。
|
||||
class ModemStatus {
|
||||
const ModemStatus._();
|
||||
|
||||
/// CTS 状态位,值为 0x01。
|
||||
static const int cts = 0x01;
|
||||
|
||||
/// DSR 状态位,值为 0x02。
|
||||
static const int dsr = 0x02;
|
||||
|
||||
/// RI 状态位,值为 0x04。
|
||||
static const int ri = 0x04;
|
||||
|
||||
/// DCD 状态位,值为 0x08。
|
||||
static const int dcd = 0x08;
|
||||
|
||||
/// 读取指定状态位是否为高电平。
|
||||
static bool isSet(int status, int mask) => (status & mask) != 0;
|
||||
}
|
||||
Reference in New Issue
Block a user