1
This commit is contained in:
9
android/.gitignore
vendored
Normal file
9
android/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.cxx
|
||||
59
android/build.gradle
Normal file
59
android/build.gradle
Normal file
@@ -0,0 +1,59 @@
|
||||
group = "com.example.ch34"
|
||||
version = "1.0"
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:7.3.0")
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
flatDir{
|
||||
dirs project(':ch34').file('libs')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: "com.android.library"
|
||||
|
||||
android {
|
||||
if (project.android.hasProperty("namespace")) {
|
||||
namespace = "com.example.ch34"
|
||||
}
|
||||
|
||||
compileSdk = 34
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
testImplementation("org.mockito:mockito-core:5.0.0")
|
||||
|
||||
implementation(name: 'CH34XUARTDriver',ext: 'jar')
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests.all {
|
||||
testLogging {
|
||||
events "passed", "skipped", "failed", "standardOut", "standardError"
|
||||
outputs.upToDateWhen {false}
|
||||
showStandardStreams = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
android/libs/CH34XUartDriver.jar
Normal file
BIN
android/libs/CH34XUartDriver.jar
Normal file
Binary file not shown.
1
android/settings.gradle
Normal file
1
android/settings.gradle
Normal file
@@ -0,0 +1 @@
|
||||
rootProject.name = 'ch34'
|
||||
7
android/src/main/AndroidManifest.xml
Normal file
7
android/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.ch34">
|
||||
|
||||
<!-- USB Host 功能声明 -->
|
||||
<uses-feature android:name="android.hardware.usb.host" android:required="false" />
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.example.ch34;
|
||||
|
||||
import io.flutter.plugin.common.EventChannel;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import cn.wch.uartlib.callback.IDataCallback;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 数据 EventChannel StreamHandler。
|
||||
*
|
||||
* 负责将 WCH IDataCallback 接收到的数据通过 EventChannel 发送给 Flutter 端。
|
||||
*/
|
||||
public class Ch34DataStreamHandler implements EventChannel.StreamHandler {
|
||||
|
||||
private static final String TAG = "Ch34DataStream";
|
||||
|
||||
private EventChannel.EventSink eventSink;
|
||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
/** 设备名 -> WCH 回调实例 */
|
||||
private final Map<String, IDataCallback> wchCallbacks = new HashMap<>();
|
||||
|
||||
/** 数据回调接口,用于桥接 WCH 回调到 EventChannel */
|
||||
public interface DataBridge {
|
||||
void onData(int serialNumber, byte[] buffer, int length);
|
||||
}
|
||||
|
||||
/** 设备名 -> 数据桥接回调 */
|
||||
private final Map<String, DataBridge> dataBridges = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void onListen(Object arguments, EventChannel.EventSink events) {
|
||||
this.eventSink = events;
|
||||
Log.d(TAG, "Data stream listening");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(Object arguments) {
|
||||
this.eventSink = null;
|
||||
Log.d(TAG, "Data stream cancelled");
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送数据到 Flutter 端。
|
||||
*
|
||||
* 会在主线程上执行 eventSink.success(),确保线程安全。
|
||||
*
|
||||
* @param data 字节数组数据。
|
||||
*/
|
||||
public void sendData(byte[] data) {
|
||||
mainHandler.post(() -> {
|
||||
EventChannel.EventSink sink = eventSink;
|
||||
if (sink != null) {
|
||||
sink.success(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册设备的数据回调。
|
||||
*
|
||||
* @param device USB 设备。
|
||||
* @param deviceName 设备名称。
|
||||
* @param serialNumber 串口号。
|
||||
* @param bridge 数据桥接回调。
|
||||
*/
|
||||
public void registerCallback(UsbDevice device, String deviceName, int serialNumber, DataBridge bridge) {
|
||||
dataBridges.put(deviceName, bridge);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备的 WCH 回调实例。
|
||||
*
|
||||
* @param deviceName 设备名称。
|
||||
* @return WCH IDataCallback 实例。
|
||||
*/
|
||||
public IDataCallback getWchCallback(String deviceName) {
|
||||
if (wchCallbacks.containsKey(deviceName)) {
|
||||
return wchCallbacks.get(deviceName);
|
||||
}
|
||||
|
||||
IDataCallback callback = new IDataCallback() {
|
||||
@Override
|
||||
public void onData(int serialNumber, byte[] buffer, int length) {
|
||||
DataBridge bridge = dataBridges.get(deviceName);
|
||||
if (bridge != null) {
|
||||
bridge.onData(serialNumber, buffer, length);
|
||||
}
|
||||
}
|
||||
};
|
||||
wchCallbacks.put(deviceName, callback);
|
||||
return callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除设备的数据回调。
|
||||
*
|
||||
* @param deviceName 设备名称。
|
||||
*/
|
||||
public void removeCallback(String deviceName) {
|
||||
wchCallbacks.remove(deviceName);
|
||||
dataBridges.remove(deviceName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.example.ch34;
|
||||
|
||||
import io.flutter.plugin.common.EventChannel;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Modem 状态 EventChannel StreamHandler。
|
||||
*
|
||||
* 负责将 Modem 状态变化通过 EventChannel 发送给 Flutter 端。
|
||||
*/
|
||||
public class Ch34ModemStreamHandler implements EventChannel.StreamHandler {
|
||||
|
||||
private static final String TAG = "Ch34ModemStream";
|
||||
|
||||
private EventChannel.EventSink eventSink;
|
||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
private boolean isListening = false;
|
||||
|
||||
@Override
|
||||
public void onListen(Object arguments, EventChannel.EventSink events) {
|
||||
this.eventSink = events;
|
||||
Log.d(TAG, "Modem stream listening");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(Object arguments) {
|
||||
this.eventSink = null;
|
||||
this.isListening = false;
|
||||
Log.d(TAG, "Modem stream cancelled");
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始监听 Modem 状态。
|
||||
*/
|
||||
public void startListening() {
|
||||
this.isListening = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止监听 Modem 状态。
|
||||
*/
|
||||
public void stopListening() {
|
||||
this.isListening = false;
|
||||
this.eventSink = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送 Modem 状态到 Flutter 端。
|
||||
*
|
||||
* 会在主线程上执行 eventSink.success(),确保线程安全。
|
||||
*
|
||||
* @param status 状态 Map,包含 cts/dsr/ri/dcd 布尔值。
|
||||
*/
|
||||
public void sendStatus(Map<String, Object> status) {
|
||||
if (!isListening) return;
|
||||
mainHandler.post(() -> {
|
||||
EventChannel.EventSink sink = eventSink;
|
||||
if (sink != null) {
|
||||
sink.success(status);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
892
android/src/main/java/com/example/ch34/Ch34Plugin.java
Normal file
892
android/src/main/java/com/example/ch34/Ch34Plugin.java
Normal file
@@ -0,0 +1,892 @@
|
||||
package com.example.ch34;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||
import io.flutter.plugin.common.EventChannel;
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||
import io.flutter.plugin.common.MethodChannel.Result;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import cn.wch.uartlib.WCHUARTManager;
|
||||
import cn.wch.uartlib.chip.type.ChipType2;
|
||||
import cn.wch.uartlib.callback.IDataCallback;
|
||||
import cn.wch.uartlib.callback.IModemStatus;
|
||||
import cn.wch.uartlib.exception.ChipException;
|
||||
import cn.wch.uartlib.exception.NoPermissionException;
|
||||
import cn.wch.uartlib.exception.UartLibException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* CH34X USB 转串口驱动 Flutter 插件。
|
||||
*
|
||||
* 实现 WCH WCHUARTManager 文档 4.3–4.38 共 36 个公开 API(getInstance/init 由内部自动调用)
|
||||
* 的 MethodChannel 映射,并通过 3 个 EventChannel 提供数据回调、Modem 状态和 USB 插拔事件流。
|
||||
*/
|
||||
public class Ch34Plugin implements FlutterPlugin, MethodCallHandler {
|
||||
|
||||
private static final String TAG = "Ch34Plugin";
|
||||
|
||||
private MethodChannel channel;
|
||||
private Context context;
|
||||
|
||||
private EventChannel dataEventChannel;
|
||||
private EventChannel modemEventChannel;
|
||||
private EventChannel usbStateEventChannel;
|
||||
|
||||
private Ch34DataStreamHandler dataStreamHandler;
|
||||
private Ch34ModemStreamHandler modemStreamHandler;
|
||||
private Ch34UsbStateStreamHandler usbStateStreamHandler;
|
||||
|
||||
/** 已打开的设备映射:deviceName -> UsbDevice */
|
||||
private final Map<String, UsbDevice> openedDevices = new HashMap<>();
|
||||
|
||||
/** 全局 WCHUARTManager 实例 */
|
||||
private WCHUARTManager manager;
|
||||
|
||||
@Override
|
||||
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
|
||||
channel = new MethodChannel(binding.getBinaryMessenger(), "ch34");
|
||||
channel.setMethodCallHandler(this);
|
||||
|
||||
context = binding.getApplicationContext();
|
||||
|
||||
dataEventChannel = new EventChannel(binding.getBinaryMessenger(), "ch34/data");
|
||||
dataStreamHandler = new Ch34DataStreamHandler();
|
||||
dataEventChannel.setStreamHandler(dataStreamHandler);
|
||||
|
||||
modemEventChannel = new EventChannel(binding.getBinaryMessenger(), "ch34/modem");
|
||||
modemStreamHandler = new Ch34ModemStreamHandler();
|
||||
modemEventChannel.setStreamHandler(modemStreamHandler);
|
||||
|
||||
usbStateEventChannel = new EventChannel(binding.getBinaryMessenger(), "ch34/usb_state");
|
||||
usbStateStreamHandler = new Ch34UsbStateStreamHandler();
|
||||
usbStateEventChannel.setStreamHandler(usbStateStreamHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
|
||||
try {
|
||||
switch (call.method) {
|
||||
case "getPlatformVersion":
|
||||
result.success("Android " + android.os.Build.VERSION.RELEASE);
|
||||
break;
|
||||
case "enumDevice":
|
||||
enumDevice(result);
|
||||
break;
|
||||
case "getChipType":
|
||||
getChipType(call, result);
|
||||
break;
|
||||
case "openDevice":
|
||||
openDevice(call, result);
|
||||
break;
|
||||
case "requestPermission":
|
||||
requestPermission(call, result);
|
||||
break;
|
||||
case "getSerialCount":
|
||||
getSerialCount(call, result);
|
||||
break;
|
||||
case "getSerialBaud":
|
||||
getSerialBaud(call, result);
|
||||
break;
|
||||
case "getChipMasterFrequency":
|
||||
getChipMasterFrequency(call, result);
|
||||
break;
|
||||
case "enableSerial":
|
||||
enableSerial(call, result);
|
||||
break;
|
||||
case "setSerialParameter":
|
||||
setSerialParameter(call, result);
|
||||
break;
|
||||
case "writeData":
|
||||
writeData(call, result);
|
||||
break;
|
||||
case "asyncWriteData":
|
||||
asyncWriteData(call, result);
|
||||
break;
|
||||
case "readData":
|
||||
readData(call, result);
|
||||
break;
|
||||
case "readDataWithTimeout":
|
||||
readDataWithTimeout(call, result);
|
||||
break;
|
||||
case "registerDataCallback":
|
||||
registerDataCallback(call, result);
|
||||
break;
|
||||
case "removeDataCallback":
|
||||
removeDataCallback(call, result);
|
||||
break;
|
||||
case "isConnected":
|
||||
isConnected(call, result);
|
||||
break;
|
||||
case "getConnectedDevices":
|
||||
getConnectedDevices(result);
|
||||
break;
|
||||
case "disconnect":
|
||||
disconnect(call, result);
|
||||
break;
|
||||
case "close":
|
||||
close(result);
|
||||
break;
|
||||
case "isSupportGpio":
|
||||
isSupportGpio(call, result);
|
||||
break;
|
||||
case "queryGpioCount":
|
||||
queryGpioCount(call, result);
|
||||
break;
|
||||
case "queryGpioStatus":
|
||||
queryGpioStatus(call, result);
|
||||
break;
|
||||
case "queryAllGpioStatus":
|
||||
queryAllGpioStatus(call, result);
|
||||
break;
|
||||
case "enableGpio":
|
||||
enableGpio(call, result);
|
||||
break;
|
||||
case "setGpioVal":
|
||||
setGpioVal(call, result);
|
||||
break;
|
||||
case "getGpioVal":
|
||||
getGpioVal(call, result);
|
||||
break;
|
||||
case "setDtr":
|
||||
setDtr(call, result);
|
||||
break;
|
||||
case "setRts":
|
||||
setRts(call, result);
|
||||
break;
|
||||
case "setBreak":
|
||||
setBreak(call, result);
|
||||
break;
|
||||
case "registerModemStatusCallback":
|
||||
registerModemStatusCallback(call, result);
|
||||
break;
|
||||
case "removeModemStatusCallback":
|
||||
removeModemStatusCallback(call, result);
|
||||
break;
|
||||
case "querySerialErrorCount":
|
||||
querySerialErrorCount(call, result);
|
||||
break;
|
||||
case "setReadTimeout":
|
||||
setReadTimeout(call, result);
|
||||
break;
|
||||
case "addNewHardware":
|
||||
addNewHardware(call, result);
|
||||
break;
|
||||
case "setDebug":
|
||||
setDebug(call, result);
|
||||
break;
|
||||
case "isDebugMode":
|
||||
isDebugMode(result);
|
||||
break;
|
||||
default:
|
||||
result.notImplemented();
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Unhandled method call: " + call.method, e);
|
||||
result.error("INTERNAL_ERROR", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
|
||||
channel.setMethodCallHandler(null);
|
||||
closeAllDevices();
|
||||
if (dataEventChannel != null) dataEventChannel.setStreamHandler(null);
|
||||
if (modemEventChannel != null) modemEventChannel.setStreamHandler(null);
|
||||
if (usbStateEventChannel != null) usbStateEventChannel.setStreamHandler(null);
|
||||
}
|
||||
|
||||
// ==================== 设备枚举与识别 ====================
|
||||
|
||||
private void enumDevice(@NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
ArrayList<UsbDevice> devices = manager.enumDevice();
|
||||
List<Map<String, Object>> list = new ArrayList<>();
|
||||
for (UsbDevice device : devices) {
|
||||
String deviceName = device.getDeviceName();
|
||||
int serialCount = -1;
|
||||
String chipType = null;
|
||||
try {
|
||||
serialCount = manager.getSerialCount(device);
|
||||
ChipType2 type = manager.getChipType(device);
|
||||
if (type != null) {
|
||||
chipType = type.getDescription();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("deviceName", deviceName);
|
||||
map.put("productId", device.getProductId());
|
||||
map.put("vendorId", device.getVendorId());
|
||||
map.put("serialCount", serialCount);
|
||||
map.put("chipType", chipType);
|
||||
list.add(map);
|
||||
}
|
||||
result.success(list);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "enumDevice error", e);
|
||||
result.error("ENUM_DEVICE_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void getChipType(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
ChipType2 type = manager.getChipType(device);
|
||||
if (type != null) {
|
||||
result.success(type.getDescription());
|
||||
} else {
|
||||
result.success(null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "getChipType error", e);
|
||||
result.error("GET_CHIP_TYPE_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 设备打开与权限 ====================
|
||||
|
||||
private void openDevice(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
|
||||
if (openedDevices.containsKey(deviceName)) {
|
||||
result.success(true);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean opened = tryOpenDevice(device);
|
||||
if (!opened) {
|
||||
manager.requestPermission(context, device);
|
||||
new Handler(Looper.getMainLooper()).postDelayed(() -> {
|
||||
try {
|
||||
boolean retryResult = tryOpenDevice(device);
|
||||
if (retryResult) {
|
||||
openedDevices.put(deviceName, device);
|
||||
usbStateStreamHandler.notifyStateChanged(deviceName, true);
|
||||
result.success(true);
|
||||
} else {
|
||||
result.error("PERMISSION_DENIED", "USB permission not granted", null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
result.error("OPEN_DEVICE_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}, 2000);
|
||||
return;
|
||||
}
|
||||
|
||||
openedDevices.put(deviceName, device);
|
||||
usbStateStreamHandler.notifyStateChanged(deviceName, true);
|
||||
result.success(true);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "openDevice error", e);
|
||||
result.error("OPEN_DEVICE_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean tryOpenDevice(@NonNull UsbDevice device) {
|
||||
try {
|
||||
manager.openDevice(device);
|
||||
return true;
|
||||
} catch (NoPermissionException e) {
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "tryOpenDevice non-permission error", e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void requestPermission(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
manager.requestPermission(context, device);
|
||||
result.success(true);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "requestPermission error", e);
|
||||
result.error("REQUEST_PERMISSION_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 串口信息 ====================
|
||||
|
||||
private void getSerialCount(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
result.success(manager.getSerialCount(device));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "getSerialCount error", e);
|
||||
result.error("GET_SERIAL_COUNT_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void getSerialBaud(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
int serialNumber = call.argument("serialNumber");
|
||||
result.success(manager.getSerialBaud(device, serialNumber));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "getSerialBaud error", e);
|
||||
result.error("GET_SERIAL_BAUD_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void getChipMasterFrequency(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
cn.wch.uartlib.base.other.ChipMasterFrequency freq =
|
||||
manager.getChipMasterFrequency(device);
|
||||
if (freq != null) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("frequency", freq.getFrequency());
|
||||
map.put("switchEnable", freq.isSwitchEnable());
|
||||
map.put("coStatus", freq.getCoStatus());
|
||||
result.success(map);
|
||||
} else {
|
||||
result.error("GET_CHIP_FREQ_FAILED", "Failed to get chip master frequency", null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "getChipMasterFrequency error", e);
|
||||
result.error("GET_CHIP_FREQ_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void enableSerial(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
int serialNumber = call.argument("serialNumber");
|
||||
boolean enable = call.argument("enable");
|
||||
result.success(manager.enableSerial(device, serialNumber, enable));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "enableSerial error", e);
|
||||
result.error("ENABLE_SERIAL_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 串口参数设置 ====================
|
||||
|
||||
private void setSerialParameter(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
int serialNumber = call.argument("serialNumber");
|
||||
int baud = call.argument("baud");
|
||||
int dataBits = call.argument("dataBits");
|
||||
int stopBits = call.argument("stopBits");
|
||||
int parity = call.argument("parity");
|
||||
boolean flow = call.argument("hardwareFlowControl");
|
||||
|
||||
boolean success = manager.setSerialParameter(
|
||||
device, serialNumber, baud, dataBits, stopBits, parity, flow);
|
||||
result.success(success);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "setSerialParameter error", e);
|
||||
result.error("SET_SERIAL_PARAM_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 数据读写 ====================
|
||||
|
||||
private void writeData(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
int serialNumber = call.argument("serialNumber");
|
||||
byte[] data = call.argument("data");
|
||||
int length = data != null ? data.length : 0;
|
||||
int timeout = call.argument("timeout");
|
||||
|
||||
int written = manager.syncWriteData(device, serialNumber, data, length, timeout);
|
||||
result.success(written);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "writeData error", e);
|
||||
result.error("WRITE_DATA_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void asyncWriteData(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
int serialNumber = call.argument("serialNumber");
|
||||
byte[] data = call.argument("data");
|
||||
|
||||
manager.asyncWriteData(device, serialNumber, data);
|
||||
result.success(null);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "asyncWriteData error", e);
|
||||
result.error("ASYNC_WRITE_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void readData(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
int serialNumber = call.argument("serialNumber");
|
||||
|
||||
byte[] data = manager.readData(device, serialNumber);
|
||||
result.success(data);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "readData error", e);
|
||||
result.error("READ_DATA_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void readDataWithTimeout(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
int serialNumber = call.argument("serialNumber");
|
||||
int vTime = call.argument("vTime");
|
||||
int vMin = call.argument("vMin");
|
||||
|
||||
byte[] data = manager.readData(device, serialNumber, vTime, vMin);
|
||||
result.success(data);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "readDataWithTimeout error", e);
|
||||
result.error("READ_DATA_TIMEOUT_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerDataCallback(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
int serialNumber = call.argument("serialNumber");
|
||||
|
||||
dataStreamHandler.registerCallback(device, deviceName, serialNumber, (serial, buffer, length) -> {
|
||||
byte[] data = new byte[length];
|
||||
System.arraycopy(buffer, 0, data, 0, length);
|
||||
dataStreamHandler.sendData(data);
|
||||
});
|
||||
|
||||
manager.registerDataCallback(device, dataStreamHandler.getWchCallback(deviceName));
|
||||
result.success(null);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "registerDataCallback error", e);
|
||||
result.error("REGISTER_CALLBACK_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeDataCallback(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
|
||||
dataStreamHandler.removeCallback(deviceName);
|
||||
manager.removeDataCallback(device);
|
||||
result.success(null);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "removeDataCallback error", e);
|
||||
result.error("REMOVE_CALLBACK_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 连接状态 ====================
|
||||
|
||||
private void isConnected(@NonNull MethodCall call, @NonNull Result result) {
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = openedDevices.get(deviceName);
|
||||
result.success(device != null);
|
||||
} catch (Exception e) {
|
||||
result.success(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void getConnectedDevices(@NonNull Result result) {
|
||||
result.success(new ArrayList<>(openedDevices.keySet()));
|
||||
}
|
||||
|
||||
// ==================== 断开与关闭 ====================
|
||||
|
||||
private void disconnect(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = openedDevices.get(deviceName);
|
||||
if (device != null) {
|
||||
manager.disconnect(device);
|
||||
openedDevices.remove(deviceName);
|
||||
usbStateStreamHandler.notifyStateChanged(deviceName, false);
|
||||
}
|
||||
result.success(null);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "disconnect error", e);
|
||||
result.error("DISCONNECT_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void close(@NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
closeAllDevices();
|
||||
if (context instanceof Application) {
|
||||
manager.close((Application) context);
|
||||
}
|
||||
result.success(null);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "close error", e);
|
||||
result.error("CLOSE_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void closeAllDevices() {
|
||||
ensureManagerInitialized();
|
||||
for (Map.Entry<String, UsbDevice> entry : openedDevices.entrySet()) {
|
||||
try {
|
||||
manager.disconnect(entry.getValue());
|
||||
usbStateStreamHandler.notifyStateChanged(entry.getKey(), false);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "disconnect error during closeAll", e);
|
||||
}
|
||||
}
|
||||
openedDevices.clear();
|
||||
}
|
||||
|
||||
// ==================== GPIO 功能 ====================
|
||||
|
||||
private void isSupportGpio(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
result.success(manager.isSupportGPIOFeature(device));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "isSupportGpio error", e);
|
||||
result.error("GPIO_CHECK_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void queryGpioCount(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
result.success(manager.queryGPIOCount(device));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "queryGpioCount error", e);
|
||||
result.error("QUERY_GPIO_COUNT_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void queryGpioStatus(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
int gpioIndex = call.argument("gpioIndex");
|
||||
|
||||
cn.wch.uartlib.base.gpio.GPIO_Status status = manager.queryGPIOStatus(device, gpioIndex);
|
||||
result.success(Ch34TypeConverter.gpioStatusToMap(status, gpioIndex));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "queryGpioStatus error", e);
|
||||
result.error("QUERY_GPIO_STATUS_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void queryAllGpioStatus(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
|
||||
List<cn.wch.uartlib.base.gpio.GPIO_Status> statuses = manager.queryAllGPIOStatus(device);
|
||||
List<Map<String, Object>> list = new ArrayList<>();
|
||||
for (int i = 0; i < statuses.size(); i++) {
|
||||
list.add(Ch34TypeConverter.gpioStatusToMap(statuses.get(i), i));
|
||||
}
|
||||
result.success(list);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "queryAllGpioStatus error", e);
|
||||
result.error("QUERY_ALL_GPIO_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void enableGpio(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
int gpioIndex = call.argument("gpioIndex");
|
||||
boolean enable = call.argument("enable");
|
||||
int directionIndex = call.argument("direction");
|
||||
|
||||
cn.wch.uartlib.base.gpio.GPIO_DIR dir = Ch34TypeConverter.toGpioDirection(directionIndex);
|
||||
boolean success = manager.enableGPIO(device, gpioIndex, enable, dir);
|
||||
result.success(success);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "enableGpio error", e);
|
||||
result.error("ENABLE_GPIO_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void setGpioVal(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
int gpioIndex = call.argument("gpioIndex");
|
||||
int valueIndex = call.argument("value");
|
||||
|
||||
cn.wch.uartlib.base.gpio.GPIO_VALUE value = Ch34TypeConverter.toGpioValue(valueIndex);
|
||||
boolean success = manager.setGPIOVal(device, gpioIndex, value);
|
||||
result.success(success);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "setGpioVal error", e);
|
||||
result.error("SET_GPIO_VAL_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void getGpioVal(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
int gpioIndex = call.argument("gpioIndex");
|
||||
|
||||
cn.wch.uartlib.base.gpio.GPIO_VALUE value = manager.getGPIOVal(device, gpioIndex);
|
||||
result.success(value == cn.wch.uartlib.base.gpio.GPIO_VALUE.HIGH ? 1 : 0);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "getGpioVal error", e);
|
||||
result.error("GET_GPIO_VAL_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 信号控制 ====================
|
||||
|
||||
private void setDtr(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
int serialNumber = call.argument("serialNumber");
|
||||
boolean valid = call.argument("valid");
|
||||
|
||||
boolean success = manager.setDTR(device, serialNumber, valid);
|
||||
result.success(success);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "setDtr error", e);
|
||||
result.error("SET_DTR_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void setRts(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
int serialNumber = call.argument("serialNumber");
|
||||
boolean valid = call.argument("valid");
|
||||
|
||||
boolean success = manager.setRTS(device, serialNumber, valid);
|
||||
result.success(success);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "setRts error", e);
|
||||
result.error("SET_RTS_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void setBreak(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
int serialNumber = call.argument("serialNumber");
|
||||
boolean valid = call.argument("valid");
|
||||
|
||||
boolean success = manager.setBreak(device, serialNumber, valid);
|
||||
result.success(success);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "setBreak error", e);
|
||||
result.error("SET_BREAK_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Modem 状态回调 ====================
|
||||
|
||||
private void registerModemStatusCallback(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
|
||||
modemStreamHandler.startListening();
|
||||
manager.registerModemStatusCallback(device, new IModemStatus() {
|
||||
@Override
|
||||
public void onStatusChanged(int serialNumber, boolean isDCDRaised,
|
||||
boolean isDSRRaised, boolean isCTSRaised, boolean isRINGRaised) {
|
||||
Log.d(TAG, "Modem onStatusChanged: serial=" + serialNumber
|
||||
+ " dcd=" + isDCDRaised + " dsr=" + isDSRRaised
|
||||
+ " cts=" + isCTSRaised + " ri=" + isRINGRaised);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("dcd", isDCDRaised);
|
||||
map.put("dsr", isDSRRaised);
|
||||
map.put("cts", isCTSRaised);
|
||||
map.put("ri", isRINGRaised);
|
||||
Log.d(TAG, "Sending modem status to Flutter: " + map);
|
||||
modemStreamHandler.sendStatus(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOverrunError(int serialNumber) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onParityError(int serialNumber) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameError(int serialNumber) {
|
||||
}
|
||||
});
|
||||
result.success(null);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "registerModemStatusCallback error", e);
|
||||
result.error("REGISTER_MODEM_CALLBACK_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeModemStatusCallback(@NonNull MethodCall call, @NonNull Result result) {
|
||||
try {
|
||||
modemStreamHandler.stopListening();
|
||||
result.success(null);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "removeModemStatusCallback error", e);
|
||||
result.error("REMOVE_MODEM_CALLBACK_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 错误查询 ====================
|
||||
|
||||
private void querySerialErrorCount(@NonNull MethodCall call, @NonNull Result result) {
|
||||
ensureManagerInitialized();
|
||||
try {
|
||||
String deviceName = call.argument("deviceName");
|
||||
UsbDevice device = getDeviceOrThrow(deviceName);
|
||||
int serialNumber = call.argument("serialNumber");
|
||||
String errorType = call.argument("errorType");
|
||||
|
||||
cn.wch.uartlib.base.error.SerialErrorType nativeType =
|
||||
Ch34TypeConverter.toSerialErrorType(errorType);
|
||||
int count = manager.querySerialErrorCount(device, serialNumber, nativeType);
|
||||
result.success(count);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "querySerialErrorCount error", e);
|
||||
result.error("QUERY_ERROR_COUNT_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 全局配置 ====================
|
||||
|
||||
private void setReadTimeout(@NonNull MethodCall call, @NonNull Result result) {
|
||||
try {
|
||||
int timeout = call.argument("timeout");
|
||||
WCHUARTManager.setReadTimeout(timeout);
|
||||
result.success(null);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "setReadTimeout error", e);
|
||||
result.error("SET_TIMEOUT_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void addNewHardware(@NonNull MethodCall call, @NonNull Result result) {
|
||||
try {
|
||||
int vid = call.argument("vid");
|
||||
int pid = call.argument("pid");
|
||||
String chipType = call.argument("chipType");
|
||||
cn.wch.uartlib.chip.type.ChipType2 type =
|
||||
Ch34TypeConverter.toChipType(chipType);
|
||||
WCHUARTManager.addNewHardwareAndChipType(vid, pid, type);
|
||||
result.success(null);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "addNewHardware error", e);
|
||||
result.error("ADD_HARDWARE_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void setDebug(@NonNull MethodCall call, @NonNull Result result) {
|
||||
try {
|
||||
boolean enabled = call.argument("enabled");
|
||||
WCHUARTManager.setDebug(enabled);
|
||||
result.success(null);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "setDebug error", e);
|
||||
result.error("SET_DEBUG_FAILED", e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void isDebugMode(@NonNull Result result) {
|
||||
result.success(WCHUARTManager.isDebugMode());
|
||||
}
|
||||
|
||||
// ==================== 辅助方法 ====================
|
||||
|
||||
private void ensureManagerInitialized() {
|
||||
if (manager == null) {
|
||||
Application app;
|
||||
if (context instanceof Application) {
|
||||
app = (Application) context;
|
||||
} else {
|
||||
app = (Application) context.getApplicationContext();
|
||||
}
|
||||
manager = WCHUARTManager.getInstance();
|
||||
manager.init(app);
|
||||
usbStateStreamHandler.setManager(manager);
|
||||
usbStateStreamHandler.setContext(context);
|
||||
}
|
||||
}
|
||||
|
||||
private UsbDevice getDeviceOrThrow(String deviceName) {
|
||||
UsbDevice device = openedDevices.get(deviceName);
|
||||
if (device == null) {
|
||||
try {
|
||||
ArrayList<UsbDevice> devices = manager.enumDevice();
|
||||
for (UsbDevice d : devices) {
|
||||
if (d.getDeviceName().equals(deviceName)) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
throw new IllegalStateException("Device not found: " + deviceName);
|
||||
}
|
||||
return device;
|
||||
}
|
||||
}
|
||||
134
android/src/main/java/com/example/ch34/Ch34TypeConverter.java
Normal file
134
android/src/main/java/com/example/ch34/Ch34TypeConverter.java
Normal file
@@ -0,0 +1,134 @@
|
||||
package com.example.ch34;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 类型转换工具类。
|
||||
*
|
||||
* 在 Dart 枚举值和 Java/WCH 类型之间进行转换。
|
||||
*/
|
||||
public class Ch34TypeConverter {
|
||||
|
||||
private Ch34TypeConverter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 GPIO 状态对象转换为 Map。
|
||||
*
|
||||
* @param status WCH GPIO_Status 对象。
|
||||
* @param gpioIndex GPIO 编号。
|
||||
* @return 包含 index/direction/value/enabled 的 Map。
|
||||
*/
|
||||
public static Map<String, Object> gpioStatusToMap(
|
||||
cn.wch.uartlib.base.gpio.GPIO_Status status, int gpioIndex) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
if (status != null) {
|
||||
map.put("index", gpioIndex);
|
||||
map.put("enabled", status.isEnabled());
|
||||
map.put("direction", status.getDir() == cn.wch.uartlib.base.gpio.GPIO_DIR.OUT ? 1 : 0);
|
||||
map.put("value", status.getValue() == cn.wch.uartlib.base.gpio.GPIO_VALUE.HIGH ? 1 : 0);
|
||||
} else {
|
||||
map.put("index", gpioIndex);
|
||||
map.put("enabled", false);
|
||||
map.put("direction", 0);
|
||||
map.put("value", 0);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Dart 端的方向索引转换为 WCH GPIO_DIR 枚举。
|
||||
*
|
||||
* @param index 0 = IN, 1 = OUT。
|
||||
* @return WCH GPIO_DIR 枚举。
|
||||
*/
|
||||
public static cn.wch.uartlib.base.gpio.GPIO_DIR toGpioDirection(int index) {
|
||||
if (index == 1) {
|
||||
return cn.wch.uartlib.base.gpio.GPIO_DIR.OUT;
|
||||
}
|
||||
return cn.wch.uartlib.base.gpio.GPIO_DIR.IN;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Dart 端的值索引转换为 WCH GPIO_VALUE 枚举。
|
||||
*
|
||||
* @param index 0 = LOW, 1 = HIGH。
|
||||
* @return WCH GPIO_VALUE 枚举。
|
||||
*/
|
||||
public static cn.wch.uartlib.base.gpio.GPIO_VALUE toGpioValue(int index) {
|
||||
if (index == 1) {
|
||||
return cn.wch.uartlib.base.gpio.GPIO_VALUE.HIGH;
|
||||
}
|
||||
return cn.wch.uartlib.base.gpio.GPIO_VALUE.LOW;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将芯片类型字符串转换为 WCH ChipType2 枚举。
|
||||
*
|
||||
* @param chipType 芯片类型描述字符串(如 "CH340"、"CH9102")。
|
||||
* @return WCH ChipType2 枚举。
|
||||
* @throws IllegalArgumentException 当 chipType 为 null 或未知类型时抛出。
|
||||
*/
|
||||
public static cn.wch.uartlib.chip.type.ChipType2 toChipType(String chipType) {
|
||||
if (chipType == null) {
|
||||
throw new IllegalArgumentException("chipType is null");
|
||||
}
|
||||
switch (chipType) {
|
||||
case "CH340":
|
||||
case "CH341":
|
||||
return cn.wch.uartlib.chip.type.ChipType2.CHIP_CH341;
|
||||
case "CH342":
|
||||
return cn.wch.uartlib.chip.type.ChipType2.CHIP_CH342F;
|
||||
case "CH343":
|
||||
return cn.wch.uartlib.chip.type.ChipType2.CHIP_CH343GP;
|
||||
case "CH344":
|
||||
return cn.wch.uartlib.chip.type.ChipType2.CHIP_CH344L;
|
||||
case "CH347":
|
||||
return cn.wch.uartlib.chip.type.ChipType2.CHIP_CH347TF;
|
||||
case "CH9101":
|
||||
return cn.wch.uartlib.chip.type.ChipType2.CHIP_CH9101U;
|
||||
case "CH9102":
|
||||
return cn.wch.uartlib.chip.type.ChipType2.CHIP_CH9102F;
|
||||
case "CH9103":
|
||||
return cn.wch.uartlib.chip.type.ChipType2.CHIP_CH9103M;
|
||||
case "CH9104":
|
||||
return cn.wch.uartlib.chip.type.ChipType2.CHIP_CH9104L;
|
||||
case "CH9143":
|
||||
return cn.wch.uartlib.chip.type.ChipType2.CHIP_CH9143;
|
||||
case "CH9111":
|
||||
return cn.wch.uartlib.chip.type.ChipType2.CHIP_CH9111L_MODE0;
|
||||
case "CH9114":
|
||||
return cn.wch.uartlib.chip.type.ChipType2.CHIP_CH9114L;
|
||||
case "CH339":
|
||||
return cn.wch.uartlib.chip.type.ChipType2.CHIP_CH339W;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown chipType: " + chipType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Dart 端的错误类型字符串转换为 WCH SerialErrorType 枚举。
|
||||
*
|
||||
* WCH 原生库仅支持 FRAME/PARITY/OVERRUN 三种错误类型。
|
||||
*
|
||||
* @param errorType 错误类型字符串。
|
||||
* @return WCH SerialErrorType 枚举。
|
||||
* @throws IllegalArgumentException 传入的错误类型字符串无法识别时抛出。
|
||||
*/
|
||||
public static cn.wch.uartlib.base.error.SerialErrorType toSerialErrorType(String errorType) {
|
||||
if (errorType == null) {
|
||||
throw new IllegalArgumentException("errorType is null");
|
||||
}
|
||||
switch (errorType) {
|
||||
case "SerialErrorType.FramingError":
|
||||
return cn.wch.uartlib.base.error.SerialErrorType.FRAME;
|
||||
case "SerialErrorType.ParityError":
|
||||
return cn.wch.uartlib.base.error.SerialErrorType.PARITY;
|
||||
case "SerialErrorType.OverrunError":
|
||||
return cn.wch.uartlib.base.error.SerialErrorType.OVERRUN;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown SerialErrorType: " + errorType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.example.ch34;
|
||||
|
||||
import io.flutter.plugin.common.EventChannel;
|
||||
import android.content.Context;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import cn.wch.uartlib.WCHUARTManager;
|
||||
import cn.wch.uartlib.callback.IUsbStateChange;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* USB 插拔状态 EventChannel StreamHandler。
|
||||
*
|
||||
* 基于 WCH 库的 {@link IUsbStateChange} 回调接收设备插入/拔出/权限变更事件,
|
||||
* 并通过 EventChannel 通知 Flutter 端。
|
||||
*/
|
||||
public class Ch34UsbStateStreamHandler implements EventChannel.StreamHandler {
|
||||
|
||||
private static final String TAG = "Ch34UsbStateStream";
|
||||
|
||||
private EventChannel.EventSink eventSink;
|
||||
private Context context;
|
||||
private WCHUARTManager manager;
|
||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
/** WCH 库 USB 状态回调,监听设备插拔与权限变化 */
|
||||
private final IUsbStateChange wchUsbStateListener = new IUsbStateChange() {
|
||||
@Override
|
||||
public void usbDeviceAttach(UsbDevice device) {
|
||||
if (device != null) {
|
||||
notifyStateChanged(device.getDeviceName(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void usbDeviceDetach(UsbDevice device) {
|
||||
if (device != null) {
|
||||
notifyStateChanged(device.getDeviceName(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void usbDevicePermission(UsbDevice device, boolean granted) {
|
||||
Log.d(TAG, "USB permission changed: "
|
||||
+ (device != null ? device.getDeviceName() : "null")
|
||||
+ ", granted=" + granted);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置 WCH 管理器。
|
||||
*
|
||||
* 在管理器可用后自动注册 USB 状态监听器。
|
||||
*
|
||||
* @param manager WCHUARTManager 实例。
|
||||
*/
|
||||
public void setManager(WCHUARTManager manager) {
|
||||
this.manager = manager;
|
||||
if (manager != null) {
|
||||
try {
|
||||
manager.setUsbStateListener(wchUsbStateListener);
|
||||
Log.d(TAG, "WCH USB state listener registered");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to register WCH USB state listener", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置上下文。
|
||||
*
|
||||
* @param context Android Context。
|
||||
*/
|
||||
public void setContext(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListen(Object arguments, EventChannel.EventSink events) {
|
||||
this.eventSink = events;
|
||||
Log.d(TAG, "USB state stream listening");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(Object arguments) {
|
||||
this.eventSink = null;
|
||||
Log.d(TAG, "USB state stream cancelled");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知设备状态变化。
|
||||
*
|
||||
* 会在主线程上执行 eventSink.success(),确保线程安全。
|
||||
*
|
||||
* @param deviceName 设备名称。
|
||||
* @param connected `true` 已连接,`false` 已断开。
|
||||
*/
|
||||
public void notifyStateChanged(String deviceName, boolean connected) {
|
||||
mainHandler.post(() -> {
|
||||
EventChannel.EventSink sink = eventSink;
|
||||
if (sink != null) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("deviceName", deviceName);
|
||||
map.put("connected", connected);
|
||||
sink.success(map);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
29
android/src/test/java/com/example/ch34/Ch34PluginTest.java
Normal file
29
android/src/test/java/com/example/ch34/Ch34PluginTest.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package com.example.ch34;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* This demonstrates a simple unit test of the Java portion of this plugin's implementation.
|
||||
*
|
||||
* Once you have built the plugin's example app, you can run these tests from the command
|
||||
* line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
|
||||
* you can run them directly from IDEs that support JUnit such as Android Studio.
|
||||
*/
|
||||
|
||||
public class Ch34PluginTest {
|
||||
@Test
|
||||
public void onMethodCall_getPlatformVersion_returnsExpectedValue() {
|
||||
Ch34Plugin plugin = new Ch34Plugin();
|
||||
|
||||
final MethodCall call = new MethodCall("getPlatformVersion", null);
|
||||
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
|
||||
plugin.onMethodCall(call, mockResult);
|
||||
|
||||
verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user