ソースを参照

```
feat(database): 添加数据库自动迁移功能

- 实现 auto_add_missing_columns 函数,自动检测并添加缺失的数据库字段
- 只添加新字段,不会修改或删除现有字段
- 不会丢失任何数据,可以安全地重复执行(幂等性)
- 在 create_all_database 函数中调用自动迁移功能

feat(device-config): 添加点位名称和设备移动控制字段

- 在 DeviceConfig 模型中新增 point_name 字段,默认值为 "A"
- 新增 is_move_device 字段,默认值为 True
- 控制设备是否执行移动操作

feat(program-item): 添加点位控制和条件执行功能

- 在 ProgramItem 类中新增 point_name 和 is_move_device 属性
- 根据 is_move_device 控制设备运动逻辑的执行
- 配置设置是否运动设备,不运动直接去拍照

feat(smart-shooter): 增强相机控制功能

- 添加 CameraKey 参数支持多相机管理
- 实现 sendMessageSocket 方法统一消息发送
- 改进 ISO 配置和相机属性获取逻辑
- 优化相机状态检测和预览控制流程

feat(capture): 添加相机配置初始化功能

- 实现 initConfigIsoSettings 方法首次初始化相机配置
- 根据连接的相机列表自动分配 A/B/C 点位配置
- 添加相机列表信息打印和状态跟踪

refactor(settings): 优化系统配置管理

- 新增 updateSysConfigs 函数用于更新系统配置
- 支持字典和列表类型的 JSON 序列化存储
- 改进配置获取和处理逻辑
```

rambo 3 週間 前
コミット
26570e9c8c

+ 73 - 0
python/databases.py

