Переглянути джерело

feat(camera): 添加相机焦距控制功能

- 新增 camera_focal_data 方法获取定焦器参数
- 在 DeviceConfig 模型中添加 camera_focal_distance 字段
- 在数据库中添加缺失的相机焦距字段
- 添加 camera_zoom_motor 设备类型及相关命令
- 实现相机焦段控制功能及调试接口
- 更新设备控制逻辑以支持焦距调节
rambo 19 годин тому
батько
коміт
edd2d348e2

+ 16 - 7
python/conifg_info.py

@@ -139,15 +139,24 @@ class ConfigManager:
                        "value": _value}
         return return_data
 
-    def is_camera_has_focal(self):
+    def camera_focal_data(self):
         """
-        是否有定焦器
+        定焦器参数获取
         """
-        data = {"addr": 208}
-        is_camera_has_focal = self.get_dynamic_value(data)
-        if is_camera_has_focal:
-            return True
-        return False
+        dataParams = {}
+        data = [{"addr": 208, "key": "is_camera_has_focal"},
+                {"addr": 209, "key": "camera_focal_distance"},
+                {"addr": 210, "key": "camera_focal_ratio"}]
+        for item in data:
+            value = self.get_dynamic_value(item)
+            match item["key"]:
+                case "is_camera_has_focal":
+                    dataParams[item["key"]] = True if value else False
+                case "camera_focal_distance":
+                    dataParams[item["key"]] = int(value)
+                case "camera_focal_ratio":
+                    dataParams[item["key"]] = float(value)
+        return dataParams
 
     def _init_config_keys(self):
         # test keys

+ 38 - 40
python/databases.py

@@ -4,7 +4,7 @@ from typing import Dict
 from datetime import datetime
 from typing import Optional
 import json
-from sqlalchemy import and_, desc, asc, delete,inspect
+from sqlalchemy import and_, desc, asc, delete, inspect
 import settings
 from utils.utils_func import check_path
 from sqlalchemy.dialects import sqlite
@@ -18,10 +18,10 @@ engine = create_engine(
     sqlite_url,
     echo=False,
     connect_args={"check_same_thread": False},
-    pool_size=20,        # 增加基础连接池大小
-    max_overflow=30,     # 增加最大溢出连接数
-    pool_timeout=60,     # 保持合理的超时时间
-    pool_recycle=1800,   # 连接回收时间(秒)
+    pool_size=20,  # 增加基础连接池大小
+    max_overflow=30,  # 增加最大溢出连接数
+    pool_timeout=60,  # 保持合理的超时时间
+    pool_recycle=1800,  # 连接回收时间(秒)
     pool_pre_ping=True,  # 检查连接有效性
 )
 
@@ -66,11 +66,11 @@ class CRUD:
         return result.rowcount
 
     def read(
-        self,
-        session: Session,
-        conditions: Optional[Dict] = None,
-        order_by: Optional[str] = None,
-        ascending: bool = True,
+            self,
+            session: Session,
+            conditions: Optional[Dict] = None,
+            order_by: Optional[str] = None,
+            ascending: bool = True,
     ):
         query = select(self.model)
         if conditions:
@@ -91,12 +91,12 @@ class CRUD:
         return data
 
     def read_all(
-        self,
-        session: Session,
-        conditions: Optional[Dict] = None,
-        order_by: Optional[str] = None,
-        ascending: bool = True,
-        join_conditions: Optional[list] = None,  # 新增:支持多个JOIN
+            self,
+            session: Session,
+            conditions: Optional[Dict] = None,
+            order_by: Optional[str] = None,
+            ascending: bool = True,
+            join_conditions: Optional[list] = None,  # 新增:支持多个JOIN
     ):
         query = select(self.model)
         # 处理JOIN逻辑
@@ -139,15 +139,15 @@ class CRUD:
         return db_obj
 
     def deleteConditions(
-        self,
-        session: Session,
-        conditions: Optional[Dict] = None,
-        is_soft_delete: bool = True,
+            self,
+            session: Session,
+            conditions: Optional[Dict] = None,
+            is_soft_delete: bool = True,
     ):
         query = select(self.model)
         if conditions is None:
             return False
