Files
ch34/.planning/codebase/ARCHITECTURE.md
2026-04-21 12:57:40 +08:00

199 lines
9.9 KiB
Markdown

# Architecture
**Analysis Date:** 2026-04-16
## Pattern Overview
**Overall:** Flutter Platform Plugin (Federated Plugin Pattern)
This is an Android-only Flutter plugin that wraps the WCH CH34X UART library (`CH34XUARTDriver.jar`). It bridges Flutter/Dart code to native Android USB serial communication via `MethodChannel` and `EventChannel`.
**Key Characteristics:**
- Static facade pattern for public API (`Ch34Manager`)
- Platform interface pattern with token-based verification (`Ch34Platform`)
- MethodChannel for command invocation, EventChannel for streaming callbacks
- Single platform implementation (Android only, Kotlin/Java)
- Singleton WCH UART manager on the native side
## Layers
### Dart Public API Layer
- **Purpose:** Static facade providing the consumer-facing API
- **Location:** `D:\code\new_git_code\flutter\ch34\lib\src\ch34_manager.dart`
- **Contains:** Static methods delegating to `Ch34Platform.instance`
- **Depends on:** `Ch34Platform`
- **Used by:** Flutter app consuming the plugin
### Dart Platform Interface Layer
- **Purpose:** Abstract interface defining all plugin methods with token verification
- **Location:** `D:\code\new_git_code\flutter\ch34\lib\src\ch34_platform_interface.dart`
- **Contains:** `abstract class Ch34Platform extends PlatformInterface` with 33 method signatures
- **Depends on:** `plugin_platform_interface` package, `ch34_types.dart`
- **Used by:** `MethodChannelCh34` for concrete implementation
### Dart MethodChannel Implementation Layer
- **Purpose:** Bridges abstract platform interface to native Android via Flutter's platform channels
- **Location:** `D:\code\new_git_code\flutter\ch34\lib\src\ch34_method_channel.dart`
- **Contains:** `class MethodChannelCh34 extends Ch34Platform` with actual channel communication
- **Channels:**
- `MethodChannel('ch34')` - synchronous method calls
- `EventChannel('ch34/data')` - serial data stream
- `EventChannel('ch34/modem')` - modem status stream
- `EventChannel('ch34/usb_state')` - USB hotplug events
- **Depends on:** `dart:async`, `flutter/services.dart`
- **Used by:** `Ch34Manager` (via `Ch34Platform.instance`)
### Dart Types Layer
- **Purpose:** Shared data types and enums for the plugin
- **Location:** `D:\code\new_git_code\flutter\ch34\lib\src\types\ch34_types.dart`
- **Contains:**
- Enums: `DataBits`, `StopBits`, `Parity`, `GpioDirection`, `GpioValue`, `SerialErrorType`
- Data classes: `GpioStatus`, `SerialParameter`, `ModemStatus`, `UsbDeviceInfo`
- Exception: `Ch34Exception` (defined in `ch34_method_channel.dart`)
### Android Native Layer
- **Purpose:** Actual WCH UART library interaction, USB device management
- **Location:** `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\`
- **Contains:**
- `Ch34Plugin.java` - Main plugin entry point, MethodCallHandler
- `Ch34DataStreamHandler.java` - Data EventChannel stream handler with WCH callback bridging
- `Ch34ModemStreamHandler.java` - Modem status EventChannel stream handler
- `Ch34UsbStateStreamHandler.java` - USB hotplug EventChannel with BroadcastReceiver
- `Ch34TypeConverter.java` - Type conversion between Dart and WCH types
- **Depends on:** `CH34XUARTDriver.jar` (bundled in `android/libs/`)
- **Used by:** Dart MethodChannel layer
### Backward Compatibility Layer
- **Location:** `D:\code\new_git_code\flutter\ch34\lib\ch34_platform_interface.dart`, `D:\code\new_git_code\flutter\ch34\lib\ch34_method_channel.dart`
- **Purpose:** Re-exports from `src/` for consumers using old import paths
## Data Flow
### Device Discovery Flow
1. Consumer calls `Ch34Manager.enumDevice()`
2. Delegates to `Ch34Platform.instance.enumDevice()`
3. `MethodChannelCh34.enumDevice()` invokes `methodChannel.invokeMethod('enumDevice')`
4. `Ch34Plugin.enumDevice()` calls `WCHUARTManager.enumDevice()` on native side
5. Native iterates devices, extracts VID/PID/serialCount/chipType
6. Returns `List<Map>` through MethodChannel
7. Dart side deserializes into `List<UsbDeviceInfo>`
### Data Read Flow (Callback Mode)
1. Consumer calls `Ch34Manager.registerDataCallback(deviceName, serialNumber, onData)`
2. `MethodChannelCh34` subscribes to `EventChannel('ch34/data')`
3. Invokes `methodChannel.invokeMethod('registerDataCallback')`
4. `Ch34Plugin` registers `Ch34DataStreamHandler.getWchCallback()` with `WCHUARTManager.registerDataCallback()`
5. When data arrives, WCH triggers `IDataCallback.onData()`
6. `Ch34DataStreamHandler` bridges data to EventChannel
7. Dart receives via `StreamSubscription`, invokes user callback
### Data Write Flow
1. Consumer calls `Ch34Manager.writeData(deviceName, serialNumber, data)`
2. Delegates through platform interface to `MethodChannelCh34`
3. `methodChannel.invokeMethod('writeData', {data: data, ...})`
4. `Ch34Plugin.writeData()` calls `WCHUARTManager.writeData(device, serial, data, length, timeout)`
5. Returns bytes written count
### USB Hotplug Event Flow
1. `Ch34UsbStateStreamHandler` registers `BroadcastReceiver` for `ACTION_USB_DEVICE_ATTACHED/DETACHED`
2. Android system sends broadcast on USB events
3. BroadcastReceiver extracts `UsbDevice` and calls `notifyStateChanged()`
4. EventChannel sends `{deviceName, connected}` to Dart
5. `MethodChannelCh34` invokes `_usbStateCallback`
### Device Open Flow
1. Consumer calls `Ch34Manager.openDevice(deviceName)`
2. `Ch34Plugin.openDevice()` attempts `WCHUARTManager.openDevice(device)`
3. If `NoPermissionException`, requests permission then retries after 2s delay
4. On success, adds to `openedDevices` map and notifies `usbStateStreamHandler`
## State Management
**Native State:**
- `WCHUARTManager` - Singleton instance, lazy-initialized in `ensureManagerInitialized()`
- `openedDevices: Map<String, UsbDevice>` - tracks currently opened devices
- Three `EventChannel.StreamHandler` instances - each manages its own `EventSink`
**Dart State:**
- `Ch34Platform._instance` - Singleton platform instance
- `MethodChannelCh34` maintains `_dataSubscription`, `_modemSubscription`, `_usbStateSubscription` - StreamSubscriptions for event channels
- Callback references: `_dataCallback`, `_modemCallback`, `_usbStateCallback`
**State Cleanup:**
- `Ch34Plugin.close()` calls `closeAllDevices()` to disconnect all and clear map
- `Ch34UsbStateStreamHandler.onCancel()` unregisters BroadcastReceiver
- `Ch34Manager.close()` triggers platform cleanup and cancels all subscriptions
## Key Abstractions
### Platform Interface (`Ch34Platform`)
- **Purpose:** Abstract contract that decouples Flutter code from native implementation
- **Example:** `D:\code\new_git_code\flutter\ch34\lib\src\ch34_platform_interface.dart`
- **Pattern:** Token-based platform interface verification from `plugin_platform_interface` package
- **Pattern:**
```dart
static final Object _token = Object();
static Ch34Platform get instance { ... }
static void registerDefaultInstance(Ch34Platform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
```
### Manager Facade (`Ch34Manager`)
- **Purpose:** Static facade simplifying API usage for consumers
- **Example:** `D:\code\new_git_code\flutter\ch34\lib\src\ch34_manager.dart`
- **Pattern:** All static methods, all delegate to `Ch34Platform.instance`
- **Pattern:**
```dart
static Future<List<UsbDeviceInfo>> enumDevice() {
return Ch34Platform.instance.enumDevice();
}
```
### Type Converter (`Ch34TypeConverter`)
- **Purpose:** Bridge between Dart enum indices and WCH native enum values
- **Example:** `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34TypeConverter.java`
- **Pattern:** Static utility class with conversion methods for GPIO direction/value, serial error types
## Entry Points
### Dart Library Entry
- **Location:** `D:\code\new_git_code\flutter\ch34\lib\ch34.dart`
- **Triggers:** Consumer imports `package:ch34/ch34.dart`
- **Responsibilities:** Exports `Ch34Manager`, `Ch34Platform`, all types, and `Ch34Exception`
### Plugin Initialization (Android)
- **Location:** `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java`
- **Trigger:** Flutter engine attaches plugin via `onAttachedToEngine()`
- **Responsibilities:** Creates MethodChannel, 3 EventChannels, sets MethodCallHandler
- **Declaration:** `D:\code\new_git_code\flutter\ch34\android\build.gradle` + `pubspec.yaml` declares `package: com.example.ch34`, `pluginClass: Ch34Plugin`
### MethodChannel Dispatcher
- **Location:** `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` line 80-187
- **Trigger:** Dart invokes `methodChannel.invokeMethod()`
- **Responsibilities:** Switch dispatch to 33 method handlers, error handling wrapper
## Error Handling
**Strategy:** Native exceptions caught and converted to MethodChannel error responses with error codes; Dart side uses null-coalescing defaults and `Ch34Exception` for explicit failures.
**Patterns:**
- **Native:** Each method handler has try-catch, returns `result.error(ERROR_CODE, message, null)`
- Error codes: `ENUM_DEVICE_FAILED`, `OPEN_DEVICE_FAILED`, `PERMISSION_DENIED`, `WRITE_DATA_FAILED`, etc.
- **Dart:** Default fallbacks on null results (e.g., `result ?? false`, `result ?? Uint8List(0)`)
- **Exception class:** `Ch34Exception` in `D:\code\new_git_code\flutter\ch34\lib\src\ch34_method_channel.dart` for throwing on Dart side
- **Logging:** All errors logged via `Log.e(TAG, "...")` on Android native side
## Cross-Cutting Concerns
**Logging:** Android `android.util.Log` with per-class TAGs (`Ch34Plugin`, `Ch34DataStream`, `Ch34ModemStream`, `Ch34UsbStateStream`). Dart side uses `flutter/foundation.dart` `debugPrint()` for callback errors.
**Validation:** Device name resolution via `getDeviceOrThrow()` which searches `openedDevices` map first, then falls back to `manager.enumDevice()` lookup.
**Authentication/Permission:** USB permission flow handled in `openDevice()` - attempts open, if `NoPermissionException`, calls `manager.requestPermission()` then retries after 2-second delay.
---
*Architecture analysis: 2026-04-16*