This commit is contained in:
2026-04-21 12:57:33 +08:00
commit c000eb12f8
64 changed files with 7970 additions and 0 deletions

491
lib/src/ch34_manager.dart Normal file
View File

@@ -0,0 +1,491 @@
import 'dart:typed_data';
import 'ch34_platform_interface.dart';
import 'types/ch34_types.dart';
/// CH34X USB 转串口插件的管理器。
///
/// 提供静态方法访问所有 WCH WCHUARTManager API。
/// 所有方法委托给 [Ch34Platform.instance] 实现。
class Ch34Manager {
Ch34Manager._();
/// ==================== 基础方法 ====================
/// 获取平台版本。
///
/// @return 平台版本字符串。
static Future<String?> getPlatformVersion() {
return Ch34Platform.instance.getPlatformVersion();
}
/// ==================== 设备枚举与识别 ====================
/// 枚举当前所有可用的 USB 设备。
///
/// @return 可用 USB 设备列表。
/// @throws Ch34Exception 如果枚举失败。
static Future<List<UsbDeviceInfo>> enumDevice() {
return Ch34Platform.instance.enumDevice();
}
/// 获取该 UsbDevice 的芯片型号。
///
/// @param deviceName 设备名称。
/// @return 芯片型号字符串null 表示无法识别。
static Future<String?> getChipType(String deviceName) {
return Ch34Platform.instance.getChipType(deviceName);
}
/// ==================== 设备打开与权限 ====================
/// 打开 USB 设备。
///
/// @param deviceName 设备名称。
/// @return `true` 成功,`false` 失败。
static Future<bool> openDevice(String deviceName) {
return Ch34Platform.instance.openDevice(deviceName);
}
/// 申请 USB 设备的权限。
///
/// @param deviceName 设备名称。
/// @return `true` 已授权,`false` 被拒绝。
static Future<bool> requestPermission(String deviceName) {
return Ch34Platform.instance.requestPermission(deviceName);
}
/// ==================== USB 状态监听 ====================
/// 注册 USB 设备插拔状态监听。
///
/// @param onStateChanged 状态变化回调。
static void setUsbStateListener(
void Function(String deviceName, bool connected) onStateChanged) {
Ch34Platform.instance.setUsbStateListener(onStateChanged);
}
/// 移除 USB 状态监听。
static void removeUsbStateListener() {
Ch34Platform.instance.removeUsbStateListener();
}
/// ==================== 串口信息 ====================
/// 获取设备的串口数目。
///
/// @param deviceName 设备名称。
/// @return 串口数目,-1 表示读取芯片型号失败。
static Future<int> getSerialCount(String deviceName) {
return Ch34Platform.instance.getSerialCount(deviceName);
}
/// 获取串口波特率。
///
/// 实际仅针对 CH9114 系列有效,其他类型设备无需调用。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @return 大于 0 表示串口波特率,小于 0 表示出错。
static Future<int> getSerialBaud(String deviceName, int serialNumber) {
return Ch34Platform.instance.getSerialBaud(deviceName, serialNumber);
}
/// 获取芯片主频。
///
/// 实际仅针对 CH9114 系列有效,其他类型设备无需调用。
///
/// @param deviceName 设备名称。
/// @return 芯片主频信息对象。
static Future<ChipMasterFrequency> getChipMasterFrequency(String deviceName) {
return Ch34Platform.instance.getChipMasterFrequency(deviceName);
}
/// 打开或关闭串口。
///
/// 实际仅针对 CH9114 系列有效,其他类型设备无需调用。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param enable `true` 打开,`false` 关闭。
/// @return `true` 设置成功,`false` 设置失败。
static Future<bool> enableSerial(
String deviceName,
int serialNumber,
bool enable,
) {
return Ch34Platform.instance.enableSerial(deviceName, serialNumber, enable);
}
/// ==================== 串口参数设置 ====================
/// 设置串口参数。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号(从 0 开始)。
/// @param parameter 串口参数配置。
/// @return `true` 设置成功,`false` 设置失败。
static Future<bool> setSerialParameter(
String deviceName,
int serialNumber,
SerialParameter parameter,
) {
return Ch34Platform.instance.setSerialParameter(
deviceName,
serialNumber,
parameter,
);
}
/// ==================== 数据读写 ====================
/// 发送串口数据(同步发送)。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param data 要发送的数据。
/// @param timeout 超时时间毫秒0 表示不超时。
/// @return 实际发送的字节数。
static Future<int> writeData(
String deviceName,
int serialNumber,
Uint8List data, {
int timeout = 0,
}) {
return Ch34Platform.instance.writeData(
deviceName,
serialNumber,
data,
timeout: timeout,
);
}
/// 发送串口数据(异步发送)。
///
/// 将数据加入缓存持续发送,不返回状态和结果。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param data 要发送的数据。
static Future<void> asyncWriteData(
String deviceName,
int serialNumber,
Uint8List data,
) {
return Ch34Platform.instance.asyncWriteData(
deviceName,
serialNumber,
data,
);
}
/// 阻塞读取串口数据。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @return 读取到的数据。
static Future<Uint8List> readData(
String deviceName,
int serialNumber,
) {
return Ch34Platform.instance.readData(deviceName, serialNumber);
}
/// 主动读取串口数据(带超时参数)。
///
/// 读取行为说明:
/// - 当 vTime>0vMin>0 时:阻塞直到读取到第一个字符后开始计时,
/// 时间到或已读够 vMin 个字符则返回。
/// - 当 vTime>0vMin=0 时:读到数据立即返回,否则最多等待 vTime。
/// - 当 vTime=0vMin>0 时:一直阻塞直到读到 vMin 个字符后返回。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param vTime 等待时间(毫秒)。
/// @param vMin 读取的最小字节数。
/// @return 读取到的数据。
static Future<Uint8List> readDataWithTimeout(
String deviceName,
int serialNumber,
int vTime,
int vMin,
) {
return Ch34Platform.instance.readDataWithTimeout(
deviceName,
serialNumber,
vTime,
vMin,
);
}
/// 注册串口数据回调。
///
/// 注册后数据自动推送,不需要主动调用 [readData]。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param onData 数据接收回调。
static Future<void> registerDataCallback(
String deviceName,
int serialNumber,
void Function(Uint8List data) onData,
) {
return Ch34Platform.instance.registerDataCallback(
deviceName,
serialNumber,
onData,
);
}
/// 取消注册串口数据回调。
///
/// @param deviceName 设备名称。
static void removeDataCallback(String deviceName) {
Ch34Platform.instance.removeDataCallback(deviceName);
}
/// ==================== 连接状态 ====================
/// 判断 USB 设备是否已经连接。
///
/// @param deviceName 设备名称。
/// @return `true` 已连接,`false` 未连接。
static Future<bool> isConnected(String deviceName) {
return Ch34Platform.instance.isConnected(deviceName);
}
/// 获取当前已经打开的设备列表。
///
/// @return 已打开的设备名称列表。
static Future<List<String>> getConnectedDevices() {
return Ch34Platform.instance.getConnectedDevices();
}
/// ==================== 断开与关闭 ====================
/// 断开 USB 设备的连接。
///
/// @param deviceName 设备名称。
static Future<void> disconnect(String deviceName) {
return Ch34Platform.instance.disconnect(deviceName);
}
/// 释放资源,关闭所有串口设备。
static Future<void> close() {
return Ch34Platform.instance.close();
}
/// ==================== GPIO 功能 ====================
/// 查询设备是否支持 GPIO 功能。
///
/// @param deviceName 设备名称。
/// @return `true` 支持,`false` 不支持。
static Future<bool> isSupportGpio(String deviceName) {
return Ch34Platform.instance.isSupportGpio(deviceName);
}
/// 查询该 USB 设备的 GPIO 数目。
///
/// @param deviceName 设备名称。
/// @return GPIO 数目。
static Future<int> queryGpioCount(String deviceName) {
return Ch34Platform.instance.queryGpioCount(deviceName);
}
/// 查询该 USB 设备指定 GPIO 的状态。
///
/// @param deviceName 设备名称。
/// @param gpioIndex GPIO 编号(从 0 开始)。
/// @return GPIO 状态。
static Future<GpioStatus> queryGpioStatus(
String deviceName,
int gpioIndex,
) {
return Ch34Platform.instance.queryGpioStatus(deviceName, gpioIndex);
}
/// 查询该 USB 设备的所有 GPIO 状态。
///
/// @param deviceName 设备名称。
/// @return 全部 GPIO 状态列表。
static Future<List<GpioStatus>> queryAllGpioStatus(String deviceName) {
return Ch34Platform.instance.queryAllGpioStatus(deviceName);
}
/// 使能指定 GPIO。
///
/// @param deviceName 设备名称。
/// @param gpioIndex GPIO 编号。
/// @param enable `true` 使能,`false` 关闭。
/// @param direction GPIO 方向。
/// @return `true` 使能成功,`false` 使能失败。
static Future<bool> enableGpio(
String deviceName,
int gpioIndex,
bool enable,
GpioDirection direction,
) {
return Ch34Platform.instance.enableGpio(
deviceName,
gpioIndex,
enable,
direction,
);
}
/// 设置指定 GPIO 的电平值。
///
/// @param deviceName 设备名称。
/// @param gpioIndex GPIO 编号。
/// @param value GPIO 电平值。
/// @return `true` 设置成功,`false` 设置失败。
static Future<bool> setGpioVal(
String deviceName,
int gpioIndex,
GpioValue value,
) {
return Ch34Platform.instance.setGpioVal(
deviceName,
gpioIndex,
value,
);
}
/// 获取指定 GPIO 的电平值。
///
/// @param deviceName 设备名称。
/// @param gpioIndex GPIO 编号。
/// @return GPIO 电平值。
static Future<GpioValue> getGpioVal(
String deviceName,
int gpioIndex,
) {
return Ch34Platform.instance.getGpioVal(deviceName, gpioIndex);
}
/// ==================== 信号控制 ====================
/// 设置 DTR 信号。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param valid 是否有效(低电平有效)。
/// @return `true` 设置成功,`false` 设置失败。
static Future<bool> setDtr(
String deviceName,
int serialNumber,
bool valid,
) {
return Ch34Platform.instance.setDtr(deviceName, serialNumber, valid);
}
/// 设置 RTS 信号。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param valid 是否有效(低电平有效)。
/// @return `true` 设置成功,`false` 设置失败。
static Future<bool> setRts(
String deviceName,
int serialNumber,
bool valid,
) {
return Ch34Platform.instance.setRts(deviceName, serialNumber, valid);
}
/// 设置 Break 信号。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param valid 是否有效(低电平有效)。
/// @return `true` 设置成功,`false` 设置失败。
static Future<bool> setBreakSignal(
String deviceName,
int serialNumber,
bool valid,
) {
return Ch34Platform.instance.setBreakSignal(
deviceName,
serialNumber,
valid,
);
}
/// ==================== Modem 状态回调 ====================
/// 注册 Modem 控制信号状态回调。
///
/// @param deviceName 设备名称。
/// @param onModemStatus Modem 状态变化回调。
static Future<void> registerModemStatusCallback(
String deviceName,
void Function(ModemStatus status) onModemStatus,
) {
return Ch34Platform.instance.registerModemStatusCallback(
deviceName,
onModemStatus,
);
}
/// 移除 Modem 状态回调。
///
/// @param deviceName 设备名称。
static void removeModemStatusCallback(String deviceName) {
Ch34Platform.instance.removeModemStatusCallback(deviceName);
}
/// ==================== 错误查询 ====================
/// 查询串口错误状态。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param errorType 错误类型。
/// @return 该种错误出现的次数。
static Future<int> querySerialErrorCount(
String deviceName,
int serialNumber,
SerialErrorType errorType,
) {
return Ch34Platform.instance.querySerialErrorCount(
deviceName,
serialNumber,
errorType,
);
}
/// ==================== 全局配置 ====================
/// 设置读取超时时间。
///
/// 全局有效,应在 APP 初始化时调用。
///
/// @param timeout 超时时间(毫秒)。
static Future<void> setReadTimeout(int timeout) {
return Ch34Platform.instance.setReadTimeout(timeout);
}
/// 添加自定义硬件 VID/PID。
///
/// @param vid 硬件 VID。
/// @param pid 硬件 PID。
/// @param chipType 芯片类型(必填,如 "CH340"、"CH9102")。
static Future<void> addNewHardware(int vid, int pid, String chipType) {
return Ch34Platform.instance.addNewHardware(vid, pid, chipType);
}
/// 设置调试模式。
///
/// @param enabled `true` 开启,`false` 关闭。
static Future<void> setDebug(bool enabled) {
return Ch34Platform.instance.setDebug(enabled);
}
/// 返回当前是否处于调试模式。
///
/// @return `true` 处于调试模式,`false` 不处于。
static Future<bool> isDebugMode() {
return Ch34Platform.instance.isDebugMode();
}
}

