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

View File

@@ -0,0 +1,198 @@
# 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*

View File

@@ -0,0 +1,167 @@
# Codebase Concerns
**Analysis Date:** 2026-04-16
## Technical Debt
### Placeholder Content in LICENSE and CHANGELOG
- Issue: `LICENSE` contains "TODO: Add your license here" and `CHANGELOG.md` contains "TODO: Describe initial release." No license or changelog content exists.
- Files: `D:\code\new_git_code\flutter\ch34\LICENSE`, `D:\code\new_git_code\flutter\ch34\CHANGELOG.md`
- Impact: Legal risk - the package has no defined license. No version history for consumers.
- Fix approach: Add an appropriate open-source license (e.g., MIT). Document release history in CHANGELOG.
### Unimplemented Modem Error Callbacks
- Issue: In `registerModemStatusCallback`, the `IModemStatus` interface has three error callbacks (`onOverrunError`, `onParityError`, `onFrameError`) with empty bodies - they silently discard errors.
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (lines 667-677)
- Impact: Serial communication errors are invisible to the Flutter layer. Users cannot detect data corruption.
- Fix approach: Forward these errors through the EventChannel or the data callback, or expose a separate error stream.
### GPIO Status Map Always Returns Hardcoded Values
- Issue: `Ch34TypeConverter.gpioStatusToMap()` hardcodes `index` and `value` to 0, regardless of the actual `GPIO_Status` object. Only `enabled` and `direction` are read from the status.
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34TypeConverter.java` (lines 28-29)
- Impact: Dart side `queryGpioStatus()` and `queryAllGpioStatus()` always return incorrect `index` and `value` fields.
- Fix approach: Read `status.getIndex()` and `status.getValue()` from the WCH `GPIO_Status` object.
## Known Bugs
### Data Callback Only Supports Single Device Per Serial Number
- Issue: `Ch34DataStreamHandler` uses a single `_dataCallback` field. When multiple devices register data callbacks, each new registration overwrites the previous one. Only the last registered device's data is received.
- Files: `D:\code\new_git_code\flutter\ch34\lib\src\ch34_method_channel.dart` (line 44), `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (lines 388-407)
- Impact: Multi-device scenarios lose data from all but the most recently registered device.
- Fix approach: Use a map keyed by `(deviceName, serialNumber)` to manage multiple independent subscriptions.
### Hardcoded USB Permission Wait Time
- Issue: In `openDevice`, when the initial open fails, a `Handler.postDelayed` waits 2000ms and retries. This is a magic number that may be insufficient on slow devices or excessive on fast ones.
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (line 278)
- Impact: Permission may not be granted in time on some devices, leading to false failure.
- Fix approach: Use a proper permission result callback or `PendingIntent` instead of a fixed delay.
### `tryOpenDevice` Swallows Non-Permission Exceptions
- Issue: In `tryOpenDevice`, any non-`NoPermissionException` error logs the error but returns `true`, treating it as success.
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (lines 291-300)
- Impact: Failed device opens are reported as successful, leading to downstream errors that are harder to debug.
- Fix approach: Return `false` for any exception and let the caller handle the error properly.
## Security Concerns
### No Input Validation on Native Method Arguments
- Issue: The Java plugin directly casts method call arguments without null checks or type validation (e.g., `call.argument("deviceName")` cast to `String` without null guards).
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (throughout)
- Impact: Malformed or malicious method call arguments from Flutter can cause `ClassCastException` or `NullPointerException` on the Android side.
- Fix approach: Add null/type checks before using arguments. Return error results for invalid inputs.
### `pubspec.lock` Ignored for Library Packages
- Issue: `pubspec.lock` is in `.gitignore`, which is correct for library packages. However, `example/pubspec.lock` is not explicitly tracked, meaning the example app's dependency versions are not pinned.
- Files: `D:\code\new_git_code\flutter\ch34\.gitignore` (line 26)
- Impact: Example app may resolve to different dependency versions on different machines, causing inconsistent test results.
- Fix approach: Consider committing `example/pubspec.lock` for reproducible example builds.
## Performance Bottlenecks
### Synchronous `enumDevice` in `getDeviceOrThrow`
- Issue: `getDeviceOrThrow` calls `manager.enumDevice()` to enumerate all devices just to find one by name. This is called for nearly every method invocation.
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (lines 772-787)
- Impact: USB enumeration is slow and repeated unnecessarily. Every method call (read, write, set parameters) triggers this scan if the device is not in `openedDevices`.
- Fix approach: Cache device references when they are discovered, or maintain a device lookup map.
### Blocking `readData` Without Timeout
- Issue: `readData` calls `manager.readData(device, serialNumber)` synchronously with no timeout. If no data arrives, the method blocks indefinitely.
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (lines 373-385), `D:\code\new_git_code\flutter\ch34\lib\src\ch34_method_channel.dart` (lines 176-185)
- Impact: Flutter UI thread could freeze if a `readData` call blocks on a quiet serial line.
- Fix approach: Use the async callback-based approach for reading, or enforce a configurable timeout.
## Fragile Areas
### Single Global WCHUARTManager Instance
- Issue: The entire plugin uses a single `WCHUARTManager` instance stored as a field. It is lazily initialized in `ensureManagerInitialized()` and never reset except on engine detach.
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (lines 57, 757-770)
- Impact: If the WCH library is in a bad state (e.g., after an unhandled error), there is no recovery mechanism. Hot restart during development may cause initialization issues.
- Fix approach: Add a `reset()` or `reinitialize()` method for recovery scenarios.
### EventChannel Single Instance for All Devices
- Issue: Each EventChannel (`ch34/data`, `ch34/modem`, `ch34/usb_state`) has a single StreamHandler instance for all devices. The data handler must multiplex all device data through one channel.
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (lines 66-76), `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34DataStreamHandler.java`
- Impact: Adding device identification to each data event is the responsibility of the consumer. No per-device stream isolation.
- Fix approach: Include `deviceName` and `serialNumber` in each data event payload, or create per-device EventChannels.
### `removeDataCallback` Ignores `serialNumber`
- Issue: The Dart API `removeDataCallback` takes only `deviceName`, but `registerDataCallback` takes both `deviceName` and `serialNumber`. If a device has multiple serial ports, removing the callback for one serial number removes all.
- Files: `D:\code\new_git_code\flutter\ch34\lib\src\ch34_platform_interface.dart` (line 166), `D:\code\new_git_code\flutter\ch34\lib\src\ch34_manager.dart` (lines 160-162)
- Impact: Multi-port devices cannot independently manage callbacks per serial port.
- Fix approach: Add `serialNumber` parameter to `removeDataCallback`.
### Swallowed Exceptions in `enumDevice` Loop
- Issue: In `enumDevice`, exceptions during `getSerialCount` or `getChipType` are silently caught and ignored. The device is still reported but with incomplete data.
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (lines 215)
- Impact: Consumers may receive devices with `serialCount = -1` and `chipType = null` with no indication of what went wrong.
- Fix approach: At minimum log the exception, or distinguish between "no data available" and "error reading data".
## Missing Patterns
### No Structured Error Handling
- Issue: The plugin uses `result.error(code, message, null)` with string codes for error reporting. The Dart side never throws typed exceptions (except `Ch34Exception` which is only used for GPIO failures). Most errors silently return `false` or `0`.
- Files: `D:\code\new_git_code\flutter\ch34\lib\src\ch34_method_channel.dart` (throughout), `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java` (throughout)
- Impact: Consumers cannot distinguish between different failure modes. Debugging requires enabling debug mode and reading logcat.
- Fix approach: Throw `Ch34Exception` with error code and message from all `result.error` responses. Define error code constants.
### No Lifecycle Management
- Issue: The plugin does not handle Android lifecycle events (e.g., app going to background). Active USB connections may be left open or become invalid.
- Files: `D:\code\new_git_code\flutter\ch34\android\src\main\java\com\example\ch34\Ch34Plugin.java`
- Impact: When the app resumes, existing connections may be in an undefined state.
- Fix approach: Implement `ActivityAware` or use `AppLifecycleState` in Dart to pause/resume or clean up connections.
### No Unit Test Coverage for Core Logic
- Issue: The only unit tests cover `getPlatformVersion` and `enumDevice` with stub returns. None of the data conversion, parameter handling, or error paths are tested.
- Files: `D:\code\new_git_code\flutter\ch34\test\ch34_test.dart`, `D:\code\new_git_code\flutter\ch34\test\ch34_method_channel_test.dart`, `D:\code\new_git_code\flutter\ch34\android\src\test\java\com\example\ch34\Ch34PluginTest.java`
- Impact: Refactoring or bug fixes risk introducing regressions with no automated detection.
- Fix approach: Add tests for type conversion, error handling, and the mock platform covering all API methods.
## Dependencies Risk
### Bundled JAR Dependency Without Version Pinning
- Issue: The WCH `CH34XUARTDriver.jar` is included as a local JAR file in `android/libs/` with no version information in the build file. It is loaded via `flatDir` repository.
- Files: `D:\code\new_git_code\flutter\ch34\android\build.gradle` (line 47)
- Impact: No easy way to track which version of the WCH library is used. Cannot update to newer versions without manual replacement. No transitive dependency management.
- Fix approach: Document the JAR version used. Consider hosting in a Maven repository for proper version management.
### Android Gradle Plugin Version is Outdated
- Issue: The build uses `com.android.tools.build:gradle:7.3.0` which is significantly outdated. Current versions are 8.x.
- Files: `D:\code\new_git_code\flutter\ch34\android\build.gradle` (line 11)
- Impact: May not be compatible with newer Flutter/Android SDK versions. Missing bug fixes and performance improvements.
- Fix approach: Update to AGP 8.x and test thoroughly. Update `compileSdk` and `targetSdk` accordingly.
### Java 8 Target Compatibility
- Issue: The plugin compiles with `JavaVersion.VERSION_1_8` as source and target.
- Files: `D:\code\new_git_code\flutter\ch34\android\build.gradle` (lines 35-36)
- Impact: Cannot use modern Java language features. May limit compatibility with future Android tooling.
- Fix approach: Consider updating to Java 11+ if the minimum SDK and tooling support it.
## Test Coverage Gaps
### No Integration Tests for USB Operations
- Issue: The single integration test only checks `getPlatformVersion`. No tests for actual USB device operations.
- Files: `D:\code\new_git_code\flutter\ch34\example\integration_test\plugin_integration_test.dart`
- Risk: USB-specific bugs are only caught through manual testing.
- Priority: High
### No Tests for Type Conversions
- Issue: `Ch34TypeConverter` has zero test coverage. The GPIO and error type conversions are not verified.
- Files: `D:\code\new_git_code\flutter\ch34\android\src\test\java\com\example\ch34\Ch34PluginTest.java`
- Risk: Conversion bugs (like the hardcoded GPIO values) go undetected.
- Priority: High
### No Tests for EventChannel Streams
- Issue: No tests verify that data callbacks, modem status, or USB state events flow correctly from native to Dart.
- Files: `D:\code\new_git_code\flutter\ch34\test\ch34_method_channel_test.dart`, `D:\code\new_git_code\flutter\ch34\test\ch34_test.dart`
- Risk: Stream subscription and cancellation logic may have bugs that only manifest at runtime.
- Priority: Medium
### No Tests for Exception Paths
- Issue: All tests use happy-path mock returns. No tests simulate errors, timeouts, or null responses.
- Files: `D:\code\new_git_code\flutter\ch34\test\ch34_test.dart`
- Risk: Error handling code is untested and may fail silently or crash.
- Priority: Medium
---
*Concerns audit: 2026-04-16*