-        
+
         # 构建查询条件
         query = query.where(
             and_(
@@ -157,13 +157,13 @@ class CRUD:
                 )
             )
         )
-        
+
         # 获取需要删除的对象
         objects_to_delete = session.exec(query).all()
-        
+
         # 检查模型是否包含 delete_time 字段
         model_columns = {column.name for column in inspect(self.model).columns}
-        if 'delete_time' in model_columns and is_soft_delete ==True:
+        if 'delete_time' in model_columns and is_soft_delete == True:
             # 软删除:更新 delete_time 字段
             for obj in objects_to_delete:
                 setattr(obj, 'delete_time', datetime.now())
@@ -175,7 +175,7 @@ class CRUD:
                 session.delete(obj)
             session.commit()
             print("硬删除完成")
-        
+
         return True
 
     def delete(self, session: Session, obj_id: int):
@@ -201,7 +201,7 @@ class CRUD:
                 )
             )
         )
-        print("SQL 打印==>",str(query))
+        print("SQL 打印==>", str(query))
         result = session.exec(query).first()
         if result:
             for key, value in kwargs.items():
@@ -258,7 +258,7 @@ def batch_insert_device_configs(session: Session, action_tabs: list, data_list:
 
 
 def batch_insert_device_configsNew(
-    session: Session, action_tabs: list, data_list: list
+        session: Session, action_tabs: list, data_list: list
 ):
     """批量插入数据到设备配置表"""
     for idx, tab in enumerate(action_tabs):
@@ -291,7 +291,7 @@ def batch_insert_sys_configs(session: Session, data_list: list):
 
 # 插入照片记录
 async def insert_photo_records(
-    image_deal_mode: int, goods_art_no: str, image_index: int, action_id: int
+        image_deal_mode: int, goods_art_no: str, image_index: int, action_id: int
 ):
     with SqlQuery() as session:  # 使用上下文管理器复用会话
         """批量插入数据到照片记录"""
@@ -314,42 +314,42 @@ async def insert_photo_records(
             "action_id": action_id,
         }
         # 异步插入一条数据
-        settings.syncPhotoRecord(syncData,action_type=1)
+        settings.syncPhotoRecord(syncData, action_type=1)
         return True, record_id
 
 
 def auto_add_missing_columns():
-
     """
     自动检测并添加缺失的数据库字段(最简化版本)
     只为 device_config 表添加缺失的字段
     """
     try:
         import sqlite3
-        
+
         # 连接数据库
         conn = sqlite3.connect(sqlite_file_name)
         cursor = conn.cursor()
-        
+
         # 检查表是否存在
         cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='device_config'")
         if not cursor.fetchone():
             conn.close()
             return
-        
+
         # 获取现有字段
         cursor.execute("PRAGMA table_info(device_config)")
         existing_columns = [row[1] for row in cursor.fetchall()]
-        
+
         # 定义需要添加的字段
         # 格式: (字段名, SQL类型, 默认值)
         # 根据你在 DeviceConfig model 中新增的字段来配置
         new_fields = [
             # 请在这里添加你实际新增的字段
             ("point_name", "VARCHAR", "DEFAULT 'A'"),
-            ("is_move_device", "BOOLEAN", "DEFAULT 1")
+            ("is_move_device", "BOOLEAN", "DEFAULT 1"),
+            ("camera_focal_distance", "INT", "DEFAULT 0")
         ]
-        
+
         # 添加缺失的字段
         for field_name, field_type, default_clause in new_fields:
             if field_name not in existing_columns:
@@ -360,16 +360,14 @@ def auto_add_missing_columns():
                 except Exception as e:
                     print(f"⚠️ 添加字段失败: {e}")
                     pass  # 忽略错误,继续下一个
-        
+
         conn.close()
-        
+
     except Exception as e:
         print(f"⚠️ 添加字段失败: {e}")
         pass  # 静默失败,不影响启动
 
 
-
-
 def SqlQuery():
     return next(__get_session())
 

+ 27 - 7
python/mcu/DeviceControl.py

@@ -43,19 +43,27 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
         self.msg_type = "mcu"
         self.command = {
             "to_device_move": 1,  # 设备运动
-            "to_init_device": 2,  # 初始化设备
+            'to_init_device': 2,  # 初始化设备
             "to_deal_other_device": 3,  # 处理其他设备
-            "get_all_info": 29,  # 获取所有信息
-            "set_deviation": 40,  # 设置偏移量
-            "get_deviation": 41,  # 读取偏移量
+            'get_all_info': 29,  # 获取所有信息
+            'set_deviation': 40,  # 设置偏移量
+            'get_deviation': 41,  # 读取偏移量
             "signal_forwarding": 91,  # 信号转发处理
             "signal_forwarding_return": 92,  # 信号转发返回
             "get_other_info": 44,  # 获取其他信息
             "open_rgb_led": 43,  ## RGB灯的处理与通讯
             "set_other_info": 45,  # 设置其他信息
             "query_remote_control_battery": 47,  # 查询遥控器电量
-            "set_turntable_mode": 48,  # 设置转盘通讯方式 1、串口、2、无线、3 混合
+            # "set_turntable_mode": 48,  # 设置转盘通讯方式 1、串口、2、无线、3 混合
+            "get_mcu_device_id": 49,  # 查询MCU的设备号
+            "mcu_power_off": 53,  # 设备关机
             "stop_mcu": 93,  # 停止运行mcu
+            "get_steering_temperature": 108,  # 查询舵机的当前的温度
+            # "steering_forwarding": 113,  # 舵机信号转发处理
+            # "steering_forwarding_callback": 114,  # 舵机信号转发返回
+            "get_motor_value": 119,  # 获取当前电机值;逻辑值
+            "set_zero_debug": 120,  # 简单的设备归零,用于调试
+            "set_stepper_disable": 121,  # 释放,或恢复使能
         }
         self.serial_ins = None
         self.mcu_deviation_set = McuDeviationSet(self)
@@ -102,6 +110,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             "turntable_position_motor": 7,
             "mp3_player": 8,
             "mcu": 99,
+            "camera_zoom_motor": 10,
         }
         self.last_move_time = time.time()
         self.device_name_dict_mapping = {
@@ -115,6 +124,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             7: "转盘位置",
             8: "播放音频",
             99: "mcu命令",
+            10: "相机焦段",
         }
         # 最近的mcu基础信息,用于获取数据状态检查
         self.last_mcu_info_data = {
@@ -199,7 +209,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
         if not data:
             return False
         return_data = self.analysis_data(data[1:])
-        is_camera_has_focal = self.config_manager.is_camera_has_focal()
+        camera_focal_data = self.config_manager.camera_focal_data()
         # print("return_data", return_data)
         # print("camera_high_motor_deviation_offset", self.camera_high_motor_deviation_offset)
         if return_data:
@@ -212,7 +222,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             code=0,
             msg="获取其他设备信息",
             device_status=2,
-            data={"camera_height": self.camera_height, "is_camera_has_focal": is_camera_has_focal}
+            data={"camera_height": self.camera_height, "camera_focal_data": camera_focal_data}
         )
         self.msg_type = 'mcu'
         return self.camera_height
