""" 设备扫描工具 - 通过唯一标识符而非索引来识别设备 解决重启后设备序号混乱的问题 """ import cv2 import serial.tools.list_ports import warnings import sys import io from typing import List, Dict, Optional def get_camera_info(cam_index: int) -> Optional[Dict]: """ 获取采集卡的详细信息(包括设备名称) :param cam_index: 采集卡索引 :return: 包含设备信息的字典,如果失败返回None """ old_stderr = sys.stderr suppressed_output = io.StringIO() try: sys.stderr = suppressed_output with warnings.catch_warnings(): warnings.filterwarnings('ignore') # 尝试多种后端 backends = [ (cam_index, cv2.CAP_DSHOW), (cam_index, cv2.CAP_ANY), (cam_index, None), ] cap = None for idx, backend in backends: try: if backend is not None: cap = cv2.VideoCapture(idx, backend) else: cap = cv2.VideoCapture(idx) if cap.isOpened(): ret, frame = cap.read() if ret and frame is not None: # 尝试获取设备名称(不同后端可能支持不同) device_name = None backend_name = None if backend == cv2.CAP_DSHOW: backend_name = "DirectShow" # DirectShow可能支持获取设备名称 try: # 某些情况下可以通过属性获取 pass # OpenCV限制,无法直接获取设备名称 except: pass elif backend == cv2.CAP_ANY: backend_name = "Any" else: backend_name = "Default" # 获取分辨率信息作为辅助标识 width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) cap.release() return { 'index': cam_index, 'backend': backend_name, 'width': width, 'height': height, 'name': f"采集卡-{cam_index}", 'available': True } else: if cap: cap.release() cap = None except Exception: if cap: try: cap.release() except: pass cap = None continue return None finally: sys.stderr = old_stderr def scan_cameras_with_info(max_index: int = 10) -> List[Dict]: """ 扫描所有可用采集卡,返回详细信息列表 :param max_index: 最大扫描索引 :return: 采集卡信息列表 """ cameras = [] for i in range(max_index + 1): info = get_camera_info(i) if info: cameras.append(info) return cameras def get_serial_port_info(port_name: str) -> Optional[Dict]: """ 获取串口的详细信息(包括硬件ID和描述) :param port_name: 串口名称(如 "COM3") :return: 包含串口信息的字典 """ try: ports = serial.tools.list_ports.comports() for port in ports: if port.device == port_name: return { 'device': port.device, 'description': port.description, 'hwid': port.hwid, # 硬件ID(唯一标识) 'vid': port.vid if hasattr(port, 'vid') else None, 'pid': port.pid if hasattr(port, 'pid') else None, 'serial_number': port.serial_number if hasattr(port, 'serial_number') else None, 'manufacturer': port.manufacturer if hasattr(port, 'manufacturer') else None, 'name': port.description or port.device } except Exception as e: print(f"⚠️ 获取串口信息失败: {e}") return None def scan_serial_ports_with_info() -> List[Dict]: """ 扫描所有可用串口,返回详细信息列表 :return: 串口信息列表 """ ports_info = [] try: ports = serial.tools.list_ports.comports() for port in ports: info = { 'device': port.device, 'description': port.description, 'hwid': port.hwid, 'vid': port.vid if hasattr(port, 'vid') else None, 'pid': port.pid if hasattr(port, 'pid') else None, 'serial_number': port.serial_number if hasattr(port, 'serial_number') else None, 'manufacturer': port.manufacturer if hasattr(port, 'manufacturer') else None, 'name': port.description or port.device } ports_info.append(info) except Exception as e: print(f"⚠️ 扫描串口失败: {e}") # 按设备名排序 ports_info.sort(key=lambda x: int(x['device'].replace('COM', '')) if x['device'].replace('COM', '').isdigit() else 999) return ports_info def find_camera_by_index(cameras: List[Dict], index: int) -> Optional[Dict]: """通过索引查找采集卡""" for cam in cameras: if cam['index'] == index: return cam return None def find_serial_by_hwid(ports: List[Dict], hwid: str) -> Optional[Dict]: """通过硬件ID查找串口(最可靠)""" for port in ports: if port['hwid'] == hwid: return port return None def find_serial_by_device(ports: List[Dict], device: str) -> Optional[Dict]: """通过设备名查找串口(兼容旧配置)""" for port in ports: if port['device'] == device: return port return None if __name__ == "__main__": # 测试代码 print("=" * 60) print("采集卡扫描:") print("=" * 60) cameras = scan_cameras_with_info() for cam in cameras: print(f" 索引 {cam['index']}: {cam['name']} ({cam['width']}x{cam['height']})") print("\n" + "=" * 60) print("串口扫描:") print("=" * 60) ports = scan_serial_ports_with_info() for port in ports: print(f" {port['device']}: {port['name']}") print(f" 硬件ID: {port['hwid']}") if port['vid'] and port['pid']: print(f" VID/PID: {port['vid']:04X}/{port['pid']:04X}")