View File

@@ -0,0 +1,237 @@
# Coding Conventions
**Analysis Date:** 2026-04-16
## Overview
This is a Flutter plugin package (`ch34`) that provides USB-to-serial communication for WCH CH34X series chips. The codebase is written in Dart with Android native code (Java). The plugin follows the standard Flutter plugin federated architecture pattern.
## Naming Patterns
**Files:**
- Dart files use `snake_case`: `ch34_manager.dart`, `ch34_platform_interface.dart`, `ch34_method_channel.dart`, `ch34_types.dart`
- Test files mirror source file names: `ch34_test.dart`, `ch34_method_channel_test.dart`
- Java native files use `PascalCase`: `Ch34Plugin.java`, `Ch34UsbStateStreamHandler.java`
**Classes:**
- PascalCase with `Ch34` prefix for public API: `Ch34Manager`, `Ch34Platform`, `MethodChannelCh34`, `Ch34Exception`
- Domain models without prefix: `GpioStatus`, `SerialParameter`, `ModemStatus`, `UsbDeviceInfo`
- Enum naming: `DataBits`, `StopBits`, `Parity`, `GpioDirection`, `GpioValue`, `SerialErrorType`
**Functions/Methods:**
- camelCase for all methods: `getPlatformVersion()`, `enumDevice()`, `openDevice()`, `setSerialParameter()`
- Callback-style methods use descriptive names: `setUsbStateListener()`, `registerDataCallback()`, `registerModemStatusCallback()`
- Private methods use underscore prefix: `_cancelDataSubscription()`
- Private constructor pattern: `Ch34Manager._()` (static-only class)
**Variables:**
- camelCase: `deviceName`, `serialNumber`, `gpioIndex`, `onStateChanged`
- Private fields use underscore prefix: `_dataSubscription`, `_modemCallback`
- Constants use const: `const MethodChannel('ch34')`
**Parameters:**
- Named parameters use curly braces for optional: `{int timeout = 0}`
- Required named parameters use `required` keyword in data classes
## Code Style
**Formatting:**
- Uses `flutter_lints` package (`^3.0.0`) with default `flutter.yaml` ruleset
- Config: `D:/code/new_git_code/flutter/ch34/analysis_options.yaml`
- Example app config: `D:/code/new_git_code/flutter/ch34/example/analysis_options.yaml` (same base, with commented-out custom rules)
- No custom lint rules are enabled beyond the default Flutter set
- No `.prettierrc` or other formatter config (uses Dart formatter defaults)
**SDK Constraints:**
- SDK: `>=3.4.3 <4.0.0` (`D:/code/new_git_code/flutter/ch34/pubspec.yaml`)
- Flutter: `>=3.3.0`
**Import Organization:**
1. Dart SDK imports first: `import 'dart:typed_data';`, `import 'dart:async';`
2. Flutter framework imports: `import 'package:flutter/services.dart';`
3. Third-party package imports: `import 'package:plugin_platform_interface/plugin_platform_interface.dart';`
4. Relative local imports last: `import 'ch34_platform_interface.dart';`, `import 'types/ch34_types.dart';`
- Groups separated by blank lines
**Path Aliases:**
- No path aliases detected; all imports use relative paths within the package
## Patterns
**Static Manager Pattern:**
`D:/code/new_git_code/flutter/ch34/lib/src/ch34_manager.dart` uses a private constructor (`Ch34Manager._()`) with all static methods, delegating to `Ch34Platform.instance`. This provides a clean public API without requiring instantiation.
```dart
class Ch34Manager {
Ch34Manager._();
static Future<String?> getPlatformVersion() {
return Ch34Platform.instance.getPlatformVersion();
}
}
```
**Platform Interface (Federated Plugin):**
`D:/code/new_git_code/flutter/ch34/lib/src/ch34_platform_interface.dart` extends `PlatformInterface` from `plugin_platform_interface`. Uses token-based verification for instance safety:
```dart
abstract class Ch34Platform extends PlatformInterface {
Ch34Platform() : super(token: _token);
static final Object _token = Object();
static Ch34Platform? _instance;
static Ch34Platform get instance { ... }
static set instance(Ch34Platform instance) { ... }
}
```
**MethodChannel + EventChannel Pattern:**
`D:/code/new_git_code/flutter/ch34/lib/src/ch34_method_channel.dart` uses:
- `MethodChannel` for request-response calls: `methodChannel.invokeMethod<bool>('openDevice', {...})`
- `EventChannel` for streaming data: `dataEventChannel.receiveBroadcastStream({'deviceName': deviceName}).listen(...)`
- `@visibleForTesting` annotation for testable fields (channel names)
**Data Class Pattern:**
`D:/code/new_git_code/flutter/ch34/lib/src/types/ch34_types.dart` uses immutable classes with:
- `const` constructors
- `fromMap()` factory for deserialization
- `toMap()` method for serialization
- `toString()` override for debugging
- `copyWith()` for mutable updates (e.g., `SerialParameter.copyWith()`)
- `==`/`hashCode` override for equality (e.g., `UsbDeviceInfo`)
```dart
class SerialParameter {
const SerialParameter({
this.baud = 115200,
this.dataBits = DataBits.bits8,
...
});
factory SerialParameter.fromMap(Map<dynamic, dynamic> map) { ... }
Map<String, dynamic> toMap() { ... }
SerialParameter copyWith({ ... }) { ... }
}
```
**Enum with Associated Values:**
Enums use associated integer values with `fromValue()` factory:
```dart
enum DataBits {
bits5(5), bits6(6), bits7(7), bits8(8);
const DataBits(this.value);
final int value;
static DataBits fromValue(int value) =>
DataBits.values.firstWhere((e) => e.value == value, orElse: () => DataBits.bits8);
}
```
**Null-Safe Default Handling:**
Method channel results consistently use null-coalescing for safe defaults:
```dart
return result ?? false; // boolean methods
return result ?? 0; // integer methods
return result ?? []; // list methods
return result ?? Uint8List(0); // byte data
```
**Section Comments:**
Code is organized with clear separator comments:
```dart
/// ==================== 基础方法 ====================
/// ==================== 设备枚举与识别 ====================
/// ==================== 数据读写 ====================
```
## Error Handling
**Custom Exception:**
`D:/code/new_git_code/flutter/ch34/lib/src/ch34_method_channel.dart` defines `Ch34Exception`:
```dart
class Ch34Exception implements Exception {
const Ch34Exception(this.message);
final String message;
@override
String toString() => 'Ch34Exception: $message';
}
```
**Usage Patterns:**
- Platform interface methods document `@throws Ch34Exception` in JSDoc comments for methods that may fail
- Actual throwing is limited: only GPIO methods throw `Ch34Exception` when native returns null
- Most boolean methods return `false` on failure rather than throwing
- Example app uses generic `catch (e)` for error display in UI:
```dart
} catch (e) {
setState(() {
_status = '扫描失败: $e';
});
}
```
- `StateError` thrown when platform instance not initialized:
```dart
throw StateError('Ch34Platform.instance has not been initialized...');
```
**Stream Error Handling:**
EventChannel streams use `onError` callback with `debugPrint`:
```dart
onError: (error) {
debugPrint('CH34 data callback error: $error');
}
```
## Logging
**Framework:** Flutter's `debugPrint` from `package:flutter/foundation.dart`
**Patterns:**
- Used only in error callbacks within the method channel implementation
- Log messages are descriptive with context prefix: `'CH34 USB state error: $error'`
- No logging framework beyond `debugPrint`
- Debug mode toggle available via `Ch34Manager.setDebug(bool)` (passes to native side)
## Comments & Documentation
**API Documentation:**
- All public methods have `///` Dart doc comments in Chinese
- Consistent param/return documentation using `@param` and `@return`
- Exception documentation using `@throws`
- Library-level docs at `D:/code/new_git_code/flutter/ch34/lib/ch34.dart` include usage example in code block
**Inline Comments:**
- Chinese section headers for method grouping
- Method-level comments explain purpose, parameters, and return values
**JSDoc/TSDoc Style:**
- Uses `///` (not `/** */`) for doc comments
- Parameter docs: `/// @param deviceName 设备名称。`
- Return docs: `/// @return \`true\` 成功,\`false\` 失败。`
## Module Design
**Exports:**
- Main entry `D:/code/new_git_code/flutter/ch34/lib/ch34.dart` exports all public types:
```dart
export 'src/ch34_manager.dart';
export 'src/types/ch34_types.dart';
export 'src/ch34_platform_interface.dart' show Ch34Platform;
export 'src/ch34_method_channel.dart' show Ch34Exception;
```
- Backward-compatible barrel files at `lib/` root:
- `D:/code/new_git_code/flutter/ch34/lib/ch34_platform_interface.dart`
- `D:/code/new_git_code/flutter/ch34/lib/ch34_method_channel.dart`
**Layer Separation:**
```
lib/ch34.dart -> Public API entry (exports)
lib/src/ch34_manager.dart -> Static facade (public-facing)
lib/src/ch34_platform_interface.dart -> Abstract interface
lib/src/ch34_method_channel.dart -> Concrete implementation
lib/src/types/ch34_types.dart -> Data types and enums
```
---
*Convention analysis: 2026-04-16*