View File

@@ -0,0 +1,589 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'ch34_platform_interface.dart';
import 'types/ch34_types.dart';
/// [Ch34Platform] 的 MethodChannel 实现。
///
/// 将平台接口方法映射到原生端 MethodChannel 调用,
/// 使用 EventChannel 处理数据回调、Modem 状态和 USB 插拔事件。
class MethodChannelCh34 extends Ch34Platform {
/// 注册默认实例。
static void registerDefault() {
Ch34Platform.instance = MethodChannelCh34._();
}
MethodChannelCh34._();
/// MethodChannel 名称。
@visibleForTesting
final methodChannel = const MethodChannel('ch34');
/// 数据 EventChannel 名称。
@visibleForTesting
final dataEventChannel = const EventChannel('ch34/data');
/// Modem 状态 EventChannel 名称。
@visibleForTesting
final modemEventChannel = const EventChannel('ch34/modem');
/// USB 状态 EventChannel 名称。
@visibleForTesting
final usbStateEventChannel = const EventChannel('ch34/usb_state');
// ==================== 事件流订阅 ====================
StreamSubscription? _dataSubscription;
StreamSubscription? _modemSubscription;
StreamSubscription? _usbStateSubscription;
void Function(String deviceName, bool connected)? _usbStateCallback;
void Function(Uint8List data)? _dataCallback;
void Function(ModemStatus status)? _modemCallback;
// ==================== 基础方法 ====================
@override
Future<String?> getPlatformVersion() async {
final version =
await methodChannel.invokeMethod<String>('getPlatformVersion');
return version;
}
// ==================== 设备枚举与识别 ====================
@override
Future<List<UsbDeviceInfo>> enumDevice() async {
final result = await methodChannel.invokeMethod<List>('enumDevice');
if (result == null) return [];
return result
.map((e) => UsbDeviceInfo.fromMap(e as Map<dynamic, dynamic>))
.toList();
}
@override
Future<String?> getChipType(String deviceName) async {
return await methodChannel.invokeMethod<String>(
'getChipType',
{'deviceName': deviceName},
);
}
// ==================== 设备打开与权限 ====================
@override
Future<bool> openDevice(String deviceName) async {
final result = await methodChannel.invokeMethod<bool>(
'openDevice',
{'deviceName': deviceName},
);
return result ?? false;
}
@override
Future<bool> requestPermission(String deviceName) async {
final result = await methodChannel.invokeMethod<bool>(
'requestPermission',
{'deviceName': deviceName},
);
return result ?? false;
}
// ==================== USB 状态监听 ====================
@override
void setUsbStateListener(
void Function(String deviceName, bool connected) onStateChanged) {
_usbStateCallback = onStateChanged;
_usbStateSubscription?.cancel();
_usbStateSubscription = usbStateEventChannel
.receiveBroadcastStream()
.listen(
(event) {
final map = event as Map<dynamic, dynamic>;
final deviceName = map['deviceName'] as String;
final connected = map['connected'] as bool;
_usbStateCallback?.call(deviceName, connected);
},
onError: (error) {
debugPrint('CH34 USB state error: $error');
},
);
}
@override
void removeUsbStateListener() {
_usbStateSubscription?.cancel();
_usbStateSubscription = null;
_usbStateCallback = null;
}
// ==================== 串口信息 ====================
@override
Future<int> getSerialCount(String deviceName) async {
final result = await methodChannel.invokeMethod<int>(
'getSerialCount',
{'deviceName': deviceName},
);
return result ?? -1;
}
@override
Future<int> getSerialBaud(String deviceName, int serialNumber) async {
final result = await methodChannel.invokeMethod<int>(
'getSerialBaud',
{
'deviceName': deviceName,
'serialNumber': serialNumber,
},
);
return result ?? -1;
}
@override
Future<ChipMasterFrequency> getChipMasterFrequency(String deviceName) async {
final result = await methodChannel.invokeMethod<Map>(
'getChipMasterFrequency',
{'deviceName': deviceName},
);
if (result == null) {
throw const Ch34Exception('Failed to get chip master frequency');
}
return ChipMasterFrequency.fromMap(result);
}
// ==================== 串口参数设置 ====================
@override
Future<bool> enableSerial(
String deviceName,
int serialNumber,
bool enable,
) async {
final result = await methodChannel.invokeMethod<bool>(
'enableSerial',
{
'deviceName': deviceName,
'serialNumber': serialNumber,
'enable': enable,
},
);
return result ?? false;
}
@override
Future<bool> setSerialParameter(
String deviceName,
int serialNumber,
SerialParameter parameter,
) async {
final result = await methodChannel.invokeMethod<bool>(
'setSerialParameter',
{
'deviceName': deviceName,
'serialNumber': serialNumber,
...parameter.toMap(),
},
);
return result ?? false;
}
// ==================== 数据读写 ====================
@override
Future<int> writeData(
String deviceName,
int serialNumber,
Uint8List data, {
int timeout = 0,
}) async {
final result = await methodChannel.invokeMethod<int>(
'writeData',
{
'deviceName': deviceName,
'serialNumber': serialNumber,
'data': data,
'timeout': timeout,
},
);
return result ?? 0;
}
@override
Future<void> asyncWriteData(
String deviceName,
int serialNumber,
Uint8List data,
) async {
await methodChannel.invokeMethod(
'asyncWriteData',
{
'deviceName': deviceName,
'serialNumber': serialNumber,
'data': data,
},
);
}
@override
Future<Uint8List> readData(String deviceName, int serialNumber) async {
final result = await methodChannel.invokeMethod<Uint8List>(
'readData',
{
'deviceName': deviceName,
'serialNumber': serialNumber,
},
);
return result ?? Uint8List(0);
}
@override
Future<Uint8List> readDataWithTimeout(
String deviceName,
int serialNumber,
int vTime,
int vMin,
) async {
final result = await methodChannel.invokeMethod<Uint8List>(
'readDataWithTimeout',
{
'deviceName': deviceName,
'serialNumber': serialNumber,
'vTime': vTime,
'vMin': vMin,
},
);
return result ?? Uint8List(0);
}
@override
Future<void> registerDataCallback(
String deviceName,
int serialNumber,
void Function(Uint8List data) onData,
) async {
_dataCallback = onData;
await _cancelDataSubscription();
_dataSubscription = dataEventChannel
.receiveBroadcastStream({'deviceName': deviceName})
.listen(
(event) {
if (event is Uint8List) {
_dataCallback?.call(event);
}
},
onError: (error) {
debugPrint('CH34 data callback error: $error');
},
);
await methodChannel.invokeMethod('registerDataCallback', {
'deviceName': deviceName,
'serialNumber': serialNumber,
});
}
@override
void removeDataCallback(String deviceName) {
_cancelDataSubscription();
methodChannel.invokeMethod('removeDataCallback', {
'deviceName': deviceName,
});
}
Future<void> _cancelDataSubscription() async {
await _dataSubscription?.cancel();
_dataSubscription = null;
_dataCallback = null;
}
// ==================== 连接状态 ====================
@override
Future<bool> isConnected(String deviceName) async {
final result = await methodChannel.invokeMethod<bool>(
'isConnected',
{'deviceName': deviceName},
);
return result ?? false;
}
@override
Future<List<String>> getConnectedDevices() async {
final result = await methodChannel.invokeMethod<List>(
'getConnectedDevices',
);
if (result == null) return [];
return result.map((e) => e.toString()).toList();
}
// ==================== 断开与关闭 ====================
@override
Future<void> disconnect(String deviceName) async {
await methodChannel.invokeMethod(
'disconnect',
{'deviceName': deviceName},
);
}
@override
Future<void> close() async {
// 清理所有订阅
await _cancelDataSubscription();
await _modemSubscription?.cancel();
_modemSubscription = null;
_modemCallback = null;
_usbStateSubscription?.cancel();
_usbStateSubscription = null;
_usbStateCallback = null;
await methodChannel.invokeMethod('close');
}
// ==================== GPIO 功能 ====================
@override
Future<bool> isSupportGpio(String deviceName) async {
final result = await methodChannel.invokeMethod<bool>(
'isSupportGpio',
{'deviceName': deviceName},
);
return result ?? false;
}
@override
Future<int> queryGpioCount(String deviceName) async {
final result = await methodChannel.invokeMethod<int>(
'queryGpioCount',
{'deviceName': deviceName},
);
return result ?? 0;
}
@override
Future<GpioStatus> queryGpioStatus(String deviceName, int gpioIndex) async {
final result = await methodChannel.invokeMethod<Map>(
'queryGpioStatus',
{
'deviceName': deviceName,
'gpioIndex': gpioIndex,
},
);
if (result == null) {
throw const Ch34Exception('Failed to query GPIO status');
}
return GpioStatus.fromMap(result);
}
@override
Future<List<GpioStatus>> queryAllGpioStatus(String deviceName) async {
final result = await methodChannel.invokeMethod<List>(
'queryAllGpioStatus',
{'deviceName': deviceName},
);
if (result == null) return [];
return result
.map((e) => GpioStatus.fromMap(e as Map<dynamic, dynamic>))
.toList();
}
@override
Future<bool> enableGpio(
String deviceName,
int gpioIndex,
bool enable,
GpioDirection direction,
) async {
final result = await methodChannel.invokeMethod<bool>(
'enableGpio',
{
'deviceName': deviceName,
'gpioIndex': gpioIndex,
'enable': enable,
'direction': direction.index,
},
);
return result ?? false;
}
@override
Future<bool> setGpioVal(
String deviceName,
int gpioIndex,
GpioValue value,
) async {
final result = await methodChannel.invokeMethod<bool>(
'setGpioVal',
{
'deviceName': deviceName,
'gpioIndex': gpioIndex,
'value': value.index,
},
);
return result ?? false;
}
@override
Future<GpioValue> getGpioVal(String deviceName, int gpioIndex) async {
final result = await methodChannel.invokeMethod<int>(
'getGpioVal',
{
'deviceName': deviceName,
'gpioIndex': gpioIndex,
},
);
if (result == null) {
throw const Ch34Exception('Failed to get GPIO value');
}
return GpioValue.values[result];
}
// ==================== 信号控制 ====================
@override
Future<bool> setDtr(String deviceName, int serialNumber, bool valid) async {
final result = await methodChannel.invokeMethod<bool>(
'setDtr',
{
'deviceName': deviceName,
'serialNumber': serialNumber,
'valid': valid,
},
);
return result ?? false;
}
@override
Future<bool> setRts(String deviceName, int serialNumber, bool valid) async {
final result = await methodChannel.invokeMethod<bool>(
'setRts',
{
'deviceName': deviceName,
'serialNumber': serialNumber,
'valid': valid,
},
);
return result ?? false;
}
@override
Future<bool> setBreakSignal(
String deviceName, int serialNumber, bool valid) async {
final result = await methodChannel.invokeMethod<bool>(
'setBreak',
{
'deviceName': deviceName,
'serialNumber': serialNumber,
'valid': valid,
},
);
return result ?? false;
}
// ==================== Modem 状态回调 ====================
@override
Future<void> registerModemStatusCallback(
String deviceName,
void Function(ModemStatus status) onModemStatus,
) async {
_modemCallback = onModemStatus;
await _modemSubscription?.cancel();
_modemSubscription = modemEventChannel
.receiveBroadcastStream({'deviceName': deviceName})
.listen(
(event) {
final map = event as Map<dynamic, dynamic>;
debugPrint('CH34 modem event received: $map');
_modemCallback?.call(ModemStatus.fromMap(map));
},
onError: (error) {
debugPrint('CH34 modem callback error: $error');
},
);
await methodChannel.invokeMethod('registerModemStatusCallback', {
'deviceName': deviceName,
});
}
@override
void removeModemStatusCallback(String deviceName) {
_modemSubscription?.cancel();
_modemSubscription = null;
_modemCallback = null;
methodChannel.invokeMethod('removeModemStatusCallback', {
'deviceName': deviceName,
});
}
// ==================== 错误查询 ====================
@override
Future<int> querySerialErrorCount(
String deviceName,
int serialNumber,
SerialErrorType errorType,
) async {
final result = await methodChannel.invokeMethod<int>(
'querySerialErrorCount',
{
'deviceName': deviceName,
'serialNumber': serialNumber,
'errorType': errorType.toNativeString(),
},
);
return result ?? 0;
}
// ==================== 全局配置 ====================
@override
Future<void> setReadTimeout(int timeout) async {
await methodChannel.invokeMethod(
'setReadTimeout',
{'timeout': timeout},
);
}
@override
Future<void> addNewHardware(int vid, int pid, String chipType) async {
await methodChannel.invokeMethod(
'addNewHardware',
{
'vid': vid,
'pid': pid,
'chipType': chipType,
},
);
}
@override
Future<void> setDebug(bool enabled) async {
await methodChannel.invokeMethod(
'setDebug',
{'enabled': enabled},
);
}
@override
Future<bool> isDebugMode() async {
final result =
await methodChannel.invokeMethod<bool>('isDebugMode');
return result ?? false;
}
}
/// CH34 插件自定义异常。
class Ch34Exception implements Exception {
const Ch34Exception(this.message);
/// 异常描述。
final String message;
@override
String toString() => 'Ch34Exception: $message';
}