@@ -29,6 +29,8 @@ engine = create_engine(
 # 创建表
 def create_all_database():
     SQLModel.metadata.create_all(engine)
+    # 执行自动迁移
+    auto_add_missing_columns()
 
 
 # 创建会话
@@ -316,6 +318,77 @@ async def insert_photo_records(
         return True, record_id
 
 
+def auto_add_missing_columns():
+    """
+    自动检测并添加缺失的数据库字段
+    - 只添加新字段,不会修改或删除现有字段
+    - 不会丢失任何数据
+    - 可以安全地重复执行(幂等性)
+    """
+    from sqlalchemy import inspect, text
+
+    inspector = inspect(engine)
+
+    # 定义每个模型类对应的表名和期望的列
+    models_tables = {
+        DeviceConfig: "device_config",
+        PhotoRecord: "Photo_record",
+    }
+
+    with engine.connect() as conn:
+        for model_class, table_name in models_tables.items():
+            # 检查表是否存在
+            if not inspector.has_table(table_name):
+                continue
+
+            # 获取数据库中现有的列
+            existing_columns = {
+                col["name"] for col in inspector.get_columns(table_name)
+            }
+
+            # 获取模型中定义的所有列
+            model_columns = model_class.__table__.columns
+
+            for column_name, column_obj in model_columns.items():
+                # 如果列已存在,跳过
+                if column_name in existing_columns:
+                    continue
+
+                try:
+                    # 构建列类型字符串
+                    column_type = str(column_obj.type.compile(engine.dialect))
+
+                    # 处理 NULL/NOT NULL
+                    nullable_str = "NULL" if column_obj.nullable else "NOT NULL"
+
+                    # 处理默认值
+                    default_str = ""
+                    if column_obj.default is not None:
+                        default_value = column_obj.default.arg
+                        if callable(default_value):
+                            # 如果是函数(如 func.now()),SQLite 不支持直接在 ALTER TABLE 中使用
+                            continue
+                        elif isinstance(default_value, bool):
+                            default_str = f"DEFAULT {int(default_value)}"
+                        elif isinstance(default_value, (int, float)):
+                            default_str = f"DEFAULT {default_value}"
+                        elif isinstance(default_value, str):
+                            default_str = f"DEFAULT '{default_value}'"
+
+                    # 构建 ALTER TABLE 语句
+                    sql = f"ALTER TABLE {table_name} ADD COLUMN {column_name} {column_type} {nullable_str} {default_str}".strip()
+
+                    conn.execute(text(sql))
+                    print(f"✅ 成功添加字段: {table_name}.{column_name}")
+
+                except Exception as e:
+                    print(f"⚠️ 添加字段失败 {table_name}.{column_name}: {e}")
+
+        conn.commit()
+
+    print("🎉 数据库迁移检查完成")
+
+
 def SqlQuery():
     return next(__get_session())
 

+ 2 - 0
python/mcu/DeviceControl.py

@@ -1618,6 +1618,8 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                     new_init_config["shoe_upturn"] = False
                     new_init_config["pre_delay"] = 0.0
                     new_init_config["after_delay"] = 0.0
+                    new_init_config["point_name"] = "A"
+                    new_init_config["is_move_device"] = True
                     new_init_config["led_switch"] = True
                     new_init_config["turntable_angle"] = 0.0
                     new_init_config["is_wait"] = False

+ 96 - 84
python/mcu/ProgramItem.py

@@ -51,6 +51,8 @@ class ProgramItem(BaseClass):
         # self.action_type = self.get_value(action_data, "execution_type", "程序1")
         self.action_name = self.get_value(action_data, "action_name", "")
         self.is_wait = self.get_value(action_data, "is_wait", False)
+        self.point_name = self.get_value(action_data, "point_name", "A")
+        self.is_move_device = self.get_value(action_data, "is_move_device", True)
         self.is_need_confirm = self.get_value(action_data, "is_need_confirm", False)
         self.image_index = self.get_value(action_data, "picture_index", 99)
         self.camera_height = self.get_value(action_data, "camera_height", 0)
@@ -277,116 +279,119 @@ class ProgramItem(BaseClass):
         current_time = time.time()
         self.mcu.is_get_mcu_state = self.is_get_mcu_state 
         # ============连接MCU 处理步进电机与舵机等
-        if settings.IS_MCU:
-            if self.mode_type != "其他配置" and await self.check_mcu_move_is_stop() is False:
-                # MCU运动是否有停止检查,设定超时时间
-                return
-            print(
-                "{} 检查停止时间1:{}".format(
-                    self.action_name, time.time() - start_time
+        if self.is_move_device:
+            if settings.IS_MCU:
+                if self.mode_type != "其他配置" and await self.check_mcu_move_is_stop() is False:
+                    # MCU运动是否有停止检查,设定超时时间
+                    return
+                print(
+                    "{} 检查停止时间1:{}".format(
+                        self.action_name, time.time() - start_time
+                    )
                 )
-            )
-            if self.is_led:
-                self.mcu.to_deal_device(device_name="laser_position", value=1)
-            else:
-                self.mcu.to_deal_device(device_name="laser_position", value=0)
+                if self.is_led:
+                    self.mcu.to_deal_device(device_name="laser_position", value=1)
+                else:
+                    self.mcu.to_deal_device(device_name="laser_position", value=0)
 
-            if self.shoe_overturn:
-                self.mcu.to_deal_device(device_name="overturn_steering")
-                await asyncio.sleep(0.001)
-            if self.camera_height is not None:
-                if (current_time - self.last_move_time)>110:
-                    if self.camera_height == 0:
+                if self.shoe_overturn:
+                    self.mcu.to_deal_device(device_name="overturn_steering")
+                    await asyncio.sleep(0.001)
+                if self.camera_height is not None:
+                    if (current_time - self.last_move_time)>110:
+                        if self.camera_height == 0:
+                            self.mcu.to_device_move(
+                                device_name="camera_high_motor", value=1
+                            )
+                        elif self.camera_height == 400:
+                            self.mcu.to_device_move(
+                                device_name="camera_high_motor", value=399
+                            )
+                        else:
+                            self.mcu.to_device_move(
+                                device_name="camera_high_motor", value=self.camera_height-1
+                            )
+                        await asyncio.sleep(0.01)
+                        logger.info("设备延迟执行===>,%s",time.time())
+                    self.mcu.to_device_move(
+                        device_name="camera_high_motor", value=self.camera_height
+                    )
+                    self.last_camera_height = self.camera_height
+                    await asyncio.sleep(0.01)
+                if self.camera_angle is not None:
+                    if self.turntable_position == -40:
                         self.mcu.to_device_move(
-                            device_name="camera_high_motor", value=1
+                            device_name="camera_steering", value=-39.9
                         )
-                    elif self.camera_height == 400:
+                        logger.info("转盘位置首次运动===>,%s",-39.9)
+                    elif self.turntable_position == 40:
                         self.mcu.to_device_move(
-                            device_name="camera_high_motor", value=399
+                            device_name="camera_steering", value=39.9
                         )
+                        logger.info("转盘位置首次运动===>,%s",39.9)
                     else:
                         self.mcu.to_device_move(
-                            device_name="camera_high_motor", value=self.camera_height-1
+                            device_name="camera_steering", value=self.camera_angle-0.1
                         )
+                        logger.info("转盘位置首次运动===>,%s",self.camera_angle-0.1)
                     await asyncio.sleep(0.01)
-                    logger.info("设备延迟执行===>,%s",time.time())
-                self.mcu.to_device_move(
-                    device_name="camera_high_motor", value=self.camera_height
-                )
-                self.last_camera_height = self.camera_height
-                await asyncio.sleep(0.01)
-            if self.camera_angle is not None:
-                if self.turntable_position == -40:
                     self.mcu.to_device_move(
-                        device_name="camera_steering", value=-39.9
+                        device_name="camera_steering", value=self.camera_angle
                     )
-                    logger.info("转盘位置首次运动===>,%s",-39.9)
-                elif self.turntable_position == 40:
-                    self.mcu.to_device_move(
-                        device_name="camera_steering", value=39.9
-                    )
-                    logger.info("转盘位置首次运动===>,%s",39.9)
-                else:
-                    self.mcu.to_device_move(
-                        device_name="camera_steering", value=self.camera_angle-0.1
-                    )
-                    logger.info("转盘位置首次运动===>,%s",self.camera_angle-0.1)
-                await asyncio.sleep(0.01)
-                self.mcu.to_device_move(
-                    device_name="camera_steering", value=self.camera_angle
-                )
-                await asyncio.sleep(0.01)
+                    await asyncio.sleep(0.01)
 
-            if self.turntable_position is not None:
-                if self.turntable_position == 0:
+                if self.turntable_position is not None:
+                    if self.turntable_position == 0:
                         self.mcu.to_device_move(
-                            device_name="turntable_position_motor", value=1
-                        )
+                                device_name="turntable_position_motor", value=1
+                            )
                         logger.info("转盘位置首次运动===>,%s",1)
-                elif self.turntable_position == 800:
+                    elif self.turntable_position == 800:
+                        self.mcu.to_device_move(
+                            device_name="turntable_position_motor", value=799
+                        )
+                        logger.info("转盘位置首次运动===>,%s",799)
+                    else:
+                        self.mcu.to_device_move(
+                            device_name="turntable_position_motor", value=self.turntable_position-1
+                        )
+                        logger.info("转盘位置首次运动===>,%s",self.turntable_position-1)
+                    await asyncio.sleep(0.01)
                     self.mcu.to_device_move(
-                        device_name="turntable_position_motor", value=799
+                        device_name="turntable_position_motor",
+                        value=self.turntable_position,
                     )
-                    logger.info("转盘位置首次运动===>,%s",799)
-                else:
+                    logger.info("转盘位置2次运动===>,%s",self.turntable_position)
+                    await asyncio.sleep(0.01)
+
+                if self.turntable_angle is not None:
                     self.mcu.to_device_move(
-                        device_name="turntable_position_motor", value=self.turntable_position-1
+                        device_name="turntable_steering", value=self.turntable_angle
                     )
-                    logger.info("转盘位置首次运动===>,%s",self.turntable_position-1)
-                await asyncio.sleep(0.01)
-                self.mcu.to_device_move(
-                    device_name="turntable_position_motor",
-                    value=self.turntable_position,
-                )
-                logger.info("转盘位置2次运动===>,%s",self.turntable_position)
-                await asyncio.sleep(0.01)
-
-            if self.turntable_angle is not None:
-                self.mcu.to_device_move(
-                    device_name="turntable_steering", value=self.turntable_angle
-                )
-                await asyncio.sleep(0.01)
+                    await asyncio.sleep(0.01)
 
-            # MCU运动是否有停止检查,设定超时时间
-            # self.mcu.send_all_cmd()
-            asyncio.create_task(self.mcu.send_all_cmd())
-            if self.mode_type != "其他配置":
-                await asyncio.sleep(0.2)
-                print("二次检查")
-                if await self.check_mcu_move_is_stop(re_check=True) is False:
-                    print("MCU检测运动未停止,自动退出,   提前退出")
-                    return
-        # logger.info("设备最后一次执行时间===>,%s",current_time)
-        self.mcu.is_get_mcu_state = True
+                # MCU运动是否有停止检查,设定超时时间
+                # self.mcu.send_all_cmd()
+                asyncio.create_task(self.mcu.send_all_cmd())
+                if self.mode_type != "其他配置":
+                    await asyncio.sleep(0.2)
+                    print("二次检查")
+                    if await self.check_mcu_move_is_stop(re_check=True) is False:
+                        print("MCU检测运动未停止,自动退出,   提前退出")
+                        return
+            # logger.info("设备最后一次执行时间===>,%s",current_time)
+            self.mcu.is_get_mcu_state = True
         if self.delay_time:
             await asyncio.sleep(self.delay_time)
         if self.is_photograph:
             # print("photograph==================")
             self.mcu.to_deal_device(device_name="buzzer", times=1)
             # 用于临时拍照计数
-            if not await self.camera_check_mcu_move_is_stop(re_check=True):
-                logger.info("拍照前运动检测失败===>,延迟0.5秒后拍摄")
-                await asyncio.sleep(0.1)
+            #  配置设置是否运动设备,不运动直接去拍照
+            if self.is_move_device:
+                if not await self.camera_check_mcu_move_is_stop(re_check=True):
+                    logger.info("拍照前运动检测失败===>,延迟0.5秒后拍摄")
+                    await asyncio.sleep(0.1)
                 # return
             is_af = True if self.af_times > 0 else False
             if self.smart_shooter != None:
@@ -396,12 +401,19 @@ class ProgramItem(BaseClass):
                 goods_art_no = self.goods_art_no
                 if record_id == -1:
                     goods_art_no = ""
+                camera_configs = settings.getSysConfigs(
+                    "camera_configs",
+                    "iso_config",
+                    None,
+                )
+                CameraKey = camera_configs[self.point_name].get("CameraKey",None)
                 print("smart shooter CameraShooter", record_id, goods_art_no)
                 await self.smart_shooter.CameraShooter(
                     msg_type="run_mcu",
                     goods_art_no=goods_art_no,
                     id=record_id,
                     is_af=is_af,
+                    CameraKey=CameraKey,
                 )
                 print("smart shooter CameraShooter end")
             else:

+ 7 - 0
python/mcu/RemoteControlV2.py

@@ -305,12 +305,19 @@ class RemoteControlV2(BaseClass):
                 capture_one.run_capture_action("Capture")
                 print("开始单拍1-完成拍照")
             else:
+                camera_configs = settings.getSysConfigs(
+                    "camera_configs",
+                    "iso_config",
+                    None,
+                )
+                CameraKey = camera_configs[record.point_name].get("CameraKey", None)
                 loop = asyncio.get_event_loop()
                 loop.create_task(
                     smart_shooter.CameraShooter(
                         msg_type="handler_take_picture",
                         goods_art_no=record.goods_art_no,
                         id=record_id,
+                        CameraKey=CameraKey,
                     ),
                     name="CameraShooter",
                 )

+ 105 - 57
python/mcu/capture/smart_shooter_class.py

@@ -5,14 +5,17 @@ import zmq
 import asyncio
 from PIL import Image
 from io import BytesIO
-import base64,threading
+import base64, threading
 import zmq, sys, time
 from utils.SingletonType import SingletonType
 import settings
 import logging
+from databases import SqlQuery, CRUD, SysConfigs
 from utils.common import message_queue
-
+from models import SysConfigParams
 logger = logging.getLogger(__name__)
+
+
 # 定义为单例模式,避免被重复实例化
 class SmartShooter(metaclass=SingletonType):
     SET_REQ = "tcp://127.0.0.1:54544"
@@ -51,6 +54,10 @@ class SmartShooter(metaclass=SingletonType):
         req_socket.connect(self.SET_REQ)
         return req_socket, context
 
+    async def sendMessageSocket(self, message):
+        if self.websocket_manager and self.websocket:
+            await self.websocket_manager.send_personal_message(message, self.websocket)
+
     def __create_listen(self) -> tuple[zmq.Socket, zmq.Context]:
         context = zmq.Context()
         listen_socket = context.socket(zmq.SUB)
@@ -64,7 +71,7 @@ class SmartShooter(metaclass=SingletonType):
         return listen_socket, context
 
     async def GetCameraProperty(self, CameraKey=None):
-        '''获取相机属性'''
+        """获取相机属性"""
         await asyncio.sleep(0.01)
         """
             实时获取相机信息,是否连接、软件是否被打开
@@ -92,8 +99,8 @@ class SmartShooter(metaclass=SingletonType):
                 return False, "相机未连接"
             # 链接的相机
             CameraStatus = False
-            CameraIndex =-1
-            for cam_idx,item in enumerate(cameraInfo):
+            CameraIndex = -1
+            for cam_idx, item in enumerate(cameraInfo):
                 CameraStatus = item.get("CameraStatus") in ["Ready", "Busy"]
                 if CameraStatus == True:
                     CameraIndex = cam_idx
@@ -116,7 +123,7 @@ class SmartShooter(metaclass=SingletonType):
             msg_send = "相机未连接或软件未打开"
             return False, msg_send
 
-    async def GetCameraInfo(self, is_send=True, msg_type="",CameraKey=None):
+    async def GetCameraInfo(self, is_send=True, msg_type="", CameraKey=None):
         await asyncio.sleep(0.001)
         self.msg_type = msg_type
         """
@@ -145,9 +152,7 @@ class SmartShooter(metaclass=SingletonType):
                         "msg_type": self.msg_type,
                         "device_status": -1,
                     }
-                    await self.websocket_manager.send_personal_message(
-                        message, self.websocket
-                    )
+                    await self.sendMessageSocket(message)
                 return False, msg_send
             cameraInfo = json_msg.get("CameraInfo")
             if cameraInfo == None or len(cameraInfo) == 0:
@@ -161,14 +166,21 @@ class SmartShooter(metaclass=SingletonType):
                         "msg_type": self.msg_type,
                         "device_status": -1,
                     }