@@ -515,6 +525,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             "翻板舵机高位",  # min 0 max 180,步长0.5
             "翻板舵机上升速度",  # min 1 max 10,步长1
             "翻板舵机下降速度",  # min 1 max 10,步长1
+            "相机焦段",  # min 1 max 10,步长1
         ]
         if action_name not in name_sets:
             self.msg_type = f"{type}_deviation"
@@ -1543,6 +1554,13 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                 value = value / 10  # value 单位毫米
                 assert 0 <= value <= 900
                 assert 0 <= max_speed <= 15000
+            case "camera_zoom_motor":
+                #         相机焦段设置
+                # 精确到0.1焦段
+                max_speed = 500 if max_speed is None else max_speed
+                up_speed = 100 if up_speed is None else up_speed
+                down_speed = 100 if down_speed is None else down_speed
+                assert 0 <= value <= 150
 
         _dir = True if value >= 0 else False
 
@@ -1686,6 +1704,8 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                 await self.smart_shooter.CameraShooter(msg_type="run_mcu")
             case "to_deal_device":
                 self.to_deal_device(device_name, value=value, _type=0, times=1)
+            case "camera_zoom_motor":
+                self.to_deal_device(device_name="camera_zoom_motor")
             case _:
                 pass
 

+ 57 - 41
python/mcu/Mcu.py

