- 添加Ch934xMode和GpioDirection枚举类定义 - 实现GPIO方向控制相关方法(setGpioDir/queryGpioDirFromCache) - 添加GPIO使能控制(enableGpio)和数量/组数查询(getGpiocount/getGpiogroup) - 实现串口参数配置(setSerialParameter)包括波特率/数据位/停止位/校验位/流控 - 添加Break信号控制(setBreak)功能 - 实现当前串口模式查询(getCurrentMode) - 添加设备连接状态查询(isConnected)和已打开设备列表获取(getConnectedDevices) - 更新文档说明GPIO方向控制和串口参数配置用法 - 在示例应用中添加相关功能按钮和操作逻辑
335 lines
8.7 KiB
Dart
335 lines
8.7 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/services.dart';
|
|
|
|
import 'ch934x_serial_platform_interface.dart';
|
|
import 'src/models/models.dart';
|
|
|
|
/// MethodChannel 实现的 [Ch934xSerialPlatform]。
|
|
///
|
|
/// 在 Android 端通过同一 MethodChannel 与原生层通信,
|
|
/// 命名规则:方法名使用下划线小写,字段名使用驼峰式以便
|
|
/// 直接对应 Java 侧 Map 的 key。
|
|
class MethodChannelCh934xSerial extends Ch934xSerialPlatform {
|
|
/// 测试时可被替换的 MethodChannel。
|
|
@visibleForTesting
|
|
final MethodChannel methodChannel =
|
|
const MethodChannel('ch934x_serial');
|
|
|
|
/// 通知 Dart 侧的异常事件流,用于支持 `setExceptionCallback`。
|
|
final StreamController<Ch934xException> _exceptionController =
|
|
StreamController<Ch934xException>.broadcast();
|
|
|
|
/// 接收来自原生层主动调用的 `onException` 消息。
|
|
Future<void> _onNativeMethodCall(MethodCall call) async {
|
|
if (call.method == 'onException') {
|
|
final args = call.arguments;
|
|
if (args is Map) {
|
|
_exceptionController.add(
|
|
Ch934xException(
|
|
type: (args['type'] as int?) ?? Ch934xExceptionType.unknown,
|
|
message: args['message'] as String?,
|
|
cause: args['cause'] as String?,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
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},
|
|
);
|
|
}
|
|
|
|
@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<bool> setActivePort(int serialPortIndex) async {
|
|
if (serialPortIndex < 0) {
|
|
return false;
|
|
}
|
|
final result = await methodChannel.invokeMethod<bool>(
|
|
'setActivePort',
|
|
<String, Object>{'serialPortIndex': serialPortIndex},
|
|
);
|
|
return result ?? false;
|
|
}
|
|
|
|
@override
|
|
Future<bool> requestUsbPermission(int deviceId) async {
|
|
final result = await methodChannel.invokeMethod<bool>(
|
|
'requestUsbPermission',
|
|
<String, Object>{'deviceId': deviceId},
|
|
);
|
|
return result ?? false;
|
|
}
|
|
|
|
@override
|
|
Future<Uint8List> read(int length, {int? serialPortIndex}) async {
|
|
if (length <= 0) {
|
|
return Uint8List(0);
|
|
}
|
|
final args = <String, Object>{'length': length};
|
|
if (serialPortIndex != null) {
|
|
args['serialPortIndex'] = serialPortIndex;
|
|
}
|
|
final raw = await methodChannel.invokeMethod<Uint8List>('read', args);
|
|
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 {
|
|
// 注册接收原生层主动推送的 `onException` 事件。
|
|
methodChannel.setMethodCallHandler(_onNativeMethodCall);
|
|
_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),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<bool> setBreak(bool valid) {
|
|
return methodChannel.invokeMethod<bool>(
|
|
'setBreak',
|
|
<String, Object>{'valid': valid},
|
|
).then((v) => v ?? false);
|
|
}
|
|
|
|
@override
|
|
Future<int> getCurrentMode() {
|
|
return methodChannel.invokeMethod<int>('getCurrentMode')
|
|
.then((v) => v ?? -1);
|
|
}
|
|
|
|
@override
|
|
Future<int> getGpiocount(int deviceId) {
|
|
return methodChannel.invokeMethod<int>(
|
|
'getGPIOCount',
|
|
<String, Object>{'deviceId': deviceId},
|
|
).then((v) => v ?? -1);
|
|
}
|
|
|
|
@override
|
|
Future<int> getGpiogroup(int deviceId) {
|
|
return methodChannel.invokeMethod<int>(
|
|
'getGPIOGroup',
|
|
<String, Object>{'deviceId': deviceId},
|
|
).then((v) => v ?? -1);
|
|
}
|
|
|
|
@override
|
|
Future<bool> enableGpio({
|
|
required int chipType,
|
|
required int gpioGroup,
|
|
required int enable,
|
|
}) {
|
|
return methodChannel.invokeMethod<bool>(
|
|
'enableGPIO',
|
|
<String, Object>{
|
|
'chipType': chipType,
|
|
'gpioGroup': gpioGroup,
|
|
'enable': enable,
|
|
},
|
|
).then((v) => v ?? false);
|
|
}
|
|
|
|
@override
|
|
Future<bool> setGpioDir({
|
|
required int gpioGroup,
|
|
required int gpioNumber,
|
|
required int dir,
|
|
}) {
|
|
return methodChannel.invokeMethod<bool>(
|
|
'setGPIODir',
|
|
<String, Object>{
|
|
'gpioGroup': gpioGroup,
|
|
'gpioNumber': gpioNumber,
|
|
'dir': dir,
|
|
},
|
|
).then((v) => v ?? false);
|
|
}
|
|
|
|
@override
|
|
Future<int> queryGpioDirFromCache({
|
|
required int gpioGroup,
|
|
required int gpioNumber,
|
|
}) {
|
|
return methodChannel.invokeMethod<int>(
|
|
'queryGPIODirFromCache',
|
|
<String, Object>{
|
|
'gpioGroup': gpioGroup,
|
|
'gpioNumber': gpioNumber,
|
|
},
|
|
).then((v) => v ?? -1);
|
|
}
|
|
|
|
@override
|
|
Future<bool> isConnected(int deviceId) {
|
|
return methodChannel.invokeMethod<bool>(
|
|
'isConnected',
|
|
<String, Object>{'deviceId': deviceId},
|
|
).then((v) => v ?? false);
|
|
}
|
|
|
|
@override
|
|
Future<List<int>> getConnectedDevices() async {
|
|
final raw = await methodChannel.invokeMethod<List<dynamic>>('getConnectedDevices');
|
|
if (raw == null) return const <int>[];
|
|
return raw.whereType<int>().toList(growable: false);
|
|
}
|
|
|
|
@override
|
|
Future<bool> setSerialParameter({
|
|
required int baud,
|
|
int dataBit = 8,
|
|
int stopBit = 1,
|
|
int parityBit = 0,
|
|
bool flow = false,
|
|
}) {
|
|
return methodChannel.invokeMethod<bool>(
|
|
'setSerialParameter',
|
|
<String, Object>{
|
|
'baud': baud,
|
|
'dataBit': dataBit,
|
|
'stopBit': stopBit,
|
|
'parityBit': parityBit,
|
|
'flow': flow,
|
|
},
|
|
).then((v) => v ?? false);
|
|
}
|
|
|
|
/// 释放内部资源,通常仅在测试或热重载场景下使用。
|
|
@visibleForTesting
|
|
void dispose() {
|
|
_exceptionController.close();
|
|
}
|
|
}
|