-                    await self.websocket_manager.send_personal_message(
-                        message, self.websocket
-                    )
+                    await self.sendMessageSocket(message)
                 return False, "相机未连接"
             # 链接的相机
             CameraStatus = any(
                 item.get("CameraStatus") in ["Ready", "Busy"] for item in cameraInfo
             )
+            CameraLists = [
+                {
+                    "CameraSelection": item.get("CameraSelection"),
+                    "CameraKey": item.get("CameraKey"),
+                    "CameraName": item.get("CameraName"),
+                    "CameraStatus": item.get("CameraStatus") in ["Ready", "Busy"],
+                }
+                for item in cameraInfo
+            ]
             if not CameraStatus:
                 self.connect_status = False
                 msg_send = "相机未连接"
@@ -180,12 +192,11 @@ class SmartShooter(metaclass=SingletonType):
                         "msg_type": self.msg_type,
                         "device_status": -1,
                     }
-                    await self.websocket_manager.send_personal_message(
-                        message, self.websocket
-                    )
+                    await self.sendMessageSocket(message)
                 return False, msg_send
             self.connect_status = True
             msg_send = "相机已连接"
+            print("CameraLists", CameraLists)
             if is_send:
                 message = {
                     "code": 0,
@@ -194,10 +205,9 @@ class SmartShooter(metaclass=SingletonType):
                     "msg_type": self.msg_type,
                     "device_status": 2,
                 }