@@ -5,12 +5,15 @@ from .SerialIns import SerialIns
 import time
 from threading import Lock
 from collections import defaultdict
+
+
 # from threading import Thread
 
 
 class Mcu(BaseClass, metaclass=SingletonType):
     instance = None
     init_flag = None
+
     # sign_data = Signal(dict)
     # self_sign = Signal(dict)
 
@@ -119,7 +122,7 @@ class Mcu(BaseClass, metaclass=SingletonType):
             data = receive_data[1:].decode()
             if data == "设备初始化完成":
                 self.init_state = False
-                self.sendSocketMessage(msg=data,device_status=2)
+                self.sendSocketMessage(msg=data, device_status=2)
             print("115  print_mcu_error_data:", data)
         except BaseException as e:
             print("117 error {}".format(e))
@@ -380,8 +383,8 @@ class Mcu(BaseClass, metaclass=SingletonType):
 
             turntable_steering_deviation_dir = receive_data[7]
             turntable_steering_deviation = (
-                receive_data[8] << 8 | receive_data[9]
-            ) * 0.1
+                                                   receive_data[8] << 8 | receive_data[9]
+                                           ) * 0.1
             turntable_steering_deviation = (
                 turntable_steering_deviation * -1
                 if turntable_steering_deviation_dir == 0
@@ -459,14 +462,14 @@ class Mcu(BaseClass, metaclass=SingletonType):
 
     # LED 灯光处理
     def open_rgb_led(
-        self,
-        color_name,
-        led_command=1,
-        brightness=80,
-        enable=True,
-        mode="loop",
-        times=2,
-        interval=0.1,
+            self,
+            color_name,
+            led_command=1,
+            brightness=80,
+            enable=True,
+            mode="loop",
+            times=2,
+            interval=0.1,
     ):
         color_name_value = {
             "红色": (156, 6, 3),
@@ -518,7 +521,7 @@ class Mcu(BaseClass, metaclass=SingletonType):
                     "plugins_mode": "mcu",
                     "data": "MCU 打开串口失败",
                 }
-                self.sendSocketMessage(code=1,msg="接收链接信息", data=message)
+                self.sendSocketMessage(code=1, msg="接收链接信息", data=message)
                 self.serial_ins = None
                 self.connect_state = False
                 return False
@@ -532,11 +535,11 @@ class Mcu(BaseClass, metaclass=SingletonType):
             #     }
             # )
             message = {
-                    "_type": "show_info",
-                    "plugins_mode": "mcu",
-                    "data": "MCU 打开串口失败",
-                }
-            self.sendSocketMessage(code=1,msg="MCU 打开串口失败", data=message)
+                "_type": "show_info",
+                "plugins_mode": "mcu",
+                "data": "MCU 打开串口失败",
+            }
+            self.sendSocketMessage(code=1, msg="MCU 打开串口失败", data=message)
             self.serial_ins = None
             self.connect_state = False
             return False
