Browse Source

Update 继续完善扫码功能

Yue 4 days ago
parent
commit
a887d2a9a7

+ 8 - 1
UI/CF.APP/chicken_farm/android/app/src/main/java/com/vber/chicken_farm/MainActivity.java

@@ -90,8 +90,15 @@ public class MainActivity extends FlutterActivity {
      @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         Log.d(TAG, "按键按下: " + keyCode);
-      
         
+        // 处理622和623按键事件
+        if (keyCode == 622 || keyCode == 623) {
+            if (scanMethodCallHandler != null) {
+                scanMethodCallHandler.onKeyEvent(keyCode);
+                return true; // 表示事件已被处理
+            }
+        }
+              
         // 其他按键处理
         return super.onKeyDown(keyCode, event);
     }

+ 9 - 1
UI/CF.APP/chicken_farm/android/app/src/main/java/com/vber/chicken_farm/scan/ScanConstants.java

@@ -6,6 +6,8 @@ package com.vber.chicken_farm.scan;
 public final class ScanConstants {
     // 扫描结果广播Action
     public static final String SCAN_ACTION = "scan.rcv.message";
+    // 扫描结果数据
+    public static final String SCAN_RESULT_DATA = "barocode";
     
     // 唤醒锁标识
     public static final String WAKE_LOCK_TAG = "ScanService:WakeLock";
@@ -20,7 +22,13 @@ public final class ScanConstants {
     // 唤醒锁超时时间(10分钟)
     public static final long WAKE_LOCK_TIMEOUT = 10 * 60 * 1000L;
     
-    // 扫描模式:广播模式
+    /**
+     * 0-广播模式,通过注册广播获取扫描数据
+     * 1-输入框模式,在此模式下,任何输入框都可以填充扫描条形码
+     * 2-键盘模式,模拟键盘输入
+     * 3-单输入框模式,任何输入框均可填充扫描条形码,无重复
+     * 4-剪贴板,将扫描的条形码放在剪贴板上,用户可以将其粘贴到任何地方
+     */
     public static final int SCAN_OP_MODE_BROADCAST = 0;
     
     private ScanConstants() {

+ 15 - 0
UI/CF.APP/chicken_farm/android/app/src/main/java/com/vber/chicken_farm/scan/ScanMethodCallHandler.java

@@ -53,6 +53,9 @@ public class ScanMethodCallHandler implements MethodCallHandler, ScanCallback {
         this.channel.setMethodCallHandler(this);
         // 初始化时绑定服务
         bindScanService();
+
+        Log.d(TAG, "SCAN Flutter MethodChannel初始化完成");
+
     }
 
     /**
@@ -157,6 +160,18 @@ public class ScanMethodCallHandler implements MethodCallHandler, ScanCallback {
         channel.invokeMethod("onScanError", errorMsg);
     }
 
+    // 新增方法:处理按键事件并通知Flutter
+    public void onKeyEvent(int keyCode) {
+        if (keyCode == 622) {
+            // 向Flutter发送按键622事件
+            channel.invokeMethod("onKeyPress", "KEY_622");
+        } else if (keyCode == 623) {
+            // 向Flutter发送按键623事件
+            channel.invokeMethod("onKeyPress", "KEY_623");
+        }
+        
+    }
+
     /**
      * 释放资源(Flutter插件销毁时调用)
      */

+ 89 - 27
UI/CF.APP/chicken_farm/android/app/src/main/java/com/vber/chicken_farm/scan/ScanService.java

@@ -29,8 +29,8 @@ public class ScanService extends Service {
     public static final long SCAN_SINGLE_TIMEOUT_MS = 5000;
     /** 扫描头闲置超时时间(毫秒):打开后无扫码请求则关闭 */
     public static final long SCAN_IDLE_TIMEOUT_MS = 5 * 60 * 1000L; // 5分钟
-    /** 默认灯光模式:开启瞄准 */
-    public static final int DEFAULT_LIGHT_MODE = ScanConstants.LIGHT_MODE_AIM_ONLY;
+    /** 默认灯光模式:开启照明+瞄准 */
+    public static final int DEFAULT_LIGHT_MODE = ScanConstants.LIGHT_MODE_BOTH;
     
     // -------------------------- 核心变量 --------------------------
     private ScanManager scanManager;
@@ -72,7 +72,7 @@ public class ScanService extends Service {
             if (intent == null || scanCallback == null || !isScanning) return;
             
             try {
-                byte[] barcode = intent.getByteArrayExtra("barocode");
+                byte[] barcode = intent.getByteArrayExtra(ScanConstants.SCAN_RESULT_DATA);
                 int barcodeLen = intent.getIntExtra("length", 0);
                 
                 if (barcode != null && barcodeLen > 0) {
@@ -117,6 +117,7 @@ public class ScanService extends Service {
                 }
                 return;
             }
+            
             Log.d(TAG, "扫描管理器懒加载初始化成功");
         }
     }
@@ -167,11 +168,11 @@ public class ScanService extends Service {
         if (scanManager == null) {
             return false;
         }
-
         try {
+            setDefaultParams();
             // 2. 打开扫描头硬件
             scanManager.openScanner();
-            // 3. 设置默认灯光(瞄准)
+            // 3. 设置默认灯光(照明+瞄准)
             setLightingMode(DEFAULT_LIGHT_MODE);
             // 4. 设置广播模式
             scanManager.setOPMode(ScanConstants.SCAN_OP_MODE_BROADCAST);
@@ -192,6 +193,64 @@ public class ScanService extends Service {
         }
     }
 
+    public void setDefaultParams() { 
+        /**
+         * 自定义广播 action 以接收数据
+         */
+        scanManager.setBroadcastAction(ScanConstants.SCAN_ACTION);
+        /**
+         * 自定义广播 key 以接收数据
+         */
+        scanManager.setBroadcastKey(ScanConstants.SCAN_RESULT_DATA);
+        /**
+         * 0-广播模式,通过注册广播获取扫描数据
+         * 1-输入框模式,在此模式下,任何输入框都可以填充扫描条形码
+         * 2-键盘模式,模拟键盘输入
+         * 3-单输入框模式,任何输入框均可填充扫描条形码,无重复
+         * 4-剪贴板,将扫描的条形码放在剪贴板上,用户可以将其粘贴到任何地方
+         */
+        // scanManager.setOPMode(0);
+        /**
+         * 0 - none
+         * 1 - Enter
+         * 2 - Tab
+         */
+        scanManager.setEndMark(0);
+        /**
+         * 是否启用手柄按钮
+         */
+        scanManager.setHandleKey(false);
+        /**
+         * 是否播放声音
+        */
+        scanManager.setPlaySound(true);
+        /**
+         * 是否震动
+         */
+        scanManager.setVibrate(true);
+        /**
+         * 前缀
+         */
+        scanManager.setPrefix("");
+        /**
+         * 后缀
+         */
+        scanManager.setSuffix("");
+        /**
+         * 设置连续扫描 (设置为禁用时为 true,允许为 false)
+         */
+        scanManager.setContinueScan(false);
+        /**
+         * 是否启用所有一维码(true-启用; false-不启用)
+         */
+        scanManager.enableSYM1D(true);
+        /**
+         * 是否启用所有二维码(true-启用; false-不启用)
+         */
+        scanManager.enableSYM2D(true);
+        
+    }
+
     /**
      * 手动关闭扫描头(离开场景时调用)
      * 逻辑:停止解码+关闭硬件+注销广播+取消所有超时
@@ -401,28 +460,6 @@ public class ScanService extends Service {
         }
     }
 
-    /**
-     * 设置灯光模式(外部调用)
-     */
-    public void setLightingMode(int mode) {
-        if (scanManager != null && isScanHeadOpened) {
-            try {
-                int[] paramIDList = {DecoderConfigValues.ParamID.LIGHTS_MODE};
-                int[] symValue = {mode};
-                scanManager.setSYMValueInts(paramIDList, symValue);
-                Log.d(TAG, "灯光模式设置为:" + getLightModeDesc(mode));
-            } catch (Exception e) {
-                Log.e(TAG, "设置灯光模式失败", e);
-                if (scanCallback != null) {
-                    scanCallback.onScanError("设置灯光模式失败:" + e.getMessage());
-                }
-            }
-        } else {
-            if (scanCallback != null) {
-                scanCallback.onScanError("扫描头未打开,无法设置灯光");
-            }
-        }
-    }
 
     private String getLightModeDesc(int mode) {
         switch (mode) {
@@ -468,6 +505,31 @@ public class ScanService extends Service {
     public boolean isScanHeadOpened() {
         return isScanHeadOpened;
     }
+    
+    /**
+     * 设置灯光模式
+     */
+    public void setLightingMode(int mode) {
+        if (scanManager != null && isScanHeadOpened) {
+            try {
+                // {DecoderConfigValues.ParamID.CODE128_ENABLE} 条码模式
+                // {DecoderConfigValues.ParamID.LIGHTS_MODE} 灯光模式
+                int[] paramIDList = {DecoderConfigValues.ParamID.LIGHTS_MODE};
+                int[] symValue = {mode};
+                scanManager.setSYMValueInts(paramIDList, symValue);
+                Log.d(TAG, "灯光模式设置为:" + getLightModeDesc(mode));
+            } catch (Exception e) {
+                Log.e(TAG, "设置灯光模式失败", e);
+                if (scanCallback != null) {
+                    scanCallback.onScanError("设置灯光模式失败:" + e.getMessage());
+                }
+            }
+        } else {
+            if (scanCallback != null) {
+                scanCallback.onScanError("扫描头未打开,无法设置灯光");
+            }
+        }
+    }
 
     // -------------------------- 生命周期 --------------------------
     @Override

+ 17 - 1
UI/CF.APP/chicken_farm/lib/core/services/pda/scan_channel.dart

@@ -1,3 +1,4 @@
+import 'package:chicken_farm/core/utils/logger.dart';
 import 'package:flutter/services.dart';
 
 class ScanChannel {
@@ -10,6 +11,7 @@ class ScanChannel {
   static void initScanListener({
     required Function(String) onScanResult,
     required Function(String) onScanError,
+    Function(int)? onKeyPress,
   }) {
     _channel.setMethodCallHandler((MethodCall call) async {
       switch (call.method) {
@@ -19,6 +21,20 @@ class ScanChannel {
         case 'onScanError':
           onScanError(call.arguments);
           break;
+        case 'onKeyPress': // 处理按键事件
+          if (onKeyPress != null) {
+            onKeyPress(call.arguments);
+          } else {
+            logger.d("前端扫码按键:${call.arguments}");
+            if (call.arguments == 'KEY_622') {
+              startScan();
+            } else if (call.arguments == 'KEY_623') {
+              startScan();
+            } else {
+              logger.d("未注册[${call.arguments}]按键事件");
+            }
+          }
+          break;
         default:
           break;
       }
@@ -100,7 +116,7 @@ class ScanChannel {
 
   // -------------------------- 灯光设置API --------------------------
   /// 设置灯光模式
-  /// mode: 0-关闭 1-仅瞄准 2-仅照明 3-交替 4-同时开启
+  /// mode: 0-关闭 1-仅瞄准 2-仅照明 3-交替 4-瞄准+照明
   static Future<String> setLightingMode(int mode) async {
     try {
       final String result = await _channel.invokeMethod('setLightingMode', {

+ 74 - 68
UI/CF.APP/chicken_farm/lib/pages/breeding/cage_change_page.dart

@@ -1,4 +1,3 @@
-import 'package:chicken_farm/core/services/pda/rfid_manager.dart';
 import 'package:chicken_farm/core/utils/logger.dart';
 import 'package:flutter/material.dart';
 import 'package:chicken_farm/components/vb_app_bar.dart';
@@ -14,10 +13,12 @@ class CageChangePage extends StatefulWidget {
 }
 
 class _CageChangePageState extends State<CageChangePage> {
-  String? _sourceCageId;
-  String? _targetCageId;
   final List<String> _rfids = [];
 
+  // 添加TextEditingControllers用于控制输入框
+  final TextEditingController _sourceCageController = TextEditingController();
+  final TextEditingController _targetCageController = TextEditingController();
+
   // 添加FocusNode用于控制焦点
   final FocusNode _sourceCageFocusNode = FocusNode();
   final FocusNode _targetCageFocusNode = FocusNode();
@@ -26,12 +27,13 @@ class _CageChangePageState extends State<CageChangePage> {
   bool _isScanningSource = false;
   bool _isScanningTarget = false;
   int _scanTag = 1;
-  late final RfidManager _rfidManager;
 
   @override
   void initState() {
     super.initState();
 
+    _sourceCageController.clear();
+    _targetCageController.clear();
     // 监听焦点变化以更新_scanTag值
     _sourceCageFocusNode.addListener(() {
       if (_sourceCageFocusNode.hasFocus) {
@@ -55,26 +57,27 @@ class _CageChangePageState extends State<CageChangePage> {
     // 初始化监听
     ScanChannel.initScanListener(
       onScanResult: (result) {
-        logger.d("扫码结果:$result");
+        logger.d("扫码结果:$result tag: $_scanTag");
         if (_scanTag == 1) {
           // 源笼号
           setState(() {
-            _sourceCageId = result;
+            _sourceCageController.text = result;
             _isScanningSource = false;
           });
-          // 源笼号扫描成功后自动延时扫描目标笼号
-          WidgetsBinding.instance.addPostFrameCallback((_) {
-            _sourceCageFocusNode.requestFocus();
-            logger.d("开始自动扫描目标笼号");
+          if (_targetCageController.text.isEmpty) {
+            // 源笼号扫描成功后自动延时扫描目标笼号
+            ScanChannel.stopSingleScan();
+            logger.d("自动扫描目标笼号");
+            _targetCageFocusNode.requestFocus();
             _dealyScanCode(2);
-          });
+          }
         } else if (_scanTag == 2) {
           // 目标笼号
           setState(() {
-            _targetCageId = result;
+            _targetCageController.text = result;
             _isScanningTarget = false;
           });
-        } else if (_scanTag == 3) {}
+        }
         // 扫码成功后停止解码(保留扫描头)
         ScanChannel.stopSingleScan();
       },
@@ -85,21 +88,24 @@ class _CageChangePageState extends State<CageChangePage> {
         });
       },
     );
+    _scanTag = 1;
 
     // 进入页面默认使原笼号获取到焦点
-    WidgetsBinding.instance.addPostFrameCallback((_) {
-      _sourceCageFocusNode.requestFocus();
-    });
+    // WidgetsBinding.instance.addPostFrameCallback((_) {
+    //   _sourceCageFocusNode.requestFocus();
+    // });
   }
 
   @override
   void dispose() {
+    // 释放TextEditingControllers
+    _sourceCageController.dispose();
+    _targetCageController.dispose();
+
     // 释放FocusNode
     _sourceCageFocusNode.dispose();
     _targetCageFocusNode.dispose();
 
-    // 释放RFID
-    _rfidManager.disposeRfid();
     // 手动关闭扫描头,立即释放资源
     ScanChannel.closeScanHead();
     // 释放通道
@@ -133,15 +139,15 @@ class _CageChangePageState extends State<CageChangePage> {
                 width: double.infinity,
                 child: ElevatedButton(
                   onPressed:
-                      _sourceCageId != null &&
-                          _targetCageId != null &&
+                      _sourceCageController.text.isNotEmpty &&
+                          _targetCageController.text.isNotEmpty &&
                           _rfids.isNotEmpty
                       ? _handleSubmit
                       : null,
                   style: ElevatedButton.styleFrom(
                     backgroundColor:
-                        (_sourceCageId != null &&
-                            _targetCageId != null &&
+                        (_sourceCageController.text.isNotEmpty &&
+                            _targetCageController.text.isNotEmpty &&
                             _rfids.isNotEmpty)
                         ? Colors.blue
                         : Colors.grey,
@@ -200,16 +206,25 @@ class _CageChangePageState extends State<CageChangePage> {
 
   Widget _buildCageSection(int tag) {
     String title = '源笼号';
-    String? value = _sourceCageId;
-    bool isScanning = _isScanningSource;
+    TextEditingController controller = _sourceCageController;
     FocusNode focusNode = _sourceCageFocusNode;
+    bool isScanning = _isScanningSource;
+
     if (tag != 1) {
       tag = 2;
       title = '目标笼号';
-      value = _targetCageId;
-      isScanning = _isScanningTarget;
+      controller = _targetCageController;
       focusNode = _targetCageFocusNode;
+      isScanning = _isScanningTarget;
+    }
+
+    // 确保光标在文本末尾
+    if (controller.text.isNotEmpty) {
+      controller.selection = TextSelection.fromPosition(
+        TextPosition(offset: controller.text.length),
+      );
     }
+
     return Column(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: [
@@ -227,34 +242,31 @@ class _CageChangePageState extends State<CageChangePage> {
                 // 将Text替换为TextField
                 child: TextField(
                   focusNode: focusNode,
-                  controller: TextEditingController(text: value),
+                  controller: controller,
                   decoration: InputDecoration(
                     border: InputBorder.none,
-                    hintText: value ?? '未扫描',
+                    hintText: controller.text.isNotEmpty ? null : '未扫描',
                     hintStyle: TextStyle(
-                      color: value != null ? Colors.black : Colors.grey,
+                      color: controller.text.isNotEmpty
+                          ? Colors.black
+                          : Colors.grey,
                       fontSize: 16,
                     ),
                   ),
                   style: TextStyle(
-                    color: value != null ? Colors.black : Colors.grey,
+                    color: controller.text.isNotEmpty
+                        ? Colors.black
+                        : Colors.grey,
                     fontSize: 16,
                   ),
                   onChanged: (text) {
-                    // 根据标题判断是哪个输入框
-                    if (title == '源笼号') {
-                      setState(() {
-                        _sourceCageId = text.isEmpty ? null : text;
-                      });
-                    } else if (title == '目标笼号') {
-                      setState(() {
-                        _targetCageId = text.isEmpty ? null : text;
-                      });
-                    }
+                    setState(() {
+                      // 文本更改由controller自动处理
+                    });
                   },
                 ),
               ),
-              if (value != null) ...[
+              if (controller.text.isNotEmpty) ...[
                 IconButton(
                   icon: const Icon(Icons.refresh, size: 20),
                   onPressed: () {
@@ -283,36 +295,25 @@ class _CageChangePageState extends State<CageChangePage> {
   }
 
   Widget _buildRfidSection() {
-    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(),
-        ),
-      ],
+    return VberRfidField(
+      rfids: _rfids,
+      onRfidScanned: (rfid) {
+        setState(() {
+          _rfids.insert(0, rfid);
+        });
+        ToastUtil.success("标签已添加");
+      },
+      multiple: true,
+      label: '鸡数量',
+      multiplePlaceholder: '未扫描',
+      multipleFormat: '已扫描 %d 只鸡',
     );
-    // return VberRfidField(
-    //   rfids: _rfids,
-    //   onRfidScanned: (rfid) {
-    //     setState(() {
-    //       _rfids.insert(0, rfid);
-    //     });
-    //     ToastUtil.success("标签已添加");
-    //   },
-    //   multiple: true,
-    //   label: '鸡数量',
-    //   multiplePlaceholder: '未扫描',
-    //   multipleFormat: '已扫描 %d 只鸡',
-    // );
   }
 
   void _dealyScanCode(int scanTag, {int? dealy}) async {
     dealy ??= 800;
     _scanTag = scanTag;
-
+    logger.d("开始扫码: $_scanTag");
     await Future.delayed(Duration(milliseconds: dealy));
     bool success = await ScanChannel.startScan();
     if (!success) {
@@ -330,6 +331,7 @@ class _CageChangePageState extends State<CageChangePage> {
       }
     });
     _scanTag = tag;
+    logger.d("开始扫码: $_scanTag");
     bool success = await ScanChannel.startScan();
     if (!success) {
       ToastUtil.error("扫码失败");
@@ -338,7 +340,11 @@ class _CageChangePageState extends State<CageChangePage> {
 
   void _handleChangeCageCode(int tag) {
     setState(() {
-      _sourceCageId = null;
+      if (tag == 1) {
+        _sourceCageController.clear();
+      } else {
+        _targetCageController.clear();
+      }
     });
     _dealyScanCode(tag);
   }
@@ -359,8 +365,8 @@ class _CageChangePageState extends State<CageChangePage> {
 
     // 提交后重置表单
     setState(() {
-      _sourceCageId = null;
-      _targetCageId = null;
+      _sourceCageController.clear();
+      _targetCageController.clear();
       _rfids.clear();
     });
   }