-                await self.websocket_manager.send_personal_message(
-                    message, self.websocket
-                )
+                await self.sendMessageSocket(message)
             # print("相机已连接状态信息---->", cameraInfo)
+            self.initConfigIsoSettings(CameraLists=CameraLists)
             return True, "相机已连接"
         except zmq.Again:
             print("获取相机信息超时,继续监听...")
@@ -210,12 +220,10 @@ class SmartShooter(metaclass=SingletonType):
                     "msg_type": self.msg_type,
                     "device_status": 2,
                 }
-                await self.websocket_manager.send_personal_message(
-                    message, self.websocket
-                )
+                await self.sendMessageSocket(message)
             return False, msg_send
         except Exception as e:
-            print("拍照异常", e)
+            print("相机状态获取异常", e)
             self.connect_status = False
             socket.close()
             context.term()
@@ -228,16 +236,14 @@ class SmartShooter(metaclass=SingletonType):
                     "msg_type": self.msg_type,
                     "device_status": -1,
                 }
-                await self.websocket_manager.send_personal_message(
-                    message, self.websocket
-                )
+                await self.sendMessageSocket(message)
             return False, msg_send
 
-    async def SetCameraFPS(self, fps=5,CameraKey=None):
+    async def SetCameraFPS(self, fps=5, CameraKey=None):
         """
         激活相机预览
         """