View File

@@ -0,0 +1,95 @@
# External Integrations
**Analysis Date:** 2026-04-16
## Hardware/Device
**WCH CH34X Series USB-to-Serial Chips:**
- **CH340, CH341, CH342, CH343, CH344, CH347** - Standard USB-to-serial converters
- **CH9101, CH9102, CH9103, CH9104** - Newer generation USB-to-serial chips
- **CH9143** - Multi-port USB-to-serial chip
**SDK/Driver:**
- Library: `CH34XUARTDriver.jar` (local JAR, 140KB)
- Location: `android/libs/CH34XUARTDriver.jar`
- Package: `cn.wch.uartlib`
- Manager: `WCHUARTManager.getInstance()` - Singleton pattern
**USB Host Protocol:**
- Android USB Host API (`android.hardware.usb.UsbManager`)
- Feature declaration: `android.hardware.usb.host` (optional, in `android/src/main/AndroidManifest.xml`)
- Permission model: Runtime USB permission via `WCHUARTManager.requestPermission()`
**VID/PID:**
- Default VID/PID managed by WCH driver
- Custom VID/PID supported via `Ch34Manager.addNewHardware(vid, pid)` - maps to `WCHUARTManager.addNewHardware()`
## Communication Channels
**MethodChannel (`'ch34'`):**
- Location: `lib/src/ch34_method_channel.dart` (Dart), `android/src/main/java/com/example/ch34/Ch34Plugin.java` (Java)
- 33 methods mapped: `enumDevice`, `openDevice`, `requestPermission`, `writeData`, `readData`, `setDtr`, `setRts`, `setBreak`, `enableGpio`, `setGpioVal`, `getGpioVal`, etc.
- Error codes: `ENUM_DEVICE_FAILED`, `OPEN_DEVICE_FAILED`, `PERMISSION_DENIED`, `WRITE_DATA_FAILED`, `GPIO_CHECK_FAILED`, etc.
**EventChannel (`'ch34/data'`):**
- Location: `android/src/main/java/com/example/ch34/Ch34DataStreamHandler.java`
- Payload: `byte[]` serial data bytes
- Registered via `manager.registerDataCallback(device, callback)`
**EventChannel (`'ch34/modem'`):**
- Location: `android/src/main/java/com/example/ch34/Ch34ModemStreamHandler.java`
- Payload: Map with `cts`, `dsr`, `ri`, `dcd` boolean values
- Registered via `manager.registerModemStatusCallback(device, IModemStatus)`
**EventChannel (`'ch34/usb_state'`):**
- Location: `android/src/main/java/com/example/ch34/Ch34UsbStateStreamHandler.java`
- Payload: Map with `deviceName` (String) and `connected` (boolean)
- Triggered by Android broadcast receiver (`UsbManager.ACTION_USB_DEVICE_ATTACHED`, `UsbManager.ACTION_USB_DEVICE_DETACHED`)
## Data Sources
**Serial Data:**
- Read: `Ch34Manager.readData(deviceName, serialNumber)` -> `Uint8List` (blocking)
- Stream: `Ch34Manager.registerDataCallback(deviceName, serialNumber, onData)` -> EventChannel push
- Write: `Ch34Manager.writeData(deviceName, serialNumber, data)` -> `int` bytes written
**GPIO Data:**
- Query: `Ch34Manager.queryGpioStatus(deviceName, gpioIndex)` -> `GpioStatus`
- Query all: `Ch34Manager.queryAllGpioStatus(deviceName)` -> `List<GpioStatus>`
- Control: `Ch34Manager.setGpioVal(deviceName, gpioIndex, value)` / `Ch34Manager.getGpioVal(deviceName, gpioIndex)`
**Modem Status:**
- `ModemStatus` class with fields: `cts` (Clear To Send), `dsr` (Data Set Ready), `ri` (Ring Indicator), `dcd` (Data Carrier Detect)
- Registered via `Ch34Manager.registerModemStatusCallback(deviceName, onModemStatus)`
**Error Types:**
- `SerialErrorType.framingError` - Framing error
- `SerialErrorType.parityError` - Parity error
- `SerialErrorType.overrunError` - Overrun error
- `SerialErrorType.breakInterrupt` - Break interrupt
- Query via `Ch34Manager.querySerialErrorCount(deviceName, serialNumber, errorType)`
## Authentication
**USB Device Permission:**
- Android runtime USB permission dialog via `WCHUARTManager.requestPermission(context, device)`
- No API keys, tokens, or credentials required
- Permission flow: `openDevice` -> if no permission, auto-request -> retry after 2000ms delay
## Third-party Services
**None.** This is a pure hardware driver plugin with no external API integrations, cloud services, or network dependencies.
## CI/CD & Deployment
**Hosting:** Not applicable (Flutter plugin package, published to pub.dev)
**CI Pipeline:** Not detected (no `.github/`, `.gitlab-ci.yml`, or `.circleci/` found)
## Webhooks & Callbacks
**Incoming:** None
**Outgoing:** None
---
*Integration audit: 2026-04-16*

