Browse Source

相机预览失败自动重连

cjb 3 weeks ago
parent
commit
b4661a9274

+ 1 - 1
entry/src/main/ets/common/util/Mqtt.ets

@@ -66,7 +66,7 @@ class MqttManager {
   }
 
   // 修改后的 handleMessage 方法片段
-  private handleMessage(message: MqttMessage): void {
+  private async handleMessage(message: MqttMessage): Promise<void> {
     const topic = message.topic;
     const payload = message.payload.toString();
     const topicCallbacks = this.callbacks.get(topic) || [];

+ 136 - 0
entry/src/main/ets/common/util/PreviewManager.ets

@@ -0,0 +1,136 @@
+import image from '@ohos.multimedia.image';
+import fs from "@ohos.file.fs"
+export class PreviewManager {
+  // 上下文对象
+  private context: Context;
+  // 当前活跃的显示缓冲区
+  private activeBuffer: PixelMap | null = null;
+  // 下一个准备显示的缓冲区
+  private nextBuffer: PixelMap | null = null;
+  // 处理状态标志,防止重复处理
+  private isProcessing: boolean = false;
+  // 上一帧处理完成的时间戳
+  private lastFrameTime: number = 0;
+  // 目标帧率(帧/秒)
+  private targetFPS: number = 20;
+  // 帧间隔时间(毫秒),根据目标帧率计算
+  private frameInterval: number = 1000 / this.targetFPS;
+  // 缓冲区交换锁,防止并发交换
+  private bufferLock: boolean = false;
+  // private errorCount: number = 0; // 错误计数器
+  // private readonly MAX_ERROR_COUNT = 5; // 最大连续错误次数
+
+  constructor(context: Context) {
+    this.context = context;
+  }
+  //设置目标帧率
+  setTargetFPS(fps: number): void {
+    this.targetFPS = fps;
+    this.frameInterval = 1000 / fps;
+  }
+  //处理指定索引的帧
+  async processFrame(index: number): Promise<boolean> {
+    if (this.isProcessing) return false;
+
+    // 检查错误次数
+    // if (this.errorCount >= this.MAX_ERROR_COUNT) {
+    //   console.error("达到最大错误次数,暂停处理");
+    //   return false;
+    // }
+
+    const now = Date.now();
+    if (now - this.lastFrameTime < this.frameInterval) {
+      return false;
+    }
+
+    this.isProcessing = true;
+    try {
+      const filePath = `${this.context.filesDir}/live${index}.jpg`;
+
+      // 1. 文件检查
+      try {
+        await fs.access(filePath); // 仅检查文件是否存在
+      } catch (error) {
+        console.warn("文件访问失败:", error);
+        return false;
+      }
+      // 2. 创建图像源
+      const imageSource = image.createImageSource(filePath);
+      if (!imageSource) {
+        console.warn("创建ImageSource失败");
+        return false;
+      }
+      try {
+        // 3. 创建PixelMap
+        const pixelMap = await imageSource.createPixelMap({
+          // 添加降级选项
+          desiredSize: { width: 720, height: 1280 }, // 限制分辨率
+          desiredPixelFormat: 3 // RGB_565节省内存
+        });
+        if (!pixelMap) {
+          console.warn("创建PixelMap失败");
+          return false;
+        }
+        // 5. 安全交换缓冲区
+        this.nextBuffer = pixelMap;
+        this.swapBuffers();
+        this.lastFrameTime = now;
+        //this.errorCount = 0; // 重置错误计数
+        return true;
+      } finally {
+        imageSource.release();
+      }
+    } catch (error) {
+      //this.errorCount++;
+     // console.warn(`处理预览帧失败(${this.errorCount}/${this.MAX_ERROR_COUNT}):`, error);
+      return false;
+    } finally {
+      this.isProcessing = false;
+    }
+  }
+  //交换活跃缓冲区和下一个缓冲区
+  private swapBuffers(): void {
+    if (this.bufferLock) return;
+    this.bufferLock = true;
+    try {
+      const oldBuffer = this.activeBuffer;
+      this.activeBuffer = this.nextBuffer;
+      this.nextBuffer = null;
+      if (oldBuffer) {
+        oldBuffer.release();
+      }
+    } finally {
+      this.bufferLock = false;
+    }
+  }
+  //获取当前活跃缓冲区
+  getActiveBuffer(): PixelMap | null {
+    return this.activeBuffer;
+  }
+  //检查是否准备好显示
+  isReady(): boolean {
+    return !this.isProcessing && this.activeBuffer !== null;
+  }
+  //检查是否有新帧等待处理
+  hasNewFrame(): boolean {
+    return this.nextBuffer !== null;
+  }
+
+  // getErrorCount():number{
+  //   return this.errorCount
+  // }
+  // resetErrorCount(){
+  //   this.errorCount = 0
+  // }
+  //释放资源
+  release(): void {
+    if (this.activeBuffer) {
+      this.activeBuffer.release();
+      this.activeBuffer = null;
+    }
+    if (this.nextBuffer) {
+      this.nextBuffer.release();
+      this.nextBuffer = null;
+    }
+  }
+}

+ 72 - 45
entry/src/main/ets/common/util/UsbDevice.ets

@@ -24,36 +24,42 @@ interface UsbDeviceInfo {
 
 // USB设备管理类(单例)
 export class USBDeviceManager {
-  // 单例实例
   private static instance: USBDeviceManager | null = null;
-  // 存储已连接的USB设备列表
   private connectedDevices: Array<usbManager.USBDevice> = [];
-  // 定时器ID
   private checkIntervalId: number | null = null;
+  private refreshLock: Promise<void> = Promise.resolve();
+  private deviceCheckLock: Promise<void> = Promise.resolve();
 
-  // 私有构造函数
   private constructor() {
-    this.refreshDeviceList();
+    this.init();
+  }
+
+  // 异步初始化
+  private async init(): Promise<void> {
+    await this.refreshDeviceList();
     this.startAutoCheck();
   }
 
-  // 获取单例实例
-  public static getInstance(): USBDeviceManager {
+  // 获取单例实例(异步)
+  public static async getInstance(): Promise<USBDeviceManager> {
     if (!USBDeviceManager.instance) {
       USBDeviceManager.instance = new USBDeviceManager();
+      await USBDeviceManager.instance.init();
     }
     return USBDeviceManager.instance;
   }
 
-  // 开始自动检测(每2秒一次
+  // 开始自动检测(异步
   private startAutoCheck(): void {
-    // 先停止已有的定时器
     this.stopAutoCheck();
 
-    // 启动新的定时器
-    this.checkIntervalId = setInterval(() => {
-      this.refreshDeviceList();
-      this.checkDevices();
+    this.checkIntervalId = setInterval(async () => {
+      try {
+        await this.refreshDeviceList();
+        await this.checkDevices();
+      } catch (error) {
+        console.error('[AsyncUSB] 自动检测失败:', (error as BusinessError).message);
+      }
     }, 2000);
   }
 
@@ -65,65 +71,86 @@ export class USBDeviceManager {
     }
   }
 
-  // 检查设备连接状态
-  private checkDevices(): void {
-    if (this.hasConnectedDevices()) {
-      const isCameraConnected = this.isDeviceConnectedByName('Sony_Camera');
-      const isScannerConnected = this.isDeviceConnectedByName('Scanning_Gun');
-      AppStorage.setOrCreate<boolean>('CameraStatus', isCameraConnected);
-      AppStorage.setOrCreate<boolean>('ScanningGunStatus', isScannerConnected);
-    } else {
-      console.log('USB状态检测', '没有USB设备连接');
-    }
+  // 异步检查设备连接状态
+  private async checkDevices(): Promise<void> {
+    return this.deviceCheckLock = this.deviceCheckLock.then(async () => {
+      const hasDevices = await this.hasConnectedDevices();
+      if (hasDevices) {
+        // 替换解构赋值为单独变量声明
+        const connectedResults = await Promise.all([
+          this.isDeviceConnectedByName('Sony_Camera'),
+          this.isDeviceConnectedByName('Scanning_Gun')
+        ]);
+        const isCameraConnected = connectedResults[0];
+        const isScannerConnected = connectedResults[1];
+        AppStorage.setOrCreate<boolean>('CameraStatus', isCameraConnected);
+        AppStorage.setOrCreate<boolean>('ScanningGunStatus', isScannerConnected);
+      } else {
+        console.log('USB状态检测', '没有USB设备连接');
+      }
+    });
   }
 
-  // 刷新设备列表,获取当前系统已连接的所有USB设备
-  public refreshDeviceList(): void {
-    try {
-      this.connectedDevices = usbManager.getDevices();
-      console.log('[USBDeviceManager] 当前连接的设备:', this.connectedDevices);
-    } catch (error) {
-      console.error('[USBDeviceManager] 获取USB设备失败:', (error as BusinessError).message);
-      this.connectedDevices = [];
-    }
+  // 异步刷新设备列表
+  public async refreshDeviceList(): Promise<void> {
+    return this.refreshLock = this.refreshLock.then(async () => {
+      try {
+        this.connectedDevices = await new Promise((resolve, reject) => {
+          try {
+            const devices = usbManager.getDevices();
+            resolve(devices);
+          } catch (error) {
+            reject(error);
+          }
+        });
+        console.log('[AsyncUSB] 当前连接的设备:', this.connectedDevices);
+      } catch (error) {
+        console.error('[AsyncUSB] 获取USB设备失败:', (error as BusinessError).message);
+        this.connectedDevices = [];
+      }
+    });
   }
 
-  // 检查是否有USB设备连接
-  public hasConnectedDevices(): boolean {
+  // 异步检查是否有USB设备连接
+  public async hasConnectedDevices(): Promise<boolean> {
+    await this.refreshLock;
     return this.connectedDevices.length > 0;
   }
 
-  // 根据供应商ID和产品ID检查特定USB设备是否连接
-  public isDeviceConnectedById(vendorId: number, productId: number): boolean {
+  // 异步检查特定USB设备是否连接
+  public async isDeviceConnectedById(vendorId: number, productId: number): Promise<boolean> {
+    await this.refreshLock;
     return this.connectedDevices.some(device =>
     device.vendorId === vendorId && device.productId === productId
     );
   }
 
-  // 根据设备信息检查USB设备是否连接
-  public isDeviceConnectedByInfo(usbDevice: UsbDeviceInfo): boolean {
+  // 异步检查USB设备是否连接
+  public async isDeviceConnectedByInfo(usbDevice: UsbDeviceInfo): Promise<boolean> {
     return this.isDeviceConnectedById(usbDevice.VendorId, usbDevice.ProductId);
   }
 
-  // 根据预定义的设备名称检查USB设备是否连接
-  public isDeviceConnectedByName(deviceName: string): boolean {
+  // 异步检查预定义的设备是否连接
+  public async isDeviceConnectedByName(deviceName: string): Promise<boolean> {
+    await this.refreshLock;
     const deviceInfo = USBDeviceIds.find(item => item.DeviceName === deviceName);
 
     if (!deviceInfo) {
-      console.warn(`[USBDeviceManager] 设备 ${deviceName} 不在预定义列表中`);
+      console.warn(`[AsyncUSB] 设备 ${deviceName} 不在预定义列表中`);
       return false;
     }
     return this.isDeviceConnectedById(deviceInfo.VendorId, deviceInfo.ProductId);
   }
 
-  // 获取所有已连接的USB设备
-  public getAllDevices(): Array<usbManager.USBDevice> {
-    return [...this.connectedDevices]; // 返回副本以避免外部修改
+  // 异步获取所有已连接的USB设备
+  public async getAllDevices(): Promise<Array<usbManager.USBDevice>> {
+    await this.refreshLock;
+    return [...this.connectedDevices];
   }
 
   // 销毁实例
   public destroy(): void {
     this.stopAutoCheck();
-    USBDeviceManager.instance = null; // 现在可以正常赋值为null
+    USBDeviceManager.instance = null;
   }
 }

+ 22 - 67
entry/src/main/ets/pages/CameraView.ets

@@ -8,6 +8,7 @@ import CommonEventManager from '@ohos.commonEventManager'
 import promptAction from '@ohos.promptAction';
 import { ConfirmDialogParams } from '../viewmodel/ConfirmDialogParam';
 import { ConfirmDialog } from '../view/ConfirmDialog';
+import { PreviewManager } from '../common/util/PreviewManager';
 
 const TAG = "sony_camera info"
 @Entry
@@ -173,7 +174,7 @@ export struct CameraDialog {
   // 控制帧率
   @State readTimer:number = 0
   // 预览操作
-  private previewManager: PreviewManager | null = null;
+  private previewManager: PreviewManager | null =null ;
   @State photoPixelMaps: photoInfo[] = []; // 存储所有照片的 PixelMap
   // 加载照片并生成 PixelMap
   loadPhotos = async () => {
@@ -495,22 +496,42 @@ export struct CameraDialog {
 
   //开始预览 50ms一张图片(20帧)
   liveShow = async () => {
+    if (this.previewManager) {
+      this.previewManager.release();
+    }
+    if (this.readTimer) {
+      clearInterval(this.readTimer);
+    }
     this.previewManager = new PreviewManager(getContext(this));
     let index = 0;
+    let retryCount = 0;
+    const MAX_RETRIES = 5; // 最大重试次数
     this.readTimer = setInterval(async () => {
       if (!this.isStopView && !this.isCapturing) {
         try {
           const success = await this.previewManager!.processFrame(index);
           if (success) {
             this.commodityPixelMap = this.previewManager!.getActiveBuffer();
+            retryCount=0;
           }
         } catch (error) {
+          retryCount++
           console.warn(TAG, "预览更新失败:", error);
         }
       }
+      if(retryCount===MAX_RETRIES)
+      {
+        clearInterval(this.readTimer);
+        this.previewManager?.release();
+        this.stopView()
+        await sleep(50)
+        this.startView()
+        console.info(TAG, "预览更新:");
+      }
     }, 50);
   };
 
+
   //断开相机(停止预览->关闭连接)
   disconnectCamera = () => {
     CommonEventManager.publish("stopview", (err) => {
@@ -1002,73 +1023,7 @@ function sleep(ms: number): Promise<void> {
   return new Promise((resolve) => setTimeout(resolve, ms));
 }
 
-class PreviewManager {
-  private context: Context;
-  private activeBuffer: PixelMap | null = null;
-  private nextBuffer: PixelMap | null = null;
-  private isProcessing: boolean = false;
-  private lastFrameTime: number = 0;
-  private frameInterval: number = 50;
-
-  constructor(context: Context) {
-    this.context = context;
-  }
-
-  async processFrame(index: number): Promise<boolean> {
-    if (this.isProcessing) return false;
 
-    const now = Date.now();
-    if (now - this.lastFrameTime < this.frameInterval) {
-      return false;
-    }
-
-    this.isProcessing = true;
-    try {
-      const filePath = `${this.context.filesDir}/live${index}.jpg`;
-      try {
-        fs.accessSync(filePath);
-      } catch {
-        this.isProcessing = false;
-        return false;
-      }
-
-      const imageSource = image.createImageSource(filePath);
-      if (!imageSource) {
-        this.isProcessing = false;
-        return false;
-      }
-      this.nextBuffer = await imageSource.createPixelMap();
-      imageSource.release();
-      if (this.activeBuffer) {
-        this.activeBuffer.release();
-      }
-      this.activeBuffer = this.nextBuffer;
-      this.nextBuffer = null;
-      this.lastFrameTime = now;
-      return true;
-    } catch (error) {
-      console.warn(TAG, "处理预览帧失败:", error);
-      return false;
-    } finally {
-      this.isProcessing = false;
-    }
-  }
-
-  getActiveBuffer(): PixelMap | null {
-    return this.activeBuffer;
-  }
-
-  release() {
-    if (this.activeBuffer) {
-      this.activeBuffer.release();
-      this.activeBuffer = null;
-    }
-    if (this.nextBuffer) {
-      this.nextBuffer.release();
-      this.nextBuffer = null;
-    }
-  }
-}
 
 interface photoInfo {
   pixelMap: PixelMap;

+ 2 - 2
entry/src/main/ets/pages/DeviceControllerPage.ets

@@ -653,7 +653,7 @@ struct DeviceControllerPage {
             Row() {
               Text('参数预设')
                 .fontColor($r('app.color.FFFFFF'))
-                .fontSize($r('app.float.fontSize_12'))
+                .fontSize($r('app.float.fontSize_30'))
                 .fontWeight(FontWeight.Medium)
                 .opacity(0.9)
             }
@@ -724,7 +724,7 @@ struct DeviceControllerPage {
             Row() {
               Text('参数设置')
                 .fontColor($r('app.color.FFFFFF'))
-                .fontSize($r('app.float.fontSize_12'))
+                .fontSize($r('app.float.fontSize_30'))
                 .fontWeight(FontWeight.Medium)
                 .opacity(0.9)
             }

+ 1 - 1
entry/src/main/ets/pages/ProcessIndex.ets

@@ -302,7 +302,7 @@ struct ProcessIndex {
   }
 
   connectUsbDevice=async()=>{
-    return USBDeviceManager.getInstance();
+    return await USBDeviceManager.getInstance();
   }
 
   onPageShow() {

+ 22 - 72
entry/src/main/ets/view/process/MultiMediaCollect.ets

@@ -1,5 +1,3 @@
-import image from '@ohos.multimedia.image';
-import fs from "@ohos.file.fs"
 import CommonEventManager from '@ohos.commonEventManager'
 import uploadInstance from '../../common/util/UploadUtil';
 import { DrawingInfo, DrawingPage } from '../../viewmodel/DrawingInfo';
@@ -10,6 +8,7 @@ import { ConfirmDialogParams } from '../../viewmodel/ConfirmDialogParam';
 import { ConfirmDialog } from '../ConfirmDialog';
 import promptAction from '@ohos.promptAction';
 import ProcessInfo from '../../viewmodel/process/ProcessInfo';
+import { PreviewManager } from '../../common/util/PreviewManager';
 
 const TAG = "Process camera upload"
 
@@ -275,19 +274,38 @@ export struct MultiMediaCollect {
 
   //开始预览 50ms一张图片(20帧)
   liveShow = async () => {
+    if (this.previewManager) {
+      this.previewManager.release();
+    }
+    if (this.readTimer) {
+      clearInterval(this.readTimer);
+    }
     this.previewManager = new PreviewManager(getContext(this));
     let index = 0;
+    let retryCount = 0;
+    const MAX_RETRIES = 5; // 最大重试次数
     this.readTimer = setInterval(async () => {
       if (!this.isStopView && !this.isCapturing) {
         try {
           const success = await this.previewManager!.processFrame(index);
           if (success) {
             this.commodityPixelMap = this.previewManager!.getActiveBuffer();
+            retryCount=0;
           }
         } catch (error) {
+          retryCount++
           console.warn(TAG, "预览更新失败:", error);
         }
       }
+      if(retryCount===MAX_RETRIES)
+      {
+        clearInterval(this.readTimer);
+        this.previewManager?.release();
+        this.stopView()
+        await sleep(50)
+        this.startView()
+        console.info(TAG, "预览更新:");
+      }
     }, 50);
   };
 
@@ -398,7 +416,6 @@ export struct MultiMediaCollect {
     await this.createSubscriber();
     await this.connectCamera();
     await this.queryFlashMode();
-    console.info('sssss'+this.seqNo)
     uploadInstance.uploadParams = {
       token: "your_token_here", // 替换为实际 token
       operationMediaId: this.selectOperationId, // 默认值
@@ -595,8 +612,8 @@ export struct MultiMediaCollect {
                       this.pinchCenterY = event.pinchCenterY - 121 - 520 / 2;   //rk3588
                       // this.pinchCenterX = event.pinchCenterX - 1090 / 2;     //rk3568
                       // this.pinchCenterY = event.pinchCenterY - 692 / 2;     //rk3568
-                      console.info(TAG,event.pinchCenterX,TAG,event.pinchCenterY)
-                      console.info(TAG,this.pinchCenterX,TAG,this.pinchCenterY)
+                      // console.info(TAG,event.pinchCenterX,TAG,event.pinchCenterY)
+                      // console.info(TAG,this.pinchCenterX,TAG,this.pinchCenterY)
                     })
                     .onActionUpdate((event: GestureEvent) => {
                       const newScale = this.lastScale * event.scale;
@@ -813,73 +830,6 @@ function sleep(ms: number): Promise<void> {
   return new Promise((resolve) => setTimeout(resolve, ms));
 }
 
-class PreviewManager {
-  private context: Context;
-  private activeBuffer: PixelMap | null = null;
-  private nextBuffer: PixelMap | null = null;
-  private isProcessing: boolean = false;
-  private lastFrameTime: number = 0;
-  private frameInterval: number = 50;
-
-  constructor(context: Context) {
-    this.context = context;
-  }
-
-  async processFrame(index: number): Promise<boolean> {
-    if (this.isProcessing) return false;
-
-    const now = Date.now();
-    if (now - this.lastFrameTime < this.frameInterval) {
-      return false;
-    }
-
-    this.isProcessing = true;
-    try {
-      const filePath = `${this.context.filesDir}/live${index}.jpg`;
-      try {
-        fs.accessSync(filePath);
-      } catch {
-        this.isProcessing = false;
-        return false;
-      }
-
-      const imageSource = image.createImageSource(filePath);
-      if (!imageSource) {
-        this.isProcessing = false;
-        return false;
-      }
-      this.nextBuffer = await imageSource.createPixelMap();
-      imageSource.release();
-      if (this.activeBuffer) {
-        this.activeBuffer.release();
-      }
-      this.activeBuffer = this.nextBuffer;
-      this.nextBuffer = null;
-      this.lastFrameTime = now;
-      return true;
-    } catch (error) {
-      console.warn(TAG, "处理预览帧失败:", error);
-      return false;
-    } finally {
-      this.isProcessing = false;
-    }
-  }
-
-  getActiveBuffer(): PixelMap | null {
-    return this.activeBuffer;
-  }
-
-  release() {
-    if (this.activeBuffer) {
-      this.activeBuffer.release();
-      this.activeBuffer = null;
-    }
-    if (this.nextBuffer) {
-      this.nextBuffer.release();
-      this.nextBuffer = null;
-    }
-  }
-}
 
 interface CameraParam {
   flash_mode: string;