-        camera_states, _ = await self.GetCameraInfo(is_send=False,CameraKey=CameraKey)
+        camera_states, _ = await self.GetCameraInfo(is_send=False, CameraKey=CameraKey)
         if not camera_states:
             return False, "请先连接相机"
         socket, context = self.__create_req()
@@ -293,38 +299,73 @@ class SmartShooter(metaclass=SingletonType):
             context.term()
             msg_send = "相机未连接或软件未打开"
             return False, msg_send
-    async def getConfigIso(self):
+
+    def getConfigIso(self, CameraKey=None):
         """获取ISO配置信息"""
         camera_configs = settings.getSysConfigs(
-                "camera_configs",
-                "iso_config",
-                {"low": 100, "high": 6400, "mode": "un_auto"},
-            )
+            "camera_configs",
+            "iso_config",
+            None,
+        )
+        for item in  enumerate(camera_configs):
+            pass
         low_iso = camera_configs.get("low", 100)
         high_iso = camera_configs.get("high", 6400)
         return low_iso, high_iso
-    async def EnableCameraPreview(self, enable_status=True, msg_type="",CameraKey=None):
+
+    def initConfigIsoSettings(self, CameraLists=[]):
+        if not CameraLists:
+            return None
+        """获取ISO配置信息"""
+        camera_configs = settings.getSysConfigs(
+            "camera_configs",
+            "iso_config",
+            None,
+        )
+        temp_A_point = camera_configs.get("A", None)
+        if temp_A_point is not None:
+            print("已配置无需更新")
+            return None
+        points = {"A": {}, "B": {}, "C": {}}
+        for idx,item in enumerate(points):
+            if idx > len(CameraLists) -1:
+                break
+            itemSettings = CameraLists[idx]
+            low_iso = camera_configs.get("low", 100)
+            high_iso = camera_configs.get("high", 6400)
+            points[item] = {**itemSettings, "iso": {"low": low_iso, "high": high_iso}}
+        sys_iso_config = {"key": "camera_configs", "value": {"iso_config": points}}
+        sys_iso_config: SysConfigParams
+        print("首次初始化", sys_iso_config)
+        settings.updateSysConfigs(params=sys_iso_config)
+    async def EnableCameraPreview(
+        self, enable_status=True, msg_type="", CameraKey=None
+    ):
         self.msg_type = msg_type