104
.planning/codebase/STACK.md Normal file
View File

@@ -0,0 +1,104 @@
# Technology Stack
**Analysis Date:** 2026-04-16
## Languages
**Primary:**
- **Dart** >=3.4.3 - Flutter plugin Dart-side code (`lib/`, `test/`)
- **Java** 8 - Android native plugin implementation (`android/src/main/java/`)
**Secondary:**
- **Kotlin** 1.7.10 - Declared in example app Gradle plugins (`example/android/settings.gradle`) but not actively used in plugin code
## Runtime
**Environment:**
- **Flutter** >=3.3.0
- **Android SDK** - compileSdk 34, minSdk 21
**Package Manager:**
- **pub** (Dart) - Lockfile: `pubspec.lock` present
- **Gradle** 7.3.0 - Android build system
## Frameworks
**Core:**
- **Flutter** >=3.3.0 - Cross-platform plugin framework
- **Flutter Plugin API** - `FlutterPlugin`, `MethodCallHandler`, `MethodChannel`, `EventChannel`
**Testing:**
- **flutter_test** (Flutter SDK) - Unit testing framework
- **JUnit** 4.13.2 - Android native unit testing
- **Mockito** 5.0.0 - Java mocking for Android tests
- **integration_test** (Flutter SDK) - Integration testing
**Build/Dev:**
- **flutter_lints** ^3.0.0 - Lint rules
- **Gradle** 7.3.0 - Android build automation
## Key Dependencies
**Critical:**
- **plugin_platform_interface** ^2.0.2 - Federated plugin platform interface pattern (`Ch34Platform` extends `PlatformInterface`)
- **CH34XUARTDriver** (local JAR, 140KB) - WCH official UART library, located at `android/libs/CH34XUARTDriver.jar`. Provides `WCHUARTManager`, `ChipType2`, `IDataCallback`, `IModemStatus`, `GPIO_Status` etc.
**Infrastructure:**
- **cupertino_icons** ^1.0.6 - Example app only, Material/Cupertino icon support
**Android Native Dependencies (WCH SDK):**
- `cn.wch.uartlib.WCHUARTManager` - Core manager singleton
- `cn.wch.uartlib.chipImpl.type.ChipType2` - Chip type identification
- `cn.wch.uartlib.callback.IDataCallback` - Serial data callback interface
- `cn.wch.uartlib.callback.IModemStatus` - Modem status callback interface
- `cn.wch.uartlib.callback.IUsbStateChange` - USB state change callback
- `cn.wch.uartlib.exception.*` - Exception classes (ChipException, NoPermissionException, UartLibException)
## Configuration
**Environment:**
- `analysis_options.yaml` - Includes `package:flutter_lints/flutter.yaml`
- No `.env` or environment variable configuration required
- Secrets stored in example `local.properties` (flutter.sdk path)
**Build:**
- `pubspec.yaml` - Main plugin manifest, package: `ch34`, version `1.0.0`
- `pubspec.lock` - Dependency lockfile
- `android/build.gradle` - Android library build config (namespace: `com.example.ch34`)
- `android/settings.gradle` - Project name: `ch34`
- `example/android/build.gradle` - Example app root build
- `example/android/app/build.gradle` - Example app module (namespace: `com.example.ch34_example`)
- `example/android/settings.gradle` - Gradle plugins including dev.flutter.flutter-plugin-loader
## Platform Requirements
**Development:**
- Flutter SDK >=3.3.0
- Dart SDK >=3.4.3
- Android SDK compileSdk 34
- Java 8+ (sourceCompatibility/targetCompatibility = JavaVersion.VERSION_1_8)
**Production:**
- Android device with USB Host support (`android.hardware.usb.host` feature declared in `android/src/main/AndroidManifest.xml`)
- Android minSdk 21 (Android 5.0 Lollipop)
- Physical CH34X series USB-to-serial hardware connected via USB OTG
## Supported Chip Models
CH340, CH341, CH342, CH343, CH344, CH347, CH9101, CH9102, CH9103, CH9104, CH9143
## Build System
**Flutter Plugin:** Standard Flutter plugin structure with Android platform implementation.
**Communication Pattern:**
- `MethodChannel('ch34')` - 33 method calls for device control
- `EventChannel('ch34/data')` - Serial data streaming
- `EventChannel('ch34/modem')` - Modem status streaming
- `EventChannel('ch34/usb_state')` - USB plug/unplug events
**Architecture Pattern:** Federated plugin with platform interface (`Ch34Platform`), method channel implementation (`MethodChannelCh34`), and manager facade (`Ch34Manager`).
---
*Stack analysis: 2026-04-16*