@@ -611,21 +614,21 @@ class Mcu(BaseClass, metaclass=SingletonType):
     def mcu_move_state(self):
         if self.m_t == 1:
             if (
-                self.state_camera_motor == 2
-                and self.state_camera_steering == 2
-                and self.state_turntable_steering == 2
-                and self.state_overturn_steering == 2
+                    self.state_camera_motor == 2
+                    and self.state_camera_steering == 2
+                    and self.state_turntable_steering == 2
+                    and self.state_overturn_steering == 2
             ):
                 self._mcu_move_state = 2
             else:
                 self._mcu_move_state = 1
         else:
             if (
-                self.state_camera_motor == 2
-                and self.state_camera_steering == 2
-                and self.state_turntable_steering == 2
-                and self.state_overturn_steering == 2
-                and self.state_move_turntable_steering == 2
+                    self.state_camera_motor == 2
+                    and self.state_camera_steering == 2
+                    and self.state_turntable_steering == 2
+                    and self.state_overturn_steering == 2
+                    and self.state_move_turntable_steering == 2
             ):
                 self._mcu_move_state = 2
             else:
@@ -674,7 +677,7 @@ class Mcu(BaseClass, metaclass=SingletonType):
         #     {"_type": "show_info", "plugins_mode": "mcu", "data": "MCU 连接失败"}
         # )
         message = {"_type": "show_info", "plugins_mode": "mcu", "data": "MCU 连接失败"}
-        self.sendSocketMessage(code=1,msg="MCU 连接失败",data=message)
+        self.sendSocketMessage(code=1, msg="MCU 连接失败", data=message)
         self.close_connect()
 
     def __del__(self):
