Просмотр исходного кода

Update 优化win识别电子编号进行上传

Yue 3 недель назад
Родитель
Сommit
73dfe4e3cc

+ 22 - 18
UI/CF.APP/chicken_farm/lib/components/vb_win_electronic_id_field.dart

@@ -77,29 +77,33 @@ class _VbWinElectronicIdFieldState extends State<VbWinElectronicIdField> {
     super.dispose();
   }
 
-  void _handleTagReceived(String data) {
-    logger.d('电子标签识别成功,已识别标签:$data');
+  void _handleTagReceived(dynamic data) {
+    logger.d('电子标签识别数据接收: $data (类型: ${data.runtimeType})');
 
-    if (_isMultiple) {
-      // 多标签模式:添加到列表中,避免重复
-      if (!_scannedTags.contains(data)) {
-        _scannedTags.add(data);
-
-        setState(() {
-          _isScanning = false;
-        });
+    if (data is List<String>) {
+      // 接收到tag列表,更新扫描到的标签
+      if (_isMultiple) {
+        // 多标签模式:合并标签列表,避免重复
+        for (String tag in data) {
+          if (!_scannedTags.contains(tag)) {
+            _scannedTags.add(tag);
+          }
+        }
 
         if (widget.onIdsScanned != null) {
           widget.onIdsScanned!(_scannedTags);
         }
-      }
-    } else {
-      // 单标签模式:直接使用接收到的数据
-      setState(() {
-        _isScanning = false;
-      });
+      } else if (data.isNotEmpty) {
+        // 单标签模式:使用最新的标签
+        String latestTag = data.last;
 
-      if (widget.onIdScanned != null) {
+        if (widget.onIdScanned != null) {
+          widget.onIdScanned!(latestTag);
+        }
+      }
+    } else if (data is String) {
+      // 为兼容旧格式,如果接收到字符串,则按单标签处理
+      if (!_isMultiple && widget.onIdScanned != null) {
         widget.onIdScanned!(data);
       }
     }
@@ -134,7 +138,7 @@ class _VbWinElectronicIdFieldState extends State<VbWinElectronicIdField> {
         _isScanning = false;
       });
     }
-    _autoStopTimer = Timer(const Duration(seconds: 5), () {
+    _autoStopTimer = Timer(const Duration(milliseconds: 1100), () {
       if (_isScanning) {
         // 确保仍在扫描状态
         _stopScan();

+ 60 - 12
UI/CF.APP/chicken_farm/lib/core/services/win/win_reader_channel.dart

@@ -149,9 +149,7 @@ class WinReaderChannel {
   // 设置功率
   static Future<bool> setPower(int power) async {
     try {
-      final result = await _channel.invokeMethod('setPower', {
-        'power': power,
-      });
+      final result = await _channel.invokeMethod('setPower', {'power': power});
       return result ?? false;
     } on PlatformException catch (e) {
       logger.e('调用setPower方法时发生平台异常: ${e.message}');
@@ -165,16 +163,66 @@ class WinReaderChannel {
     }
   }
 
-  // 监听设备数据流
-  static Stream<String> get onDataReceived {
-    return _eventChannel.receiveBroadcastStream().map((dynamic data) {
-      if (data is String) {
-        // 去除所有空格(包括首尾空格和中间空格)
-        return data.replaceAll(RegExp(r'\s+'), '').trim();
+  // 监听数据回调流
+  static Stream<List<String>>? _dataStream;
+  static Stream<List<String>>? get dataStream {
+    return _dataStream ??= _eventChannel.receiveBroadcastStream().map((
+      dynamic event,
+    ) {
+      try {
+        // 尝试解析为JSON数组格式的tag列表
+        if (event is String) {
+          // 检查是否是JSON数组格式
+          if (event.startsWith('[') && event.endsWith(']')) {
+            // 解析JSON数组并返回tag列表
+            return _parseTagList(event);
+          } else {
+            // 如果不是JSON数组,则将单个字符串作为列表返回
+            return [event];
+          }
+        } else if (event is List) {
+          // 如果事件本身就是列表,将其转换为字符串列表
+          return event.map((e) => e?.toString() ?? '').toList().cast<String>();
+        }
+        // 默认返回空列表
+        return [];
+      } catch (e) {
+        logger.e('解析数据回调事件失败: $e, 原始数据: $event');
+        return [];
       }
-      return '';
     });
   }
 
-  // 监听连接状态事件
-}
+  // 解析tag列表的辅助方法
+  static List<String> _parseTagList(String jsonString) {
+    try {
+      // 简单解析JSON数组格式的字符串
+      // 例如: ["tag1", "tag2", "tag3"]
+      final trimmed = jsonString.trim().substring(
+        1,
+        jsonString.length - 1,
+      ); // 移除首尾的方括号
+
+      if (trimmed.isEmpty) {
+        return [];
+      }
+
+      // 分割并去除引号
+      final tags = <String>[];
+      final parts = trimmed.split(',').map((part) => part.trim()).toList();
+
+      for (final part in parts) {
+        var tag = part;
+        if (tag.startsWith('"') && tag.endsWith('"')) {
+          tag = tag.substring(1, tag.length - 1); // 移除首尾引号
+        }
+        tags.add(tag);
+      }
+
+      return tags;
+    } catch (e) {
+      logger.e('解析tag列表失败: $e, 原始数据: $jsonString');
+      return [];
+    }
+  }
+}

+ 15 - 15
UI/CF.APP/chicken_farm/lib/core/services/win/win_reader_manager.dart

@@ -4,7 +4,7 @@ import 'package:chicken_farm/core/utils/toast.dart';
 import 'win_reader_channel.dart';
 
 typedef OnConnectCallback = void Function(bool success);
-typedef OnDataReceivedCallback = void Function(String data);
+typedef OnDataReceivedCallback = void Function(List<String> data);
 typedef OnErrorCallback = void Function(String error);
 
 /// Windows读卡器管理器 (单例模式)
@@ -46,14 +46,14 @@ class WinReaderManager {
     _isInitialized = true;
 
     // 监听数据流 - 确保在任何读取操作前已经订阅
-    _dataSubscription = WinReaderChannel.onDataReceived.listen(
+    _dataSubscription = WinReaderChannel.dataStream?.listen(
       _handleDataReceived,
       onError: (error) {
         logger.e('读卡器数据接收错误: $error');
         _onError?.call(error.toString());
       },
     );
-    
+
     logger.i("WinReaderManager 初始化完成,已订阅数据流");
   }
 
@@ -119,28 +119,28 @@ class WinReaderManager {
   /// 自动扫描并连接到第一个USB设备
   Future<bool> autoConnect() async {
     logger.i("开始自动连接设备");
-    
+
     // 先扫描USB设备
     List<String>? devices = await scanUsb();
-    
+
     if (devices == null || devices.isEmpty) {
       logger.e("未找到任何USB设备");
       _onError?.call("未找到任何USB设备");
       return false;
     }
-    
+
     logger.i("找到 ${devices.length} 个USB设备: $devices");
-    
+
     // 连接到第一个设备(索引为0)
     int deviceIndex = 0;
     bool result = await connect(deviceIndex);
-    
+
     if (result) {
       logger.i("自动连接设备成功,索引: $deviceIndex");
     } else {
       logger.e("自动连接设备失败,索引: $deviceIndex");
     }
-    
+
     return result;
   }
 
@@ -302,11 +302,11 @@ class WinReaderManager {
   }
 
   /// 处理数据接收
-  void _handleDataReceived(dynamic data) {
-    if (data is String) {
-      logger.i("接收到读卡器数据: $data");
-      _onDataReceived?.call(data);
-    }
+  void _handleDataReceived(List<String> data) {
+    logger.i("接收到读卡器数据: $data (类型: ${data.runtimeType})");
+
+    // 直接传递List<String>给回调
+    _onDataReceived?.call(data);
   }
 
   /// 检查是否已连接
@@ -324,4 +324,4 @@ class WinReaderManager {
     _dataSubscription = null;
     logger.i("WinReaderManager 资源已释放");
   }
-}
+}

+ 14 - 18
UI/CF.APP/chicken_farm/lib/pages/breeding/batch_create_page_win.dart

@@ -138,7 +138,7 @@ class _BatchCreatePageWinState extends State<BatchCreatePageWin> {
             },
             converter: (BatchModel data) {
               return SelectOption(
-                label: '${data.batchNum}(${data.batchName})',
+                label: '批次号:${data.batchNum}',
                 value: data.batchNum,
                 extra: data,
               );
@@ -160,6 +160,8 @@ class _BatchCreatePageWinState extends State<BatchCreatePageWin> {
   }
 
   Widget _buildFamilySearchSelect() {
+    final bool isFamilySelectEnabled = _batchNum != null;
+
     return Column(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: [
@@ -173,6 +175,7 @@ class _BatchCreatePageWinState extends State<BatchCreatePageWin> {
           ),
           child: VberSearchSelect<FamilyModel>(
             searchApi: ({required dynamic queryParams}) async {
+              queryParams['batchNum'] = _batchNum;
               final result = await apis.breeding.queryApi.queryPageFamilys(
                 queryParams,
               );
@@ -180,13 +183,14 @@ class _BatchCreatePageWinState extends State<BatchCreatePageWin> {
             },
             converter: (FamilyModel data) {
               return SelectOption(
-                label: data.familyNum,
-                value: data.id.toString(),
+                label: '家系号:${data.familyNum}',
+                value: data.familyNum,
                 extra: data,
               );
             },
             value: _familyNum,
             hint: '家系号',
+            enabled: isFamilySelectEnabled,
             onChanged: (String? value) {
               setState(() {
                 _familyNum = value;
@@ -265,24 +269,16 @@ class _BatchCreatePageWinState extends State<BatchCreatePageWin> {
 
   // 提交数据
   void _handleSubmit() {
-    final data = _electronicIds.map((id) {
-      return {
-        'electronicId': id,
-        'batchNum': _batchNum,
-        'familyNum': _familyNum,
-        'date': DateTimeUtil.format(DateTime.now()),
-      };
-    }).toList();
+    final data = {
+      'electronicIds': _electronicIds,
+      'batchNum': _batchNum,
+      'familyNum': _familyNum,
+      'date': DateTimeUtil.format(DateTime.now()),
+    };
     apis.breeding.submitApi.bindChicken(data).then((res) {
       if (res.success) {
         ToastUtil.success(res.message.isEmpty ? '批量绑定个体成功' : res.message);
         if (mounted) {
-          ScaffoldMessenger.of(context).showSnackBar(
-            const SnackBar(
-              content: Text('批量绑定个体成功'),
-              backgroundColor: Colors.green,
-            ),
-          );
           // 提交后重置表单
           setState(() {
             _electronicIds.clear();
@@ -293,4 +289,4 @@ class _BatchCreatePageWinState extends State<BatchCreatePageWin> {
       }
     });
   }
-}
+}

+ 1 - 1
UI/CF.APP/chicken_farm/lib/pages/breeding/batch_culling_page.dart

@@ -235,7 +235,7 @@ class _BatchCullingPageState extends State<BatchCullingPage> {
       'cullReason': _cullReason,
       'date': DateTimeUtil.format(DateTime.now()),
     };
-    apis.breeding.submitApi.weight(data).then((res) {
+    apis.breeding.submitApi.batchCull(data).then((res) {
       if (res.success) {
         ToastUtil.success(res.message.isNotEmpty ? res.message : '批量淘汰提交成功');
         if (mounted) {

+ 41 - 13
UI/CF.APP/chicken_farm/lib/pages/breeding/batch_culling_page_win.dart

@@ -1,4 +1,5 @@
 import 'package:chicken_farm/apis/index.dart';
+import 'package:chicken_farm/components/vb_win_electronic_id_field.dart';
 import 'package:chicken_farm/core/utils/datetime_util.dart';
 import 'package:flutter/material.dart';
 import 'package:chicken_farm/components/vb_app_bar.dart';
@@ -114,16 +115,41 @@ class _BatchCullingPageWinState extends State<BatchCullingPageWin> {
   }
 
   Widget _buildElectronicIdSection() {
-    return Column(
-      crossAxisAlignment: CrossAxisAlignment.start,
-      children: [
-        const Text('请输入或扫描电子编号', style: TextStyle(fontWeight: FontWeight.bold)),
-        const SizedBox(height: 10),
-        Container(
-          padding: const EdgeInsets.fromLTRB(16, 2, 16, 2),
-          decoration: BoxDecoration(border: Border.all(color: Colors.grey)),
-        ),
-      ],
+    return VbWinElectronicIdField(
+      electronicIds: _electronicIds,
+      onIdsScanned: (List<String> idList) {
+        // 过滤出未存在的ID
+        final newIds = idList
+            .where((id) => !_electronicIds.contains(id))
+            .toList();
+
+        if (newIds.isNotEmpty) {
+          setState(() {
+            // 将新的ElectronicId添加到列表中
+            for (var id in newIds) {
+              _electronicIds.insert(0, id);
+            }
+          });
+          ScaffoldMessenger.of(context).showSnackBar(
+            SnackBar(
+              content: Text("新增 ${newIds.length} 枚电子编号"),
+              backgroundColor: Colors.green,
+            ),
+          );
+        } else {
+          // 所有ID都已存在
+          ScaffoldMessenger.of(context).showSnackBar(
+            const SnackBar(
+              content: Text('电子编号已存在'),
+              backgroundColor: Colors.orange,
+            ),
+          );
+        }
+      },
+      multiple: true,
+      label: '电子编号',
+      multiplePlaceholder: '未识别',
+      multipleFormat: '已识别 %d 枚电子编号',
     );
   }
 
@@ -202,17 +228,19 @@ class _BatchCullingPageWinState extends State<BatchCullingPageWin> {
   void _handleSubmit() {
     final data = {
       'electronicIds': _electronicIds,
-      'disposal_method': _disposalMethod,
-      'cull_reason': _cullReason,
+      'disposalMethod': _disposalMethod,
+      'cullReason': _cullReason,
       'date': DateTimeUtil.format(DateTime.now()),
     };
-    apis.breeding.submitApi.weight(data).then((res) {
+    apis.breeding.submitApi.batchCull(data).then((res) {
       if (res.success) {
         ToastUtil.success(res.message.isNotEmpty ? res.message : '批量淘汰提交成功');
         if (mounted) {
           // 提交后重置表单
           setState(() {
             _electronicIds.clear();
+            // _cullReason = null;
+            // _disposalMethod = null;
           });
         }
       } else {

+ 5 - 3
UI/CF.APP/chicken_farm/lib/pages/serial/serial_setting_page.dart

@@ -150,9 +150,11 @@ class _SerialSettingPageState extends State<SerialSettingPage> {
   }
 
   /// 数据接收回调
-  void _onDataReceived(String data) {
-    if (mounted) {
-      _addLog("读取标签: $data");
+  void _onDataReceived(List<String> data) {
+    if (mounted && data.isNotEmpty) {
+      for (var element in data) {
+        _addLog("接收数据: $element");
+      }
     }
   }
 

+ 94 - 109
UI/CF.APP/chicken_farm/windows/runner/reader/reader_method_call.cpp

@@ -1,11 +1,10 @@
 #include "reader_method_call.h"
 #include "reader_service.h"
 
-#include <flutter/flutter_engine.h>
 #include <flutter/method_channel.h>
+#include <flutter/plugin_registrar_windows.h>
 #include <flutter/standard_method_codec.h>
 #include <flutter/event_channel.h>
-#include <flutter/event_stream_handler_functions.h>
 
 #include <memory>
 #include <string>
@@ -13,138 +12,124 @@
 #include <functional>
 #include <map>
 
-static std::unique_ptr<flutter::EventChannel<flutter::EncodableValue>> g_event_channel = nullptr;
-static std::unique_ptr<flutter::StreamHandler<flutter::EncodableValue>> g_stream_handler = nullptr;
+// 全局变量存储事件发送器
+static std::unique_ptr<flutter::EventSink<flutter::EncodableValue>> event_sink = nullptr;
 
 void ReaderMethodCall::RegisterWindowsAPI(flutter::FlutterEngine *engine)
 {
+  // 注册MethodChannel用于方法调用
   auto channel = std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
       engine->messenger(), "com.vber.chicken_farm/win_reader",
       &flutter::StandardMethodCodec::GetInstance());
+
   channel->SetMethodCallHandler([](const auto &call, auto result)
                                 { HandleMethodCall(call, std::move(result)); });
 
-  g_event_channel = std::make_unique<flutter::EventChannel<flutter::EncodableValue>>(
+  // 注册EventChannel用于数据回调
+  auto event_channel = std::make_unique<flutter::EventChannel<flutter::EncodableValue>>(
       engine->messenger(), "com.vber.chicken_farm/win_reader_events",
       &flutter::StandardMethodCodec::GetInstance());
 
-  g_stream_handler =
-      std::make_unique<flutter::StreamHandlerFunctions<flutter::EncodableValue>>(
-          [](const flutter::EncodableValue *arguments,
-             std::unique_ptr<flutter::EventSink<flutter::EncodableValue>> &&events)
-              -> std::unique_ptr<flutter::StreamHandlerError<flutter::EncodableValue>>
-          {
-            // 使用shared_ptr包装,使其可拷贝
-            auto events_ptr = std::shared_ptr<flutter::EventSink<flutter::EncodableValue>>(std::move(events));
-            ReaderService::GetInstance()->SetDataCallback([events_ptr](const std::string &data)
-                                                          { events_ptr->Success(flutter::EncodableValue(data)); });
-            return nullptr;
-          },
-          [](const flutter::EncodableValue *arguments)
-              -> std::unique_ptr<flutter::StreamHandlerError<flutter::EncodableValue>>
-          {
-            ReaderService::GetInstance()->SetDataCallback(nullptr);
-            return nullptr;
-          });
+  event_channel->SetStreamHandler(std::make_unique<flutter::StreamHandler<flutter::EncodableValue>>(
+      [engine](const flutter::EncodableValue* arguments,
+               std::unique_ptr<flutter::EventSink<flutter::EncodableValue>>&& sink) -> std::unique_ptr<flutter::StreamHandlerError<flutter::EncodableValue>> {
+        event_sink = std::move(sink);
+        
+        // 设置数据回调
+        ReaderService::GetInstance()->SetDataCallback([](const std::string& data) {
+          if (event_sink) {
+            // 将数据发送到Flutter端
+            event_sink->Success(flutter::EncodableValue(data));
+          }
+        });
 
-  g_event_channel->SetStreamHandler(std::move(g_stream_handler));
+        return nullptr;
+      },
+      [](const flutter::EncodableValue* arguments) -> std::unique_ptr<flutter::StreamHandlerError<flutter::EncodableValue>> {
+        event_sink = nullptr;
+        return nullptr;
+      }));
 }
 
 void ReaderMethodCall::HandleMethodCall(
     const flutter::MethodCall<flutter::EncodableValue> &method_call,
     std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
 {
-  // Use a map to handle different method calls
-  static std::map<std::string, std::function<void(const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)>> handlers = {
-      {"scanUsb", [](const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
-       {
-         // Call the scan USB method of the reader service
-         std::vector<std::string> usb = ReaderService::GetInstance()->ScanUsb();
+  // 使用映射表来处理不同的方法调用
+  static std::map<std::string, std::function<void(const flutter::MethodCall<flutter::EncodableValue>&, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>>)>> handlers = {
+    {"scanUsb", [](const flutter::MethodCall<flutter::EncodableValue>& method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
+      // 调用读取器服务的扫描USB方法
+      std::vector<std::string> usb = ReaderService::GetInstance()->ScanUsb();
 
-         // Create a list to store USB device names
-         flutter::EncodableList device_list;
-         for (const auto &device : usb)
-         {
-           device_list.push_back(flutter::EncodableValue(device));
-         }
+      // 创建列表以存储USB设备名称
+      flutter::EncodableList device_list;
+      for (const auto &device : usb)
+      {
+        device_list.push_back(flutter::EncodableValue(device));
+      }
 
-         // Return the list of USB devices to Flutter (empty if no devices found)
-         result->Success(flutter::EncodableValue(device_list));
-       }},
-      {"connect", [](const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
-       {
-         const auto *args = std::get_if<flutter::EncodableMap>(method_call.arguments());
-         if (args)
-         {
-           auto deviceIndexIt = args->find(flutter::EncodableValue("deviceIndex"));
-           if (deviceIndexIt != args->end())
-           {
-             int deviceIndex = std::get<int>(deviceIndexIt->second);
-             bool success = ReaderService::GetInstance()->Connect(deviceIndex);
-             result->Success(flutter::EncodableValue(success));
-             return;
-           }
-         }
-         result->Success(flutter::EncodableValue(false));
-       }},
-      {"disconnect", [](const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
-       {
-         ReaderService::GetInstance()->Disconnect();
-         result->Success();
-       }},
-      {"startRead", [](const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
-       {
-         bool success = ReaderService::GetInstance()->StartRead();
-         result->Success(flutter::EncodableValue(success));
-       }},
-      {"stopRead", [](const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
-       {
-         ReaderService::GetInstance()->StopRead();
-         result->Success();
-       }},
-      {"getDeviceInfo", [](const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
-       {
-         std::string deviceInfo = ReaderService::GetInstance()->GetDeviceInfo();
-         result->Success(flutter::EncodableValue(deviceInfo));
-       }},
-      {"getPower", [](const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
-       {
-         unsigned char power = 0;
-         bool success = ReaderService::GetInstance()->GetPower(power);
-         if (success) {
-           result->Success(flutter::EncodableValue(static_cast<int>(power)));
-         } else {
-           result->Success(flutter::EncodableValue());
-         }
-       }},
-      {"setPower", [](const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
-       {
-         const auto *args = std::get_if<flutter::EncodableMap>(method_call.arguments());
-         if (args)
-         {
-           auto powerIt = args->find(flutter::EncodableValue("power"));
-           if (powerIt != args->end())
-           {
-             int power = std::get<int>(powerIt->second);
-             bool success = ReaderService::GetInstance()->SetPower(static_cast<unsigned char>(power));
-             result->Success(flutter::EncodableValue(success));
-             return;
-           }
-         }
-         result->Success(flutter::EncodableValue(false));
-       }}
-      // More method handlers can be added here
-      // {"methodName", [](auto result) { /* Handle method */ }}
+      // 将USB设备列表返回给Flutter(如果没有找到设备则为空)
+      result->Success(flutter::EncodableValue(device_list));
+    }},
+    {"connect", [](const flutter::MethodCall<flutter::EncodableValue>& method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
+      // 获取参数
+      const auto *arguments = std::get_if<flutter::EncodableMap>(method_call.arguments());
+      if (arguments) {
+        auto device_index_it = arguments->find(flutter::EncodableValue("deviceIndex"));
+        if (device_index_it != arguments->end()) {
+          int device_index = device_index_it->second.LongValue();
+          bool success = ReaderService::GetInstance()->Connect(device_index);
+          result->Success(flutter::EncodableValue(success));
+          return;
+        }
+      }
+      result->Error("Invalid arguments", "Device index not provided");
+    }},
+    {"disconnect", [](const flutter::MethodCall<flutter::EncodableValue>& method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
+      ReaderService::GetInstance()->Disconnect();
+      result->Success();
+    }},
+    {"startRead", [](const flutter::MethodCall<flutter::EncodableValue>& method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
+      bool success = ReaderService::GetInstance()->StartRead();
+      result->Success(flutter::EncodableValue(success));
+    }},
+    {"stopRead", [](const flutter::MethodCall<flutter::EncodableValue>& method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
+      ReaderService::GetInstance()->StopRead();
+      result->Success();
+    }},
+    {"getDeviceInfo", [](const flutter::MethodCall<flutter::EncodableValue>& method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
+      std::string deviceInfo = ReaderService::GetInstance()->GetDeviceInfo();
+      result->Success(flutter::EncodableValue(deviceInfo));
+    }},
+    {"getPower", [](const flutter::MethodCall<flutter::EncodableValue>& method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
+      unsigned char power = 0;
+      bool success = ReaderService::GetInstance()->GetPower(power);
+      if (success) {
+        result->Success(flutter::EncodableValue(static_cast<int>(power)));
+      } else {
+        result->Success(flutter::EncodableValue());
+      }
+    }},
+    {"setPower", [](const flutter::MethodCall<flutter::EncodableValue>& method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
+      const auto *arguments = std::get_if<flutter::EncodableMap>(method_call.arguments());
+      if (arguments) {
+        auto power_it = arguments->find(flutter::EncodableValue("power"));
+        if (power_it != arguments->end()) {
+          int power = power_it->second.LongValue();
+          bool success = ReaderService::GetInstance()->SetPower(static_cast<unsigned char>(power));
+          result->Success(flutter::EncodableValue(success));
+          return;
+        }
+      }
+      result->Error("Invalid arguments", "Power value not provided");
+    }}
   };
 
-  // Find and execute the corresponding method handler
+  // 查找并执行对应的方法处理器
   auto handler = handlers.find(method_call.method_name());
-  if (handler != handlers.end())
-  {
+  if (handler != handlers.end()) {
     handler->second(method_call, std::move(result));
-  }
-  else
-  {
+  } else {
     result->NotImplemented();
   }
 }

+ 58 - 10
UI/CF.APP/chicken_farm/windows/runner/reader/reader_service.cpp

@@ -4,6 +4,8 @@
 #include <iostream>
 #include <thread>
 #include <chrono>
+#include <mutex>
+#include <sstream>
 
 void PrintLogMessage(const std::string &message)
 {
@@ -120,10 +122,32 @@ bool ReaderService::StartRead()
   reading_ = true;
   timer_running_ = true;
 
+  tags_.clear();
+
   // Start timer thread to periodically call AnswerMode
   timer_thread_ = std::make_unique<std::thread>(&ReaderService::RunTimer, this);
 
-  PrintLogMessage("Started reading in answer mode.");
+  // Start a thread to periodically send tag list to UI
+  callback_thread_ = std::make_unique<std::thread>([this]()
+                                                   {
+    while (timer_running_) {
+      SendTagList();
+      std::this_thread::sleep_for(std::chrono::seconds(1));
+    } });
+
+  // // Start a separate thread to automatically stop reading after 3 seconds
+  // auto stop_thread = std::make_unique<std::thread>([this]() {
+  //   std::this_thread::sleep_for(std::chrono::seconds(3));
+  //   if (reading_) {
+  //     StopRead();
+  //     PrintLogMessage("Automatically stopped reading after 3 seconds.");
+  //   }
+  // });
+
+  // // Detach the thread to run independently and clean up resources
+  // stop_thread->detach();
+
+  PrintLogMessage("Started reading in answer mode. Will auto-stop after 3 seconds.");
 
   return true;
 }
@@ -140,6 +164,11 @@ void ReaderService::StopRead()
       {
         timer_thread_->join();
       }
+
+      if (callback_thread_ && callback_thread_->joinable())
+      {
+        callback_thread_->join();
+      }
     }
 
     reading_ = false;
@@ -340,7 +369,6 @@ void ReaderService::DeviceCallback(int msg, int param1, unsigned char *param2, i
 
 void ReaderService::AnswerMode()
 {
-  PrintLogMessage("reading...");
   unsigned char arrBuffer[2048] = {0};
   unsigned short iTagLength = 0;
   unsigned short iTagNumber = 0;
@@ -383,26 +411,46 @@ void ReaderService::AnswerMode()
 
     for (int i = 2; i < iIDLen; i++)
     {
-      sprintf_s(buffer, sizeof(buffer), "%.2X ", pID[i]);
+      sprintf_s(buffer, sizeof(buffer), "%.2X", pID[i]);
       str2 += buffer;
+      str2 += " ";
       tag += buffer;
     }
 
     sprintf_s(buffer, sizeof(buffer), "RSSI:%.2X", pID[iIDLen]);
     str2 += buffer;
     PrintLogMessage(str2);
-    // Notify UI of new data
-    if (instance->data_callback_)
+
+    // Store the tag without spaces
+    if (!tag.empty())
     {
-      PrintLogMessage("Tag: " + tag);
-      instance->data_callback_(tag);
+      std::lock_guard<std::mutex> lock(instance->tags_mutex_);
+      instance->tags_.insert(tag);
     }
-    else
+
+    iLength = iLength + bPackLength + 1;
+  }
+}
+
+void ReaderService::SendTagList()
+{
+  if (data_callback_ && !tags_.empty())
+  {
+    // Convert the set of tags to a JSON string
+    std::string tag_list_json = "[";
+    bool first = true;
+    for (const auto &tag : tags_)
     {
-      PrintLogMessage("Tag2: " + tag);
+      if (!first)
+      {
+        tag_list_json += ",";
+      }
+      tag_list_json += "\"" + tag + "\"";
+      first = false;
     }
+    tag_list_json += "]";
 
-    iLength = iLength + bPackLength + 1;
+    data_callback_(tag_list_json);
   }
 }
 

+ 10 - 0
UI/CF.APP/chicken_farm/windows/runner/reader/reader_service.h

@@ -8,6 +8,8 @@
 #include <thread>
 #include <atomic>
 #include <chrono>
+#include <set>
+#include <mutex>
 extern "C"
 {
 #include "SWHidApi.h"
@@ -66,12 +68,20 @@ private:
   // Function to run the timer loop for periodically calling AnswerMode
   void RunTimer();
 
+  // Function to periodically send tag list to UI
+  void SendTagList();
+
   // Member variables
   bool connected_ = false;
   bool reading_ = false;
   std::atomic<bool> timer_running_{false};
   std::unique_ptr<std::thread> timer_thread_;
+  std::unique_ptr<std::thread> callback_thread_;
   DataCallback data_callback_;
+  
+  // Store unique tags
+  std::set<std::string> tags_;
+  std::mutex tags_mutex_;
 };
 
 #endif // RUNNER_READER_SERVICE_H_