View File

@@ -0,0 +1,217 @@
# Codebase Structure
**Analysis Date:** 2026-04-16
## Directory Layout
```
ch34/ # Flutter plugin root
├── lib/ # Dart source code
│ ├── ch34.dart # Main library entry point (exports)
│ ├── ch34_platform_interface.dart # Backward compat: re-exports src/
│ ├── ch34_method_channel.dart # Backward compat: re-exports src/
│ └── src/ # Core implementation
│ ├── ch34_manager.dart # Public static API facade
│ ├── ch34_platform_interface.dart # Abstract platform interface
│ ├── ch34_method_channel.dart # MethodChannel implementation
│ └── types/
│ └── ch34_types.dart # All data types and enums
├── android/ # Android native code
│ ├── build.gradle # Android library build config
│ ├── settings.gradle # Gradle settings
│ ├── libs/ # Native dependency JARs
│ │ └── CH34XUARTDriver.jar # WCH UART library
│ └── src/
│ ├── main/java/com/example/ch34/
│ │ ├── Ch34Plugin.java # Main plugin + MethodCallHandler
│ │ ├── Ch34DataStreamHandler.java # Data EventChannel handler
│ │ ├── Ch34ModemStreamHandler.java # Modem EventChannel handler
│ │ ├── Ch34UsbStateStreamHandler.java # USB hotplug handler
│ │ └── Ch34TypeConverter.java # Type conversion utilities
│ └── test/java/com/example/ch34/
│ └── Ch34PluginTest.java # Unit tests
├── example/ # Example Flutter app
│ ├── lib/
│ │ └── main.dart # Example app with device scan/connect/send
│ ├── test/
│ │ └── widget_test.dart # Widget test (stub)
│ └── integration_test/
│ └── plugin_integration_test.dart # Integration test
├── test/ # Plugin unit tests
│ ├── ch34_test.dart # Platform interface + manager tests
│ └── ch34_method_channel_test.dart # MethodChannel mock tests
├── docs/
│ └── CH34X-api_docs.md # WCH API documentation reference
├── pubspec.yaml # Plugin package manifest
├── analysis_options.yaml # Dart linting config
├── CHANGELOG.md # Version history
├── LICENSE # License file
├── README.md # Plugin documentation
└── .gitignore # Git ignore rules
```
## Directory Purposes
### `lib/` - Dart Source
- **Purpose:** All Dart code for the Flutter plugin
- **Key files:**
- `lib/ch34.dart` - Primary consumer import: `import 'package:ch34/ch34.dart';`
- `lib/src/ch34_manager.dart` - Static facade, all public APIs
- `lib/src/ch34_platform_interface.dart` - Abstract interface with 33 methods
- `lib/src/ch34_method_channel.dart` - MethodChannel + EventChannel implementation
- `lib/src/types/ch34_types.dart` - Shared data models and enums
### `android/` - Android Native
- **Purpose:** Java implementation bridging to WCH CH34X UART library
- **Key files:**
- `android/src/main/java/com/example/ch34/Ch34Plugin.java` - Plugin entry point (788 lines)
- `android/build.gradle` - Library configuration (compileSdk 34, minSdk 21, Java 8)
- `android/libs/CH34XUARTDriver.jar` - WCH vendor SDK (binary dependency)
### `example/` - Sample Application
- **Purpose:** Demonstrates plugin usage for development and testing
- **Key files:**
- `example/lib/main.dart` - Full working example: scan devices, open, set params, send/receive data
### `test/` - Plugin Tests
- **Purpose:** Unit tests for the Dart plugin code
- **Contains:** MethodChannel mock tests, platform interface mock implementation
## Key File Locations
**Entry Points:**
- `lib/ch34.dart`: Main library entry, consumer-facing import
- `android/src/main/java/com/example/ch34/Ch34Plugin.java`: Android plugin entry, registered via `pubspec.yaml`
**Configuration:**
- `pubspec.yaml`: Plugin declaration, Android platform registration (`package: com.example.ch34`, `pluginClass: Ch34Plugin`)
- `android/build.gradle`: Android library build configuration
- `analysis_options.yaml`: Dart linting (extends `flutter_lints`)
**Core Logic:**
- `lib/src/ch34_manager.dart`: Static API facade (all consumer methods)
- `lib/src/ch34_platform_interface.dart`: Abstract interface contract
- `lib/src/ch34_method_channel.dart`: Channel communication implementation
- `android/src/main/java/com/example/ch34/Ch34Plugin.java`: Native bridge logic
**Types:**
- `lib/src/types/ch34_types.dart`: All data classes (`UsbDeviceInfo`, `SerialParameter`, `ModemStatus`, `GpioStatus`) and enums (`DataBits`, `StopBits`, `Parity`, `GpioDirection`, `GpioValue`, `SerialErrorType`)
**Testing:**
- `test/ch34_method_channel_test.dart`: Unit tests with mock platform
- `test/ch34_test.dart`: Integration-style tests with real MethodChannel mock
- `example/integration_test/plugin_integration_test.dart`: On-device integration test
## Naming Conventions
**Files:**
- Dart: `snake_case.dart` for all files (e.g., `ch34_manager.dart`, `ch34_types.dart`)
- Java: `PascalCase.java` for all classes (e.g., `Ch34Plugin.java`, `Ch34TypeConverter.java`)
**Dart Classes:**
- Public API class: `Ch34Manager` (static methods, facade pattern)
- Platform interface: `Ch34Platform` (abstract, extends `PlatformInterface`)
- Channel implementation: `MethodChannelCh34` (extends `Ch34Platform`)
- Data types: `UsbDeviceInfo`, `SerialParameter`, `ModemStatus`, `GpioStatus`
- Enums: `DataBits`, `StopBits`, `Parity`, `GpioDirection`, `GpioValue`, `SerialErrorType`
- Exception: `Ch34Exception`
**Java Classes:**
- Main plugin: `Ch34Plugin` (implements `FlutterPlugin`, `MethodCallHandler`)
- Stream handlers: `Ch34DataStreamHandler`, `Ch34ModemStreamHandler`, `Ch34UsbStateStreamHandler`
- Utility: `Ch34TypeConverter` (static methods only, private constructor)
**MethodChannel names:**
- Method channel: `'ch34'`
- Data event channel: `'ch34/data'`
- Modem event channel: `'ch34/modem'`
- USB state event channel: `'ch34/usb_state'`
**Method names:** Match Dart method names exactly (e.g., `enumDevice`, `openDevice`, `setSerialParameter`, `writeData`, `readData`)
## Where to Add New Code
**New Dart API Method:**
1. Add abstract method signature to `lib/src/ch34_platform_interface.dart`
2. Add concrete implementation to `lib/src/ch34_method_channel.dart` (using `methodChannel.invokeMethod()`)
3. Add static wrapper to `lib/src/ch34_manager.dart` (delegating to `Ch34Platform.instance`)
4. Add mock implementation in `test/ch34_method_channel_test.dart` for testing
5. Add native handler in `android/src/main/java/com/example/ch34/Ch34Plugin.java`
**New EventChannel (streaming):**
1. Create new `*StreamHandler.java` in `android/src/main/java/com/example/ch34/`
2. Register in `Ch34Plugin.onAttachedToEngine()`
3. Add `EventChannel` and `StreamSubscription` in `lib/src/ch34_method_channel.dart`
4. Add platform interface methods to `Ch34Platform`
5. Add static wrappers to `Ch34Manager`
**New Data Type:**
- Add to `lib/src/types/ch34_types.dart`
- Add corresponding conversion in `android/src/main/java/com/example/ch34/Ch34TypeConverter.java`
**New Native Utility:**
- Add to `android/src/main/java/com/example/ch34/` as a new Java class or extend existing
## Module Boundaries
### Dart Layer
```
lib/ch34.dart (exports)
|
v
lib/src/ch34_manager.dart (static facade)
|
v
lib/src/ch34_platform_interface.dart (abstract contract)
|
v
lib/src/ch34_method_channel.dart (concrete implementation)
|
+---> lib/src/types/ch34_types.dart (shared types)
```
### Native Layer
```
Ch34Plugin.java (MethodCallHandler + 3 EventChannels)
|
+---> Ch34DataStreamHandler.java (data streaming)
+---> Ch34ModemStreamHandler.java (modem status streaming)
+---> Ch34UsbStateStreamHandler.java (USB hotplug streaming)
+---> Ch34TypeConverter.java (type conversion utilities)
|
v
CH34XUARTDriver.jar (WCH vendor SDK)
```
### Platform Bridge
```
Dart: MethodChannel('ch34') <---> Java: Ch34Plugin (MethodCallHandler)
Dart: EventChannel('ch34/data') <---> Java: Ch34DataStreamHandler
Dart: EventChannel('ch34/modem') <---> Java: Ch34ModemStreamHandler
Dart: EventChannel('ch34/usb_state') <---> Java: Ch34UsbStateStreamHandler
```
## Special Directories
**`android/libs/`:**
- Purpose: Contains binary JAR dependency (`CH34XUARTDriver.jar`)
- Generated: No, committed to repo
- Referenced by: `android/build.gradle` via `flatDir` repository
**`example/`:**
- Purpose: Self-contained Flutter app demonstrating plugin usage
- Generated: No, committed to repo
- Note: The example app package is `ch34_example`
**`docs/`:**
- Purpose: API documentation reference for WCH WCHUARTManager
- Contains: `CH34X-api_docs.md` - comprehensive API documentation in Markdown
**`lib/` root-level files:**
- Purpose: Backward compatibility re-exports
- `lib/ch34_platform_interface.dart` exports `src/ch34_platform_interface.dart`
- `lib/ch34_method_channel.dart` exports `src/ch34_method_channel.dart`
---
*Structure analysis: 2026-04-16*