@@ -844,15 +847,15 @@ class Mcu(BaseClass, metaclass=SingletonType):
         self.is_just_init_time = False
 
     def to_device_move(
-        self,
-        device_name,
-        value=0,
-        max_speed=None,
-        up_speed=None,
-        down_speed=None,
-        _is_debug=0,
-        is_relative=0,
-        is_deviation=1,
+            self,
+            device_name,
+            value=0,
+            max_speed=None,
+            up_speed=None,
+            down_speed=None,
+            _is_debug=0,
+            is_relative=0,
+            is_deviation=1,
     ):
         """
         此处输入单位为 毫米,以及度  需要先缩小,再放大
@@ -946,7 +949,7 @@ class Mcu(BaseClass, metaclass=SingletonType):
 
     # 通用串口数据解析器
     def get_data_from_receive_data(
-        self, receive_data, start, len_data, data_magnification=1
+            self, receive_data, start, len_data, data_magnification=1
     ):
         # data_magnification 数据放大倍数,或缩小倍数,默认为1
         try:
@@ -958,10 +961,10 @@ class Mcu(BaseClass, metaclass=SingletonType):
                 return data * data_magnification
             elif len_data == 4:
                 data = (
-                    receive_data[start] << 24
-                    | receive_data[start + 1] << 16
-                    | receive_data[start + 2] << 8
-                    | receive_data[start + 3]
+                        receive_data[start] << 24
+                        | receive_data[start + 1] << 16
+                        | receive_data[start + 2] << 8
+                        | receive_data[start + 3]
                 )
                 return data * data_magnification
             return None
@@ -1012,6 +1015,19 @@ class McuDebug(object):
             is_deviation=self.is_deviation,
         )
 
+    def camera_zoom(self, value, max_speed=None, up_speed=None, down_speed=None, times=1, is_response=False):
+        # 相机焦段
+        self.windows.mcu.to_device_move(device_name="camera_zoom_motor",
+                                        value=value,
+                                        max_speed=max_speed,
+                                        up_speed=up_speed,
+                                        down_speed=down_speed,
+                                        _is_debug=self.is_debug,
+                                        is_deviation=self.is_deviation,
+                                        times=times,
+                                        is_response=is_response,
+                                        )
+
     def turntable_steering(self, value):
         # 转盘舵机
         self.windows.mcu.to_device_move(

+ 15 - 0
python/mcu/McuDebug.py

@@ -1,4 +1,6 @@
 import time
+
+
 class McuDebug(object):
 
     def __init__(self, mcu, is_debug=True, is_deviation=False):
@@ -57,6 +59,19 @@ class McuDebug(object):
             is_deviation=self.is_deviation,
         )
 
+    def camera_zoom(self, value, max_speed=None, up_speed=None, down_speed=None, times=1, is_response=False):
+        # 相机焦段
+        self.mcu.to_device_move(device_name="camera_zoom_motor",
+                                value=value,
+                                max_speed=max_speed,
+                                up_speed=up_speed,
+                                down_speed=down_speed,
+                                _is_debug=self.is_debug,
+                                is_deviation=self.is_deviation,
+                                times=times,
+                                is_response=is_response,
+                                )
+
     def to_deal_device(self, device_name, value=1, _type=0, times=1):
         self.mcu.to_deal_device(
             device_name, value=value, _type=_type, times=times

+ 2 - 0
python/mcu/OtherSet.py

@@ -108,6 +108,8 @@ class OtherSet():
                 self.mcu_debug.overturn_steering(value=value)
             if name == "翻板舵机高位":
                 self.mcu_debug.overturn_steering(value=value)
+            if name == "相机焦段":
+                self.mcu_debug.camera_zoom(value=value)
             if name == "翻板舵机上升速度":
                 pass
             print(value, name)

+ 6 - 3
python/model/device_config.py

@@ -1,6 +1,8 @@
 from typing import Optional
 from datetime import datetime
 from sqlmodel import Field, SQLModel
+
+
 # 定义DeviceConfig模型类
 
 class DeviceConfig(SQLModel, table=True):
@@ -10,9 +12,9 @@ class DeviceConfig(SQLModel, table=True):
     action_name: Optional[str] = Field(
         default=None, index=True, max_length=128, description="动作名称"
     )
-    is_system: Optional[bool] = Field(default=False,description="是否是系统配置")
-    action_status: Optional[bool] = Field(default=True,description="是否启用;true或者false")
-    action_index: Optional[int] = Field(default=999,description="排序,默认999")
+    is_system: Optional[bool] = Field(default=False, description="是否是系统配置")
+    action_status: Optional[bool] = Field(default=True, description="是否启用;true或者false")
+    action_index: Optional[int] = Field(default=999, description="排序,默认999")
     camera_height: Optional[int] = Field(default=None, description="相机高度;步长1;最小0;最大400")
     camera_angle: Optional[float] = Field(default=None, description="相机倾角;步长0.1,最小-40;最大40")
     number_focus: Optional[int] = Field(default=None, description="对焦次数;最小0;最大1")
@@ -26,6 +28,7 @@ class DeviceConfig(SQLModel, table=True):
     )
     point_name: Optional[str] = Field(default="A", description="点位名称,默认A点")
     is_move_device: Optional[bool] = Field(default=True, description="是否移动设备,默认移动设备")
+    camera_focal_distance: Optional[int] = Field(default=None, description="相机焦距;最小0;最大动态获取")
     led_switch: Optional[bool] = Field(default=False, description="Led灯光开关;")
     is_wait: Optional[bool] = Field(default=False, description="没用;")
     is_need_confirm: Optional[bool] = Field(default=False, description="没用;")

+ 7 - 7
python/sockets/message_handler.py

@@ -348,13 +348,6 @@ async def handlerSend(
                 ),
                 name="run_mcu_single",
             )
-        case "get_device_info":
-            device_ctrl = DeviceControl(
-                websocket_manager=manager, smart_shooter=smart_shooter
-            )
-            device_ctrl.getDeviationInfo()
-            await asyncio.sleep(0.1)
-            device_ctrl.get_device_info()
         case "handler_take_picture":
             if data is None:
                 PointName = "A"
@@ -820,6 +813,13 @@ async def handlerSend(
                 data={"status": dynamic_configs},
             )
             await manager.send_personal_message(data, websocket)
+        case "get_device_info":
+            device_ctrl = DeviceControl(
+                websocket_manager=manager, smart_shooter=smart_shooter
+            )
+            device_ctrl.getDeviationInfo()
+            await asyncio.sleep(0.1)
+            device_ctrl.get_device_info()
         case _:
             data = manager.jsonMessage(code=1, msg="未知消息")
             await manager.send_personal_message(data, websocket)