View File

@@ -0,0 +1,418 @@
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'types/ch34_types.dart';
/// CH34X USB 转串口插件的抽象平台接口。
///
/// 定义所有 WCH WCHUARTManager API 的方法签名。
/// 具体实现由 `MethodChannelCh34` 提供。
abstract class Ch34Platform extends PlatformInterface {
/// Constructs a Ch34Platform.
Ch34Platform() : super(token: _token);
static final Object _token = Object();
static Ch34Platform? _instance;
/// The default instance of [Ch34Platform] to use.
///
/// Defaults to [MethodChannelCh34].
/// 注意: 需要在应用初始化时调用 [Ch34Platform.registerDefaultInstance] 注册默认实例。
static Ch34Platform get instance {
if (_instance == null) {
throw StateError(
'Ch34Platform.instance has not been initialized. '
'Ensure the plugin is properly registered.',
);
}
return _instance!;
}
/// 注册默认平台实例。
///
/// 通常在插件初始化时由 [MethodChannelCh34] 调用。
static void registerDefaultInstance(Ch34Platform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
/// Platform-specific implementations should set this with their own
/// platform-specific class that extends [Ch34Platform] when they register
/// themselves.
static set instance(Ch34Platform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
/// ==================== 基础方法 ====================
/// 获取平台版本。
///
/// @return 平台版本字符串。
Future<String?> getPlatformVersion();
/// ==================== 设备枚举与识别 ====================
/// 枚举当前所有可用的 USB 设备。
///
/// 返回设备信息列表,包含 VID/PID、串口数量等。
///
/// @return 可用 USB 设备列表。
/// @throws Ch34Exception 如果枚举失败。
Future<List<UsbDeviceInfo>> enumDevice();
/// 获取该 UsbDevice 的芯片型号。
///
/// @param deviceName 设备名称(由 [enumDevice] 返回的 deviceName
/// @return 芯片型号字符串。如果为 null表示无法识别。
Future<String?> getChipType(String deviceName);
/// ==================== 设备打开与权限 ====================
/// 打开 USB 设备。
///
/// 打开设备后可以进行串口通信。
///
/// @param deviceName 设备名称。
/// @return `true` 成功,`false` 失败。
Future<bool> openDevice(String deviceName);
/// 申请 USB 设备的权限。
///
/// @param deviceName 设备名称。
/// @return `true` 已授权,`false` 被拒绝。
Future<bool> requestPermission(String deviceName);
/// ==================== USB 状态监听 ====================
/// 注册 USB 设备插拔状态监听。
///
/// 通过 EventChannel 监听设备的连接和断开事件。
///
/// @param onStateChanged 状态变化回调,参数为设备名称和是否已连接。
void setUsbStateListener(void Function(String deviceName, bool connected) onStateChanged);
/// 移除 USB 状态监听。
void removeUsbStateListener();
/// ==================== 串口信息 ====================
/// 获取设备的串口数目。
///
/// @param deviceName 设备名称。
/// @return 串口数目;如果为 -1说明读取芯片型号失败。
Future<int> getSerialCount(String deviceName);
/// 获取串口波特率。
///
/// 实际仅针对 CH9114 系列有效,其他类型设备无需调用。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @return 大于 0 表示串口波特率,小于 0 表示出错。
Future<int> getSerialBaud(String deviceName, int serialNumber);
/// 获取芯片主频。
///
/// 实际仅针对 CH9114 系列有效,其他类型设备无需调用。
///
/// @param deviceName 设备名称。
/// @return 芯片主频信息对象。
Future<ChipMasterFrequency> getChipMasterFrequency(String deviceName);
/// 打开或关闭串口。
///
/// 实际仅针对 CH9114 系列有效,其他类型设备无需调用。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param enable `true` 打开,`false` 关闭。
/// @return `true` 设置成功,`false` 设置失败。
/// @throws Ch34Exception 如果操作失败。
Future<bool> enableSerial(String deviceName, int serialNumber, bool enable);
/// ==================== 串口参数设置 ====================
/// 设置串口参数。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号(从 0 开始)。
/// @param parameter 串口参数配置。
/// @return `true` 设置成功,`false` 设置失败。
/// @throws Ch34Exception 如果参数无效或设备未打开。
Future<bool> setSerialParameter(
String deviceName,
int serialNumber,
SerialParameter parameter,
);
/// ==================== 数据读写 ====================
/// 发送串口数据(同步发送)。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param data 要发送的数据。
/// @param timeout 超时时间毫秒0 表示不超时。
/// @return 实际发送的字节数。
/// @throws Ch34Exception 如果发送失败。
Future<int> writeData(
String deviceName,
int serialNumber,
Uint8List data, {
int timeout = 0,
});
/// 发送串口数据(异步发送)。
///
/// 将数据加入缓存持续发送,不返回状态和结果。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param data 要发送的数据。
/// @throws Ch34Exception 如果发送失败。
Future<void> asyncWriteData(
String deviceName,
int serialNumber,
Uint8List data,
);
/// 阻塞读取串口数据。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @return 读取到的数据。
/// @throws Ch34Exception 如果读取失败。
Future<Uint8List> readData(String deviceName, int serialNumber);
/// 主动读取串口数据(带超时参数)。
///
/// 读取行为说明:
/// - 当 vTime>0vMin>0 时:读取将保持阻塞直到读取到第一个字符,
/// 读到了第一个字符之后开始计时,此后若时间到了 vTime 或者时间未到
/// 但已读够了 vMin 个字符则会返回。
/// - 当 vTime>0vMin=0 时:读取读到数据则立即返回,否则将为每个字符
/// 最多等待 vTime 时间。
/// - 当 vTime=0vMin>0 时:读取一直阻塞,直到读到 vMin 个字符后立即返回。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param vTime 等待时间(毫秒)。
/// @param vMin 读取的最小字节数。
/// @return 读取到的数据。
/// @throws Ch34Exception 如果读取失败。
Future<Uint8List> readDataWithTimeout(
String deviceName,
int serialNumber,
int vTime,
int vMin,
);
/// 注册串口数据回调。
///
/// 注册后数据会通过 EventChannel 自动推送,不需要主动调用 [readData]。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param onData 数据接收回调。
/// @throws Ch34Exception 如果注册失败。
Future<void> registerDataCallback(
String deviceName,
int serialNumber,
void Function(Uint8List data) onData,
);
/// 取消注册串口数据回调。
///
/// @param deviceName 设备名称。
void removeDataCallback(String deviceName);
/// ==================== 连接状态 ====================
/// 判断 USB 设备是否已经连接。
///
/// @param deviceName 设备名称。
/// @return `true` 已连接,`false` 未连接。
Future<bool> isConnected(String deviceName);
/// 获取当前已经打开的设备列表。
///
/// @return 已打开的设备名称列表。
Future<List<String>> getConnectedDevices();
/// ==================== 断开与关闭 ====================
/// 断开 USB 设备的连接。
///
/// @param deviceName 设备名称。
Future<void> disconnect(String deviceName);
/// 释放资源,关闭所有的串口设备。
Future<void> close();
/// ==================== GPIO 功能 ====================
/// 查询设备是否支持 GPIO 功能。
///
/// 应该在操作 GPIO 前调用。
///
/// @param deviceName 设备名称。
/// @return `true` 支持,`false` 不支持。
/// @throws Ch34Exception 如果查询失败。
Future<bool> isSupportGpio(String deviceName);
/// 查询该 USB 设备的 GPIO 数目。
///
/// @param deviceName 设备名称。
/// @return GPIO 数目。
/// @throws Ch34Exception 如果查询失败。
Future<int> queryGpioCount(String deviceName);
/// 查询该 USB 设备指定 GPIO 的状态。
///
/// @param deviceName 设备名称。
/// @param gpioIndex GPIO 编号(从 0 开始)。
/// @return GPIO 状态。
/// @throws Ch34Exception 如果查询失败。
Future<GpioStatus> queryGpioStatus(String deviceName, int gpioIndex);
/// 查询该 USB 设备的所有 GPIO 状态。
///
/// @param deviceName 设备名称。
/// @return 全部 GPIO 状态列表。
/// @throws Ch34Exception 如果查询失败。
Future<List<GpioStatus>> queryAllGpioStatus(String deviceName);
/// 使能指定 GPIO。
///
/// @param deviceName 设备名称。
/// @param gpioIndex GPIO 编号。
/// @param enable `true` 使能,`false` 关闭。
/// @param direction GPIO 方向。
/// @return `true` 使能成功,`false` 使能失败。
/// @throws Ch34Exception 如果操作失败。
Future<bool> enableGpio(
String deviceName,
int gpioIndex,
bool enable,
GpioDirection direction,
);
/// 设置指定 GPIO 的电平值。
///
/// @param deviceName 设备名称。
/// @param gpioIndex GPIO 编号。
/// @param value GPIO 电平值。
/// @return `true` 设置成功,`false` 设置失败。
/// @throws Ch34Exception 如果操作失败。
Future<bool> setGpioVal(
String deviceName,
int gpioIndex,
GpioValue value,
);
/// 获取指定 GPIO 的电平值。
///
/// @param deviceName 设备名称。
/// @param gpioIndex GPIO 编号。
/// @return GPIO 电平值。
/// @throws Ch34Exception 如果操作失败。
Future<GpioValue> getGpioVal(String deviceName, int gpioIndex);
/// ==================== 信号控制 ====================
/// 设置 DTR 信号。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param valid 是否有效(低电平有效)。
/// @return `true` 设置成功,`false` 设置失败。
/// @throws Ch34Exception 如果操作失败。
Future<bool> setDtr(String deviceName, int serialNumber, bool valid);
/// 设置 RTS 信号。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param valid 是否有效(低电平有效)。
/// @return `true` 设置成功,`false` 设置失败。
/// @throws Ch34Exception 如果操作失败。
Future<bool> setRts(String deviceName, int serialNumber, bool valid);
/// 设置 Break 信号。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param valid 是否有效(低电平有效)。
/// @return `true` 设置成功,`false` 设置失败。
/// @throws Ch34Exception 如果操作失败。
Future<bool> setBreakSignal(String deviceName, int serialNumber, bool valid);
/// ==================== Modem 状态回调 ====================
/// 注册 Modem 控制信号状态回调。
///
/// @param deviceName 设备名称。
/// @param onModemStatus Modem 状态变化回调。
/// @throws Ch34Exception 如果注册失败。
Future<void> registerModemStatusCallback(
String deviceName,
void Function(ModemStatus status) onModemStatus,
);
/// 移除 Modem 状态回调。
///
/// @param deviceName 设备名称。
void removeModemStatusCallback(String deviceName);
/// ==================== 错误查询 ====================
/// 查询串口错误状态。
///
/// @param deviceName 设备名称。
/// @param serialNumber 串口号。
/// @param errorType 错误类型。
/// @return 该种错误出现的次数。
/// @throws Ch34Exception 如果查询失败。
Future<int> querySerialErrorCount(
String deviceName,
int serialNumber,
SerialErrorType errorType,
);
/// ==================== 全局配置 ====================
/// 设置读取超时时间。
///
/// 默认为 0使用 USBRequest 异步读取;
/// 如果不为 0使用同步传输超时单位为毫秒。
/// 全局有效,应在 APP 初始化时调用。
///
/// @param timeout 超时时间(毫秒)。
Future<void> setReadTimeout(int timeout);
/// 添加自定义硬件 VID/PID 及芯片类型。
///
/// 当用户修改了硬件设备的 VID 和 PID 后,需要将修改后的值添加到库中。
///
/// @param vid 硬件 VID。
/// @param pid 硬件 PID。
/// @param chipType 芯片类型(必填,如 "CH340"、"CH9102")。
Future<void> addNewHardware(int vid, int pid, String chipType);
/// 设置调试模式。
///
/// 开启调试模式会打印日志,默认关闭。
/// 应在 APP 初始化时调用。
///
/// @param enabled `true` 开启,`false` 关闭。
Future<void> setDebug(bool enabled);
/// 返回当前是否处于调试模式。
///
/// @return `true` 处于调试模式,`false` 不处于。
Future<bool> isDebugMode();
}

View File

@@ -0,0 +1,359 @@
/// CH34X 插件的所有类型和枚举定义。
///
/// 对应 WCH WCHUARTManager API 文档中的参数类型。
library ch34_types;
/// 数据位
enum DataBits {
bits5(5),
bits6(6),
bits7(7),
bits8(8);
const DataBits(this.value);
final int value;
static DataBits fromValue(int value) {
return DataBits.values.firstWhere(
(e) => e.value == value,
orElse: () => DataBits.bits8,
);
}
}
/// 停止位
enum StopBits {
one(1),
two(2);
const StopBits(this.value);
final int value;
static StopBits fromValue(int value) {
return StopBits.values.firstWhere(
(e) => e.value == value,
orElse: () => StopBits.one,
);
}
}
/// 校验位
enum Parity {
none(0),
odd(1),
even(2),
mark(3),
space(4);
const Parity(this.value);
final int value;
static Parity fromValue(int value) {
return Parity.values.firstWhere(
(e) => e.value == value,
orElse: () => Parity.none,
);
}
}
/// GPIO 方向
enum GpioDirection {
inDir,
outDir;
}
/// GPIO 电平值
enum GpioValue {
low,
high;
}
/// GPIO 状态
class GpioStatus {
const GpioStatus({
required this.index,
required this.direction,
required this.value,
required this.enabled,
});
factory GpioStatus.fromMap(Map<dynamic, dynamic> map) {
return GpioStatus(
index: map['index'] as int,
direction:
GpioDirection.values[map['direction'] as int? ?? 0],
value: GpioValue.values[map['value'] as int? ?? 0],
enabled: map['enabled'] as bool? ?? false,
);
}
/// GPIO 编号(从 0 开始)
final int index;
/// 方向
final GpioDirection direction;
/// 电平值
final GpioValue value;
/// 是否已使能
final bool enabled;
Map<String, dynamic> toMap() {
return {
'index': index,
'direction': direction.index,
'value': value.index,
'enabled': enabled,
};
}
@override
String toString() {
return 'GpioStatus(index: $index, direction: $direction, '
'value: $value, enabled: $enabled)';
}
}
/// 串口参数
class SerialParameter {
const SerialParameter({
this.baud = 115200,
this.dataBits = DataBits.bits8,
this.stopBits = StopBits.one,
this.parity = Parity.none,
this.hardwareFlowControl = false,
});
factory SerialParameter.fromMap(Map<dynamic, dynamic> map) {
return SerialParameter(
baud: map['baud'] as int? ?? 115200,
dataBits: DataBits.fromValue(map['dataBits'] as int? ?? 8),
stopBits: StopBits.fromValue(map['stopBits'] as int? ?? 1),
parity: Parity.fromValue(map['parity'] as int? ?? 0),
hardwareFlowControl: map['hardwareFlowControl'] as bool? ?? false,
);
}
/// 波特率
final int baud;
/// 数据位
final DataBits dataBits;
/// 停止位
final StopBits stopBits;
/// 校验位
final Parity parity;
/// 硬件流控
final bool hardwareFlowControl;
Map<String, dynamic> toMap() {
return {
'baud': baud,
'dataBits': dataBits.value,
'stopBits': stopBits.value,
'parity': parity.value,
'hardwareFlowControl': hardwareFlowControl,
};
}
@override
String toString() {
return 'SerialParameter(baud: $baud, dataBits: $dataBits, '
'stopBits: $stopBits, parity: $parity, '
'hardwareFlowControl: $hardwareFlowControl)';
}
SerialParameter copyWith({
int? baud,
DataBits? dataBits,
StopBits? stopBits,
Parity? parity,
bool? hardwareFlowControl,
}) {
return SerialParameter(
baud: baud ?? this.baud,
dataBits: dataBits ?? this.dataBits,
stopBits: stopBits ?? this.stopBits,
parity: parity ?? this.parity,
hardwareFlowControl:
hardwareFlowControl ?? this.hardwareFlowControl,
);
}
}
/// 芯片主频信息
///
/// 对应 WCH API 4.12 节 `getChipMasterFrequency` 返回值。
class ChipMasterFrequency {
const ChipMasterFrequency({
required this.frequency,
required this.switchEnable,
required this.coStatus,
});
factory ChipMasterFrequency.fromMap(Map<dynamic, dynamic> map) {
return ChipMasterFrequency(
frequency: map['frequency'] as int? ?? 0,
switchEnable: map['switchEnable'] as bool? ?? false,
coStatus: map['coStatus'] as int? ?? 0,
);
}
/// 芯片主频Hz
final int frequency;
/// 是否允许切换主频
final bool switchEnable;
/// 晶振状态
final int coStatus;
Map<String, dynamic> toMap() {
return {
'frequency': frequency,
'switchEnable': switchEnable,
'coStatus': coStatus,
};
}
@override
String toString() {
return 'ChipMasterFrequency(frequency: $frequency Hz, '
'switchEnable: $switchEnable, coStatus: $coStatus)';
}
}
/// Modem 状态
class ModemStatus {
const ModemStatus({
this.cts = false,
this.dsr = false,
this.ri = false,
this.dcd = false,
});
factory ModemStatus.fromMap(Map<dynamic, dynamic> map) {
return ModemStatus(
cts: map['cts'] as bool? ?? false,
dsr: map['dsr'] as bool? ?? false,
ri: map['ri'] as bool? ?? false,
dcd: map['dcd'] as bool? ?? false,
);
}
/// Clear To Send
final bool cts;
/// Data Set Ready
final bool dsr;
/// Ring Indicator
final bool ri;
/// Data Carrier Detect
final bool dcd;
Map<String, dynamic> toMap() {
return {
'cts': cts,
'dsr': dsr,
'ri': ri,
'dcd': dcd,
};
}
@override
String toString() {
return 'ModemStatus(CTS: $cts, DSR: $dsr, RI: $ri, DCD: $dcd)';
}
}
/// 串口错误类型
///
/// 对应 WCH 原生库的 `SerialErrorType` 枚举(仅包含 FRAME/PARITY/OVERRUN 三种)。
enum SerialErrorType {
framingError,
parityError,
overrunError;
/// 转换为原生端字符串标识。
String toNativeString() {
switch (this) {
case SerialErrorType.framingError:
return 'SerialErrorType.FramingError';
case SerialErrorType.parityError:
return 'SerialErrorType.ParityError';
case SerialErrorType.overrunError:
return 'SerialErrorType.OverrunError';
}
}
}
/// USB 设备信息
class UsbDeviceInfo {
const UsbDeviceInfo({
required this.deviceName,
required this.productId,
required this.vendorId,
required this.serialCount,
this.chipType,
});
factory UsbDeviceInfo.fromMap(Map<dynamic, dynamic> map) {
return UsbDeviceInfo(
deviceName: map['deviceName'] as String,
productId: map['productId'] as int,
vendorId: map['vendorId'] as int,
serialCount: map['serialCount'] as int? ?? 1,
chipType: map['chipType'] as String?,
);
}
/// 设备名称
final String deviceName;
/// 产品 ID
final int productId;
/// 厂商 ID
final int vendorId;
/// 串口数量
final int serialCount;
/// 芯片型号(如果识别)
final String? chipType;
Map<String, dynamic> toMap() {
return {
'deviceName': deviceName,
'productId': productId,
'vendorId': vendorId,
'serialCount': serialCount,
'chipType': chipType,
};
}
@override
String toString() {
return 'UsbDeviceInfo(name: $deviceName, VID: 0x${vendorId.toRadixString(16).toUpperCase().padLeft(4, '0')}, '
'PID: 0x${productId.toRadixString(16).toUpperCase().padLeft(4, '0')}, '
'ports: $serialCount, chip: $chipType)';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is UsbDeviceInfo &&
other.deviceName == deviceName &&
other.productId == productId &&
other.vendorId == vendorId;
}
@override
int get hashCode => Object.hash(deviceName, productId, vendorId);
}