View File

@@ -0,0 +1,232 @@
# Testing Patterns
**Analysis Date:** 2026-04-16
## Framework
**Runner:**
- `flutter_test` (Flutter SDK built-in)
- `integration_test` (Flutter SDK built-in, for E2E/integration tests)
- No explicit `test` package dependency (uses Flutter's bundled test framework)
**Assertion Library:**
- Built-in `flutter_test` matchers (`expect`, `findsOneWidget`, `isEmpty`, `isInstanceOf`)
**Dependencies:**
- `flutter_test` (sdk: flutter) - Unit and widget testing
- `flutter_lints` (^3.0.0) - Code analysis
- `plugin_platform_interface` (^2.0.2) - Required for mock platform interface mixin
**Run Commands:**
```bash
flutter test # Run all unit tests
flutter test --coverage # Run tests with coverage
flutter test test/ch34_test.dart # Run a specific test file
flutter test integration_test/ # Run integration tests (from example/)
```
## Test Structure
**Test File Organization:**
- Plugin tests in `D:/code/new_git_code/flutter/ch34/test/`:
- `ch34_test.dart` - Unit tests with mock platform
- `ch34_method_channel_test.dart` - MethodChannel-level tests with mock handler
- Example app tests in `D:/code/new_git_code/flutter/ch34/example/test/`:
- `widget_test.dart` - Widget tests for example app
- Integration tests in `D:/code/new_git_code/flutter/ch34/example/integration_test/`:
- `plugin_integration_test.dart` - End-to-end plugin tests on real device
**Naming:**
- Test files: `{source_file}_test.dart` pattern
- Integration test files: `{feature}_integration_test.dart`
**Test Entry Pattern:**
All tests call framework initialization before running:
```dart
// Unit tests
TestWidgetsFlutterBinding.ensureInitialized();
// Integration tests
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
```
**Suite Organization:**
Unit tests follow a consistent pattern:
```dart
// D:/code/new_git_code/flutter/ch34/test/ch34_test.dart
void main() {
MethodChannelCh34.registerDefault();
final Ch34Platform initialPlatform = Ch34Platform.instance;
test('$MethodChannelCh34 is the default instance', () {
expect(initialPlatform, isInstanceOf<MethodChannelCh34>());
});
test('getPlatformVersion', () async {
MockCh34Platform fakePlatform = MockCh34Platform();
Ch34Platform.instance = fakePlatform;
expect(await Ch34Manager.getPlatformVersion(), '42');
});
test('enumDevice returns empty list', () async {
MockCh34Platform fakePlatform = MockCh34Platform();
Ch34Platform.instance = fakePlatform;
final devices = await Ch34Manager.enumDevice();
expect(devices, isEmpty);
});
}
```
## Mocking
**Framework:** Manual mock classes (no mockito or mocktail dependency)
**Platform Mock Pattern:**
`D:/code/new_git_code/flutter/ch34/test/ch34_test.dart` uses `MockPlatformInterfaceMixin` for creating test doubles:
```dart
class MockCh34Platform
with MockPlatformInterfaceMixin
implements Ch34Platform {
@override
Future<String?> getPlatformVersion() => Future.value('42');
@override
Future<List<UsbDeviceInfo>> enumDevice() => Future.value([]);
// ... all 30+ methods must be overridden
}
```
**Mock Usage Pattern:**
Mock instances are swapped into the platform interface before each test:
```dart
MockCh34Platform fakePlatform = MockCh34Platform();
Ch34Platform.instance = fakePlatform;
expect(await Ch34Manager.getPlatformVersion(), '42');
```
**MethodChannel Mock Pattern:**
`D:/code/new_git_code/flutter/ch34/test/ch34_method_channel_test.dart` uses `setMockMethodCallHandler` for low-level channel mocking:
```dart
const MethodChannel channel = MethodChannel('ch34');
setUp(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(
channel,
(MethodCall methodCall) async {
return '42'; // Returns same value for all method calls
},
);
});
tearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, null);
});
```
**What to Mock:**
- Platform interface layer (`Ch34Platform`) for testing `Ch34Manager` delegation
- `MethodChannel` for testing `MethodChannelCh34` channel mapping
**What NOT to Mock:**
- Native Android code (tested via integration tests on real device)
- EventChannel streams (not currently mocked in any test)
## Coverage
**Requirements:** No coverage target enforced
**Current State:**
- Only 2 unit test assertions beyond boilerplate (`getPlatformVersion`, `enumDevice returns empty list`)
- MethodChannel test has only 1 assertion (`getPlatformVersion`)
- Integration test has 1 assertion (`getPlatformVersion` on real device)
- Most platform methods have zero test coverage (GPIO, serial I/O, modem status, signal control, etc.)
**View Coverage:**
```bash
flutter test --coverage
# Coverage written to coverage/lcov.info
```
## Test Locations
**Unit Tests:**
- `D:/code/new_git_code/flutter/ch34/test/ch34_test.dart` - Ch34Manager + mock platform
- `D:/code/new_git_code/flutter/ch34/test/ch34_method_channel_test.dart` - MethodChannel tests
**Integration Tests:**
- `D:/code/new_git_code/flutter/ch34/example/integration_test/plugin_integration_test.dart` - Real device tests
**Widget Tests:**
- `D:/code/new_git_code/flutter/ch34/example/test/widget_test.dart` - Example app widget test (currently outdated - looks for "Running on:" text that no longer exists)
## Test Types
**Unit Tests:**
- Scope: Test `Ch34Manager` delegation to mocked platform
- Approach: Replace `Ch34Platform.instance` with `MockCh34Platform`
- Current coverage: 2 methods tested out of 30+ API methods
**Integration Tests:**
- Framework: `integration_test` package
- Approach: Run on real Android device with actual USB hardware
- Current coverage: 1 test (`getPlatformVersion`)
**E2E Tests:**
- No dedicated E2E test framework beyond `integration_test`
## Common Patterns
**Async Testing:**
All platform method tests use `async/await`:
```dart
test('enumDevice returns empty list', () async {
MockCh34Platform fakePlatform = MockCh34Platform();
Ch34Platform.instance = fakePlatform;
final devices = await Ch34Manager.enumDevice();
expect(devices, isEmpty);
});
```
**Error Testing:**
Mock throws exceptions to test error paths:
```dart
@override
Future<GpioStatus> queryGpioStatus(String deviceName, int gpioIndex) =>
throw const Ch34Exception('Not supported');
```
**Setup/Teardown Pattern:**
```dart
setUp(() {
// Configure mock method channel handler
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, handler);
});
tearDown(() {
// Clean up mock handler
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, null);
});
```
## CI Testing
**CI Pipeline:** Not detected - no `.github/workflows/`, `.gitlab-ci.yml`, or `azure-pipelines.yml` found.
**Recommendation:** Tests should be run via `flutter analyze && flutter test` in any CI pipeline.
---
*Testing analysis: 2026-04-16*