-        await self.SetCameraFPS(5,CameraKey=CameraKey)
+        await self.SetCameraFPS(5, CameraKey=CameraKey)
         """
         激活相机预览
         """
-        camera_states, _ = await self.GetCameraInfo(is_send=False,CameraKey=CameraKey)
+        camera_states, _ = await self.GetCameraInfo(is_send=False, CameraKey=CameraKey)
         if not camera_states:
             return False, "请先连接相机"
         try:
-            low_iso,high_iso = await self.getConfigIso()
+            low_iso, high_iso = self.getConfigIso(CameraKey=CameraKey)
             print("LOW_ISO", low_iso)
             print("HIGH_ISO", high_iso)
             # 等于auto就不设置
             if enable_status == True:
                 if str(high_iso).lower() != "auto":
-                    await self.setCameraProperty(property="ISO", value=str(high_iso), CameraKey=CameraKey)
+                    await self.setCameraProperty(
+                        property="ISO", value=str(high_iso), CameraKey=CameraKey
+                    )
                 else:
                     print("high_iso 等于auto就不设置")
             if enable_status == False:
                 if str(low_iso).lower() != "auto":
-                    await self.setCameraProperty(property="ISO", value=str(low_iso), CameraKey=CameraKey)
+                    await self.setCameraProperty(
+                        property="ISO", value=str(low_iso), CameraKey=CameraKey
+                    )
                 else:
                     print("low_iso 等于auto就不设置")
             socket, context = self.__create_req()
@@ -356,7 +397,7 @@ class SmartShooter(metaclass=SingletonType):
                 "msg_type": self.msg_type,
                 "device_status": 2,
             }
-            await self.websocket_manager.send_personal_message(message, self.websocket)
+            await self.sendMessageSocket(message)
             return True, "预览启用成功" if enable_status else "预览关闭成功"
         except zmq.Again:
             print("启动预览超时,继续监听...")
@@ -372,7 +413,7 @@ class SmartShooter(metaclass=SingletonType):
                 "msg_type": self.msg_type,
                 "device_status": -1,
             }
-            await self.websocket_manager.send_personal_message(message, self.websocket)
+            await self.sendMessageSocket(message)
             return False, "相机未连接或软件未打开"
 
     async def CameraAutofocus(self, CameraKey=None):
@@ -409,18 +450,24 @@ class SmartShooter(metaclass=SingletonType):
             context.term()
             return False, "相机未连接或软件未打开"
 
-    async def CameraShooter(self, msg_type="", goods_art_no="", id=0, is_af=False,delay=0.5, CameraKey=None):
+    async def CameraShooter(
+        self, msg_type="", goods_art_no="", id=0, is_af=False, delay=0.5, CameraKey=None
+    ):
         # 这里延迟一秒钟 等待前置命令完成
         await asyncio.sleep(delay)
         # 对焦混用
         if is_af:
-            await self.EnableCameraPreview(enable_status=True, msg_type=msg_type, CameraKey=CameraKey)
+            await self.EnableCameraPreview(
+                enable_status=True, msg_type=msg_type, CameraKey=CameraKey
+            )
             start_time = time.time()
             await self.CameraAutofocus(CameraKey=CameraKey)
             end_time = time.time()
             elapsed_time = end_time - start_time
             logger.info(f"自动对焦耗时  {elapsed_time:.4f} 秒")
-            await self.EnableCameraPreview(enable_status=False, msg_type=msg_type, CameraKey=CameraKey)
+            await self.EnableCameraPreview(
+                enable_status=False, msg_type=msg_type, CameraKey=CameraKey
+            )
         self.msg_type = msg_type
         print("camera_states", msg_type)
         """
@@ -432,11 +479,13 @@ class SmartShooter(metaclass=SingletonType):
             return False, "请先连接相机"
         socket, context = self.__create_req()
         try:
-            low_iso,high_iso = await self.getConfigIso()
+            low_iso, high_iso = await self.getConfigIso(CameraKey=CameraKey)
             print("LOW_ISO", low_iso)
             print("HIGH_ISO", high_iso)
             if str(low_iso).lower() != "auto":
-                await self.setCameraProperty(property="ISO", value=str(low_iso), CameraKey=CameraKey)
+                await self.setCameraProperty(
+                    property="ISO", value=str(low_iso), CameraKey=CameraKey
+                )
             else:
                 print("low_iso 等于auto就不设置")
             req = {}
@@ -464,9 +513,7 @@ class SmartShooter(metaclass=SingletonType):
                     "msg_type": self.msg_type,
                     "device_status": -1,
                 }
-                await self.websocket_manager.send_personal_message(
-                    message, self.websocket
-                )
+                await self.sendMessageSocket(message)
                 return False, msg_send
             msg_send = "拍照成功"
             message = {
@@ -476,7 +523,7 @@ class SmartShooter(metaclass=SingletonType):
                 "msg_type": self.msg_type,
                 "device_status": 2,
             }
-            await self.websocket_manager.send_personal_message(message, self.websocket)
+            await self.sendMessageSocket(message)
             return True, "拍照成功"
         except zmq.Again:
             msg_send = "相机未连接或软件未打开"
@@ -487,7 +534,7 @@ class SmartShooter(metaclass=SingletonType):
                 "msg_type": self.msg_type,
                 "device_status": -1,
             }
-            await self.websocket_manager.send_personal_message(message, self.websocket)
+            await self.sendMessageSocket(message)
             return True, "拍照失败"
         except:
             socket.close()
@@ -500,8 +547,9 @@ class SmartShooter(metaclass=SingletonType):
                 "msg_type": self.msg_type,
                 "device_status": -1,
             }
-            await self.websocket_manager.send_personal_message(message, self.websocket)
+            await self.sendMessageSocket(message)
             return False, msg_send
+
     async def asyncMessageListen(self):
         if self.websocket.client_state.name != "CONNECTED":
             print("WebSocket连接已断开,停止发送消息")
@@ -511,9 +559,7 @@ class SmartShooter(metaclass=SingletonType):
             if not message_queue.empty():
                 message = message_queue.get_nowait()
                 print("发送消息中。。。。。", message)
-                await self.websocket_manager.send_personal_message(
-                    message, self.websocket
-                )
+                await self.sendMessageSocket(message)
                 message_queue.task_done()
         except Exception as e:
             # 处理可能的异常,如队列为空等
@@ -538,7 +584,7 @@ class SmartShooter(metaclass=SingletonType):
 
                 # 1. 阻塞接收 ZMQ 消息 (这是唯一的阻塞点,但在子线程,所以没问题)
                 try:
-                    raw = sub_socket.recv() # 这里会阻塞直到有消息或超时
+                    raw = sub_socket.recv()  # 这里会阻塞直到有消息或超时
                     str_msg = raw.decode("utf-8")
                     json_msg = json.loads(str_msg)
 
@@ -546,9 +592,11 @@ class SmartShooter(metaclass=SingletonType):
                         continue
 
                     # 2. 将回调提交到【主事件循环】
-                    if hasattr(self, 'main_loop') and self.main_loop:
+                    if hasattr(self, "main_loop") and self.main_loop:
                         # 非阻塞提交,立即返回
-                        asyncio.run_coroutine_threadsafe(self.callback_listen(json_msg), self.main_loop)
+                        asyncio.run_coroutine_threadsafe(
+                            self.callback_listen(json_msg), self.main_loop
+                        )
                     else:
                         print("Error: main_loop not set in SmartShooter")
 

+ 2 - 0
python/model/device_config.py

@@ -24,6 +24,8 @@ class DeviceConfig(SQLModel, table=True):
     after_delay: Optional[float] = Field(
         default=None, description="拍照后延迟;步长1;最小0;最大99"
     )
+    point_name: Optional[str] = Field(default="A", description="点位名称,默认A点")
+    is_move_device: Optional[bool] = Field(default=True, description="是否移动设备,默认移动设备")
     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="没用;")

+ 14 - 12
python/models.py

@@ -51,6 +51,10 @@ class SaveDeviceConfig(BaseModel):
     led_switch: Optional[bool] = Field(default=None)
     is_wait: Optional[bool] = Field(default=False)
     is_need_confirm: Optional[bool] = Field(default=False)
+    point_name: Optional[str] = Field(default="A", description="点位名称,默认A点")
+    is_move_device: Optional[bool] = Field(
+        default=True, description="是否移动设备,默认移动设备"
+    )
 
 
 class PhotoRecordDelete(BaseModel):
@@ -147,36 +151,34 @@ class SyncLocalConfigs(BaseModel):
 
     token: str = Field(default=None, description="用户token")
     env: str = Field(default="dev", description="当前环境")
-    
+
 
 class GenerateImageJson(BaseModel):
     """货号json数据生成"""
 
     goods_art_no: str = Field(default=None, description="货号")
-    
-    
+
+
 class PhotoRecordRemoveBackground(BaseModel):
     """获取可执行程序命令列表"""
 
     goods_art_nos: list[str] = Field(default=None, description="货号数组")
     token: str = Field(default=None, description="用户token")
-    
-    
+
+
 class SyncPhotoRecord(BaseModel):
     """同步图片记录"""
 
     token: str = Field(default=None, description="用户token")
     env: str = Field(default="dev", description="当前环境")
-    
-    
+
+
 class RenameShadow(BaseModel):
     """重命名阴影文件"""
     goods_art_nos: list[str] = Field(default=None, description="货号数组")
-    
-    
-    
-    
+
+
 class ImportDirs(BaseModel):
     """重命名阴影文件"""
     dir_path: str = Field(default=None, description="货号数组")
-    goods_art_nos:list[str] = Field(default=["BH73323",'BH94727'], description="货号数组")
+    goods_art_nos:list[str] = Field(default=["BH73323",'BH94727'], description="货号数组")

+ 11 - 4
python/settings.py

@@ -75,7 +75,16 @@ def getSysConfigs(key, item, default=None):
         if default_format == "" or default_format == None:
             return "png"
     return config.get(item, default)
-
+def updateSysConfigs(params):
+    session = SqlQuery()
+    sysConfig = CRUD(SysConfigs)
+    # 如果value是字典或列表,需要转换为JSON字符串
+    if "value" in params and isinstance(params["value"], (dict, list)):
+        params["value"] = json.dumps(params["value"], ensure_ascii=False)
+    # 走编辑逻辑
+    # kws = params.__dict__
+    sysConfig.updateConditions(session, conditions={"key": params["key"]}, **params)
+    session.close()
 
 def get_dict_value(_dict, key, default=None):
     if key in _dict:
@@ -376,8 +385,6 @@ print("SCAN_DIR",SCAN_DIR)
 CUSTOMER_TEMPLATE_URL = config.get("customer_template", "template_url")
 
 
-
-
 def hex_to_rgb(hex_color):
     """
     将十六进制颜色值转换为RGB颜色值
@@ -413,4 +420,4 @@ def rgb_to_hex(r, g, b):
     if not all(0 <= val <= 255 for val in [r, g, b]):
         return "#FFFFFF"
     
-    return "#{:02X}{:02X}{:02X}".format(r, g, b)
+    return "#{:02X}{:02X}{:02X}".format(r, g, b)

+ 9 - 3
python/sockets/message_handler.py

@@ -296,7 +296,7 @@ async def handlerSend(
             crud = CRUD(PhotoRecord)
             record = crud.read(session=session, order_by="id", ascending=False,conditions={"delete_time": None})
             if record == None:
-            # 发送失败消息
+                # 发送失败消息
                 data = manager.jsonMessage(
                             code=1,
                             msg="单拍失败,请先输入货号或扫码进行组合拍摄",
@@ -433,9 +433,12 @@ async def handlerSend(
             启动相机或关闭实时预览
             """
             value = data.get("value", True)
+            CameraKey = data.get("CameraKey", None)
             loop.create_task(
                 smart_shooter.EnableCameraPreview(
-                    enable_status=value, msg_type="smart_shooter_enable_preview"
+                    enable_status=value, 
+                    msg_type="smart_shooter_enable_preview",
+                    CameraKey=CameraKey
                 ),
                 name="smart_shooter_enable_preview",
             )
@@ -443,9 +446,10 @@ async def handlerSend(
             """
             启动相机或关闭实时预览
             """
+            CameraKey = data.get("CameraKey", None)
             msg_type = "smart_shooter_get_camera_property"
             code = 0
-            status, info = await smart_shooter.GetCameraProperty()
+            status, info = await smart_shooter.GetCameraProperty(CameraKey=CameraKey)
             code = 1 if status == False else 0
             msg = info if status == False else "操作成功"
             data = info if status == True else {}
@@ -460,6 +464,7 @@ async def handlerSend(
             """
             获取相机信息,是否连接
             """
+            CameraKey = data.get("CameraKey", None)
             device_ctrl = DeviceControl(
                 websocket_manager=manager, smart_shooter=smart_shooter
             )
@@ -488,6 +493,7 @@ async def handlerSend(
                     id=id,
                     goods_art_no=goods_art_no,
                     is_af=is_af,
+                    CameraKey=CameraKey
                 ),
                 name="smart_shooter_photo_take",
             )

+ 23 - 1
python/temp.py

@@ -23,4 +23,26 @@ import zmq,json
 # req["CameraKey"] = "Canon Inc.|Canon EOS 650D|12"
 # json_msg = __send_tcp_message(req_socket,req)
 # cameraInfo = json_msg.get("CameraInfo")
-# print("cameraInfo",json_msg)
+# print("cameraInfo",json_msg)
+import zmq, json
+import asyncio, settings
+
+# # ... existing code ...
+# from mcu.capture.smart_shooter_class import SmartShooter
+
+
+# async def main():
+#     sm = SmartShooter(None)
+#     await sm.GetCameraInfo(CameraKey='Canon Inc.|Canon EOS 650D|12')
+
+
+# if __name__ == "__main__":
+#     asyncio.run(main())
+
+camera_configs = settings.getSysConfigs(
+            "camera_configs",
+            "iso_config",
+            None,
+        )
+for idx, item in enumerate(camera_configs):
+    print(camera_configs[item])