Przeglądaj źródła

feat(mcu): 实现相机高度偏差补偿功能

- 添加 camera_high_motor_deviation_offset 属性用于偏差补偿
- 修改 get_device_info 方法实现相机高度计算时应用偏差补偿
- 更新 getDeviationInfo 方法从异步改为同步以优化性能
- 在 MCU 初始化时自动获取偏差信息并应用补偿
- 优化设备运动状态检测逻辑增加双重验证机制
- 修复单拍功能中的设备配置ID传递问题
- 调整代码格式化和调试信息输出优化
rambo 4 dni temu
rodzic
commit
022ca03e6e

+ 102 - 151
python/mcu/DeviceControl.py

@@ -23,6 +23,7 @@ import logging
 from mcu.capture.smart_shooter_class import SmartShooter
 import logging
 from conifg_info import ConfigManager
+
 logger = logging.getLogger(__name__)
 
 
@@ -31,11 +32,12 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
     lock = threading.Lock()
 
     def __init__(
-        self, websocket_manager: ConnectionManager, smart_shooter: SmartShooter = None
+            self, websocket_manager: ConnectionManager, smart_shooter: SmartShooter = None
     ):
         super().__init__(
             websocket_manager=websocket_manager, smart_shooter=smart_shooter
         )
+        self.camera_high_motor_deviation_offset = 0
         self.camera_height = 400
         self.config_manager = None
         self.msg_type = "mcu"
@@ -103,16 +105,16 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
         }
         self.last_move_time = time.time()
         self.device_name_dict_mapping = {
-            0:"相机角度",
-            1:"相机高度",
-            2:"转盘角度",
-            3:"翻板角度",
-            4:"激光灯位置",
-            5:"蜂鸣器",
-            6:"split",
-            7:"转盘位置",
-            8:"播放音频",
-            99:"mcu命令",
+            0: "相机角度",
+            1: "相机高度",
+            2: "转盘角度",
+            3: "翻板角度",
+            4: "激光灯位置",
+            5: "蜂鸣器",
+            6: "split",
+            7: "转盘位置",
+            8: "播放音频",
+            99: "mcu命令",
         }
         # 最近的mcu基础信息,用于获取数据状态检查
         self.last_mcu_info_data = {
@@ -127,7 +129,6 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             "data": {},
         }
 
-
         # self.window = window
         self.last_push_time = defaultdict(float)
         self.is_running = False
@@ -153,6 +154,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             126: self.get_all_registers_list_by_usb,  # 获取所有寄存器
             150: self.dynamic_parameter_issuance,  # 动态参数下发
         }
+
     def get_device_info(self):
         if not self.init_state:
             self.sendSocketMessage(code=1, msg="mcu设备未初始化", device_status=4)
@@ -165,27 +167,31 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
         if not data:
             return False
         return_data = self.analysis_data(data[1:])
+        print("return_data", return_data)
+        print("camera_high_motor_deviation_offset", self.camera_high_motor_deviation_offset)
         if return_data:
-            camera_height = return_data.get('value',35)
-            self.camera_height = camera_height
+            camera_height = return_data.get('value', 35)
+            self.camera_height = camera_height - self.camera_high_motor_deviation_offset
         else:
-            self.camera_height = 400
+            self.camera_height = 400 - self.camera_high_motor_deviation_offset
         self.msg_type = 'get_device_info'
         self.sendSocketMessage(
             code=0,
             msg="设置mcu其他配置信息完成",
             device_status=2,
-            data={"camera_height":self.camera_height}
+            data={"camera_height": self.camera_height}
         )
         self.msg_type = 'mcu'
         return self.camera_height
+
     # 获取异步数据
     def analysis_data(self, _data):
         _addr = _data[0] << 8 | _data[1]
         if _addr not in self.config_manager.CONFIG_METADATA_BY_ADDR:
             return False
         start = 2
-        _value = _data[start] << 40 | _data[start + 1] << 32 | _data[start + 2] << 24 | _data[start + 3] << 16 | _data[start + 4] << 8 | _data[start + 5]
+        _value = _data[start] << 40 | _data[start + 1] << 32 | _data[start + 2] << 24 | _data[start + 3] << 16 | _data[
+            start + 4] << 8 | _data[start + 5]
 
         start = start + 5
         _read_only = True if _data[start + 1] == 1 else False  # 是否只读
@@ -211,7 +217,8 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                        "value": _value}
         return return_data
         # 跳过异步,直接查询某个数据信息
-    def get_basic_info_mcu_without_async(self, data,fiddler_cmd=0):
+
+    def get_basic_info_mcu_without_async(self, data, fiddler_cmd=0):
         """
         fiddler_cmd :只接收指定的命令内容
         """
@@ -223,11 +230,12 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             r_data = self.get_basic_info_mcu()
             print("264----------r_data:", r_data)
         except BaseException as e:
-            print("302---e",e)
+            print("302---e", e)
             r_data = []
         #
         # self.async_lock.release()
         return r_data
+
     def dynamic_parameter_issuance(self, receive_data):
         print("dynamic_parameter_issuance   receive_data", receive_data)
         func_code, status_code, out_par_data_list = dynamic_parameter_issuance_get(
@@ -358,38 +366,6 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
         self.line_control.connect_state = True
         self.line_control.serial_ins = serial_handle
         await self.line_control.run()
-        # if self.init_state == True:
-        #     print("已经初始化过,请勿重复初始化")
-        #     self.sendSocketMessage(msg="设备初始化完成", device_status=2)
-        #     return False
-        # self.serial_ins.clearn_flush()
-        # self.to_init_device_origin_point(device_name="mcu", is_force=is_force)
-        # print("MCU 开始循环~")
-        # while 1:
-        #     await asyncio.sleep(0.01)
-        #     if not self.serial_ins or not self.connect_state:
-        #         break
-        #     try:
-        #         # print("mcu   send_cmd")
-        #         self.send_cmd()
-        #         # time.sleep(0.01)
-        #         self.get_basic_info_mcu()
-        #         # self.close_other_window()
-        #     except BaseException as e:
-        #         print("121231298908", e)
-        #         break
-
-        # self.is_running = False
-        # self.connect_state = False
-        # print("MCU 循环退出~")
-        # # self.sign_data.emit(
-        # #     {"_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, device_status=-1
-        # )
-        # self.close_connect()
 
     def stop_mcu(self):
         buf = [self.command["stop_mcu"]]
@@ -421,14 +397,14 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             return [0xFF & data >> 24, 0xFF & data >> 16, 0xFF & data >> 8, 0xFF & data]
 
     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),
@@ -452,8 +428,8 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             )
             self.add_send_data_queue(buf)
 
-    async def getDeviationInfo(self):
-        await asyncio.sleep(0.01)
+    def getDeviationInfo(self):
+        # await asyncio.sleep(0.01)
         try:
             # 发送获取偏移量
             data = [self.command["get_deviation"], 1]
@@ -586,26 +562,27 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             print("117 error {}".format(e))
             logger.info(f"117 error %{e}")
         return
+
     def print_mcu_noraml_data(self, receive_data):
         # 扫码数据
         print("接收到255数据:", receive_data)
         try:
             command_mapping = {
-                1:"设备运动",
-                2:"初始化设备",  # 初始化设备
-                3:"处理其他设备",  # 处理其他设备
-                29:"获取所有信息",  # 获取所有信息
-                40:"设置偏移量",  # 设置偏移量
-                41:"读取偏移量",  # 读取偏移量
-                91:"信号转发处理",  # 信号转发处理
-                92:"信号转发返回",  # 信号转发返回
-                44:"获取其他信息",  # 获取其他信息
-                43:"RGB灯的处理与通讯",  ## RGB灯的处理与通讯
-                45:"设置其他信息",  # 设置其他信息
-                47:"查询遥控器电量",  # 查询遥控器电量
-                48:"设置转盘通讯方式 1、串口、2、无线、3 混合",  # 
-                93:"停止运行mcu",  # 停止运行mcu
-                90:"连接MCU",# 连接MCU
+                1: "设备运动",
+                2: "初始化设备",  # 初始化设备
+                3: "处理其他设备",  # 处理其他设备
+                29: "获取所有信息",  # 获取所有信息
+                40: "设置偏移量",  # 设置偏移量
+                41: "读取偏移量",  # 读取偏移量
+                91: "信号转发处理",  # 信号转发处理
+                92: "信号转发返回",  # 信号转发返回
+                44: "获取其他信息",  # 获取其他信息
+                43: "RGB灯的处理与通讯",  ## RGB灯的处理与通讯
+                45: "设置其他信息",  # 设置其他信息
+                47: "查询遥控器电量",  # 查询遥控器电量
+                48: "设置转盘通讯方式 1、串口、2、无线、3 混合",  #
+                93: "停止运行mcu",  # 停止运行mcu
+                90: "连接MCU",  # 连接MCU
             }
             # command = int(receive_data[0])
             command = int(receive_data[1])
@@ -613,11 +590,11 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             # receive_data_temp = receive_data[2:]
             receive_data_temp_text = " ".join([hex(x) for x in receive_data])
             # print("255  command_text:", command_text)
-            if command_text in ["设备运动","处理其他设备"]:
+            if command_text in ["设备运动", "处理其他设备"]:
                 device_id = int(receive_data[2])
                 device_value = int(receive_data[3])
                 device_name_info = self.device_name_dict_mapping[device_id]
-                message_info = {"设备名称":device_name_info,"运动值":device_value}
+                message_info = {"设备名称": device_name_info, "运动值": device_value}
                 print("【设备运动】消息回执:", message_info)
                 logger.info(f"设备运动消息回执:{message_info}")
             print("接收设备消息回执:", command_text)
@@ -626,6 +603,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             print(f"255 error {e}")
             logger.info(f"255 error {e}")
         return
+
     def get_from_mcu_move_respond_data(self, receive_data):
         self.last_from_mcu_move_respond_data = receive_data
 
@@ -722,7 +700,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
         if not receive_data:
             return False
         command = receive_data[0]
-        print("get_basic_info_mcu",command)
+        print("get_basic_info_mcu", command)
         if command in self.deal_code_func_dict:
             _data = ' '.join([hex(x) for x in receive_data])
             return self.deal_code_func_dict[command](receive_data)
@@ -782,8 +760,8 @@ class DeviceControl(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
@@ -808,35 +786,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
 
             overturn_steering_up_speed = receive_data[16]
             overturn_steering_down_speed = receive_data[17]
-
-            # self.sign_data.emit(
-            #     {
-            #         "_type": "get_deviation_data",
-            #         "plugins_mode": "mcu",
-            #         "data": {
-            #             "camera_high_motor_deviation": camera_high_motor_deviation,
-            #             "camera_steering_deviation": camera_steering_deviation,
-            #             "turntable_steering_deviation": turntable_steering_deviation,
-            #             "overturn_steering_middle": overturn_steering_middle,
-            #             "overturn_steering_high": overturn_steering_high,
-            #             "overturn_steering_up_speed": overturn_steering_up_speed,
-            #             "overturn_steering_down_speed": overturn_steering_down_speed,
-            #         },
-            #     }
-            # )
-            # message = {
-            #     "_type": "get_deviation_data",
-            #     "plugins_mode": "mcu",
-            #     "data": {
-            #         "camera_high_motor_deviation": camera_high_motor_deviation,
-            #         "camera_steering_deviation": camera_steering_deviation,
-            #         "turntable_steering_deviation": turntable_steering_deviation,
-            #         "overturn_steering_middle": overturn_steering_middle,
-            #         "overturn_steering_high": overturn_steering_high,
-            #         "overturn_steering_up_speed": overturn_steering_up_speed,
-            #         "overturn_steering_down_speed": overturn_steering_down_speed,
-            #     },
-            # }
+            self.camera_high_motor_deviation_offset = camera_high_motor_deviation
             get_deviation_data = {
                 "camera_high_motor_deviation": camera_high_motor_deviation,
                 "camera_steering_deviation": camera_steering_deviation,
@@ -851,7 +801,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             self.msg_type = "get_deviation_data"
             self.sendSocketMessage(msg="接收偏移量信息", data=get_deviation_data)
             self.msg_type = "mcu"
-            print("接收偏移量信息")
+            print("接收偏移量信息", get_deviation_data)
             logger.info("接收偏移量信息")
         return
 
@@ -965,7 +915,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
         self.msg_type = "mcu"
 
     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:
@@ -977,10 +927,10 @@ class DeviceControl(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
@@ -1060,13 +1010,13 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             }
             # if self.state_camera_motor
             if all(
-                value == 2
-                for value in [
-                    self.state_camera_motor,
-                    self.state_camera_steering,
-                    self.state_turntable_steering,
-                    self.state_overturn_steering,
-                ]
+                    value == 2
+                    for value in [
+                        self.state_camera_motor,
+                        self.state_camera_steering,
+                        self.state_turntable_steering,
+                        self.state_overturn_steering,
+                    ]
             ):
                 self.init_state = True
                 self.sendSocketMessage(msg="设备初始化完成", device_status=2)
@@ -1418,21 +1368,21 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
         else:
             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:
@@ -1467,16 +1417,16 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
         return True
 
     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,
-        times=2, is_response=False
+            self,
+            device_name,
+            value=0,
+            max_speed=None,
+            up_speed=None,
+            down_speed=None,
+            _is_debug=0,
+            is_relative=0,
+            is_deviation=1,
+            times=2, is_response=False
     ):
         """
         此处输入单位为 毫米,以及度  需要先缩小,再放大
@@ -1513,7 +1463,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                     else down_speed
                 )
                 value = value / 10  # value 单位毫米
-                max_camera_hight = self.camera_height/10
+                max_camera_hight = self.camera_height / 10
                 # print("高度位置",max_camera_hight)
                 if value > max_camera_hight:
                     value = max_camera_hight
@@ -1725,7 +1675,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             return False
 
     async def run_mcu_config(
-        self, config_list, goods_art_no, action_info, smart_shooter
+            self, config_list, goods_art_no, action_info, smart_shooter
     ):
         if self.checkDevice() == False:
             return
@@ -1860,14 +1810,15 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
         # await smart_shooter.EnableCameraPreview(
         #             enable_status=False, msg_type="smart_shooter_enable_preview"
         #         )
+
     async def run_mcu_config_single(
-        self,
-        config_info,
-        goods_art_no,
-        msg_type="run_mcu_single_finish",
-        image_index=-1,
-        smart_shooter=None,
-        action_id=-1,
+            self,
+            config_info,
+            goods_art_no,
+            msg_type="run_mcu_single_finish",
+            image_index=-1,
+            smart_shooter=None,
+            action_id=-1,
     ):
         """独立拍照  仅作测试用"""
         await asyncio.sleep(0.01)

+ 2 - 1
python/mcu/McuDeviationSet.py

@@ -236,7 +236,8 @@ class McuDeviationSet:
         self.mcu.to_init_device_origin_point(device_name="mcu", is_force=True)
 
     def get_mcu_deviation(self):
-        asyncio.run(self.mcu.getDeviationInfo())
+        self.mcu.getDeviationInfo()
+
     def get_mcu_deviation_info(self, data):
         if "_type" not in data:
             return

+ 30 - 22
python/mcu/ProgramItem.py

@@ -188,10 +188,22 @@ class ProgramItem(BaseClass):
         print("\033[1;31m执行结束\033[0m", self.mcu.action_state)
         # await asyncio.sleep(0.1)
         # self.mcu.to_get_mcu_base_info()
+    async def _do_camera_check(self):
+        """执行一次完整的状态查询检测,返回是否全部停止"""
+        self.mcu.send_get_all_info_to_mcu()
+        await asyncio.sleep(0.5)
+        return all(
+            value == 2
+            for value in [
+                self.mcu.state_camera_motor,
+                self.mcu.state_camera_steering,
+                self.mcu.state_turntable_steering,
+                self.mcu.state_overturn_steering,
+            ]
+        )
+
     async def camera_check_mcu_move_is_stop(self, re_check=False):
         self.error_info_text = ""
-        # 发送基础数据信息
-        # self.mcu.to_get_mcu_base_info()
         _s = time.time()
         check_times = 0
         await self.mcu.cleanAllReceiveData()
@@ -199,27 +211,22 @@ class ProgramItem(BaseClass):
             if self.mcu.action_state != 1:
                 return False
             # 发送获取设备状态消息
-            self.mcu.send_get_all_info_to_mcu()
-            await asyncio.sleep(0.5)
-            if all(
-                    value == 2
-                    for value in [
-                        self.mcu.state_camera_motor,
-                        self.mcu.state_camera_steering,
-                        self.mcu.state_turntable_steering,
-                        self.mcu.state_overturn_steering,
-                    ]
-                ):
-                logger.info("拍照前运动检测状态[成功]")
-                await asyncio.sleep(1)
-                return True
+            if await self._do_camera_check():
+                # 首次检测通过,追加一次确认检测,防止读到旧状态误判
+                logger.info("拍照前运动检测首次通过,发起确认检测")
+                if await self._do_camera_check():
+                    logger.info("拍照前运动检测状态[成功],确认通过,耗时%.1f秒", time.time() - _s)
+                    await asyncio.sleep(0.5)
+                    return True
+                else:
+                    logger.warning("拍照前运动检测确认失败,继续等待")
+                    check_times += 1
             else:
                 check_times += 1
                 if check_times > 5:
-                    logger.info("拍照前运动检测状态[失败]")
+                    logger.info("拍照前运动检测状态[失败],耗时%.1f秒", time.time() - _s)
                     return False
-                # return True
-        print("\033[1;31m执行结束\033[0m", self.mcu.action_state)
+            await asyncio.sleep(0.2)
     async def run(self, total_len=5, *args):
         if total_len == 1:
             self.mode_type = "其他配置"
@@ -393,9 +400,10 @@ class ProgramItem(BaseClass):
             #  配置设置是否运动设备,不运动直接去拍照
             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
+                    logger.warning("拍照前运动检测失败===>,等待0.5秒后再检测一次")
+                    await asyncio.sleep(0.5)
+                    if not await self.camera_check_mcu_move_is_stop(re_check=True):
+                        logger.warning("拍照前运动检测二次失败===>,强制拍摄")
             is_af = True if self.af_times > 0 else False
             if self.smart_shooter != None:
                 # 拍照

+ 3 - 3
python/mcu/RemoteControlV2.py

@@ -234,7 +234,7 @@ class RemoteControlV2(BaseClass):
         self.msg_type = "blue_tooth"
         self.photo_take_state = 2
 
-    async def handlerTakePhoto(self, smart_shooter=None,session=None,record=None,PointName="A"):
+    async def handlerTakePhoto(self, smart_shooter=None,session=None,record=None,PointName="A",action_id=None):
         """处理单独拍照"""
         await asyncio.sleep(0.1)
         print("开始单拍1")
@@ -251,7 +251,7 @@ class RemoteControlV2(BaseClass):
         self.photo_take_state = 1
         deviceConfig = CRUD(DeviceConfig)
         deviceConfigData = deviceConfig.read(
-            session=session, conditions={"id": record.action_id}
+            session=session, conditions={"id": action_id}
         )
         if deviceConfigData == None:
             self.msg_type = "photo_take"
@@ -269,7 +269,7 @@ class RemoteControlV2(BaseClass):
             record.image_deal_mode,
             record.goods_art_no,
             image_index,
-            record.action_id,
+            action_id,
         )
         session.close()
         print("开始单拍1-插入数据")

+ 92 - 71
python/sockets/message_handler.py

@@ -18,19 +18,22 @@ from concurrent.futures import ThreadPoolExecutor
 from functools import partial
 from logger import logger
 import stat
+
 # 创建全局线程池
 executor = ThreadPoolExecutor(max_workers=4)
+
+
 async def handlerCutOut(
-    manager=None, run_main=None, config_data={}, websocket=None, msg_type=""
+        manager=None, run_main=None, config_data={}, websocket=None, msg_type=""
 ):
     max_retry_count = 1  # 最多重试1次
     retry_count = 0
-    
+
     while retry_count <= max_retry_count:
         try:
             if retry_count > 0:
                 logger.info(f"抠图操作重试第{retry_count}次")
-            
+
             # return_data = run_main.check_before_cutout(config_data)
             # await run_main.check_for_cutout_image_first_call_back(return_data)
             # 将阻塞操作放到线程池中执行
@@ -49,7 +52,7 @@ async def handlerCutOut(
             logger.error(f"抠图操作发生UnicornException: {e.msg}")
             logger.error(f"异常类型: UnicornException")
             logger.error(f"当前重试次数: {retry_count}/{max_retry_count}")
-            
+
             # data = manager.jsonMessage(
             #     code=1,
             #     msg=e.msg,
@@ -62,7 +65,7 @@ async def handlerCutOut(
             logger.error(f"抠图操作发生FileNotFoundError: {error_msg}")
             logger.error(f"异常类型: FileNotFoundError")
             logger.error(f"当前重试次数: {retry_count}/{max_retry_count}")
-            
+
             # 检查是否是原始图缺失错误
             if "原始图" in error_msg and retry_count < max_retry_count:
                 logger.info("检测到原始图缺失,尝试重新拷贝原图并重试...")
@@ -70,7 +73,7 @@ async def handlerCutOut(
                     # 重新拷贝原图
                     goods_art_nos = config_data.get("goods_art_nos", [])
                     image_dir = config_data.get("image_dir", "")
-                    
+
                     for goods_art_no in goods_art_nos:
                         dealImage = DealImage(image_dir)
                         resFlag, path = dealImage.dealMoveImageV2(
@@ -79,7 +82,7 @@ async def handlerCutOut(
                         if not resFlag:
                             logger.error(f"重新拷贝原图失败: {goods_art_no}")
                             raise UnicornException(f"重新拷贝原图失败: {goods_art_no}")
-                    
+
                     logger.info("原图重新拷贝成功,准备重试抠图操作")
                     retry_count += 1
                     continue  # 重试
@@ -93,12 +96,12 @@ async def handlerCutOut(
             import traceback
             error_msg = str(e)
             stack_trace = traceback.format_exc()
-            
+
             logger.error(f"抠图操作发生未知异常: {error_msg}")
             logger.error(f"异常类型: {type(e).__name__}")
             logger.error(f"当前重试次数: {retry_count}/{max_retry_count}")
             logger.error(f"完整堆栈跟踪:\n{stack_trace}")
-            
+
             # 如果是第一次执行且未达到最大重试次数,则重试
             if retry_count < max_retry_count:
                 logger.info("准备重试抠图操作...")
@@ -106,12 +109,13 @@ async def handlerCutOut(
                 continue
             return
 
+
 def handlerFolderDelete(limit_path, goods_art_no_arrays, is_write_txt_log):
     check_path(limit_path)
     move_folder_array = check_move_goods_art_no_folder(
         "output", goods_art_no_arrays, limit_path
     )
-    
+
     for goods_art_revice in goods_art_no_arrays:
         cutout_goods = f"{limit_path}/{goods_art_revice}"
         if os.path.exists(cutout_goods):
@@ -133,7 +137,8 @@ def handlerFolderDelete(limit_path, goods_art_no_arrays, is_write_txt_log):
                 except (PermissionError) as e:
                     retry_count -= 1
                     if retry_count == 0:
-                        logger.info(f"抠图前目录删除出现问题-PermissionError:{str(e)};{goods_art_revice};{cutout_goods}")
+                        logger.info(
+                            f"抠图前目录删除出现问题-PermissionError:{str(e)};{goods_art_revice};{cutout_goods}")
                         if is_write_txt_log:
                             error_file_path = f"{cutout_goods}/异常说明-出现目录丢失或缺少图片请点开查看原因.txt"
                             with open(error_file_path, 'w', encoding='utf-8') as f:
@@ -163,27 +168,31 @@ def handlerFolderDelete(limit_path, goods_art_no_arrays, is_write_txt_log):
                     else:
                         logger.info(f"抠图前目录删除出现问题--Exception:{str(e)};{retry_count}")
                         time.sleep(0.5)  # 等待0.5秒后重试
-    
+
     return move_folder_array
+
+
 def validate_goods_art_no(goods_art_no):
-        """
-        验证货号输入是否包含特殊字符
-        """
-        import re
-        # 定义不允许的特殊字符,主要是文件系统中可能导致路径问题的字符
-        invalid_chars = r'[<>:"/\\|?*]'
-        if re.search(invalid_chars, goods_art_no):
-            # 找出所有非法字符
-            invalid_found = re.findall(invalid_chars, goods_art_no)
-            invalid_str = ', '.join(set(invalid_found))
-            return False,f"货号包含非法字符: {invalid_str},请修改后再提交"
-        return True,""
+    """
+    验证货号输入是否包含特殊字符
+    """
+    import re
+    # 定义不允许的特殊字符,主要是文件系统中可能导致路径问题的字符
+    invalid_chars = r'[<>:"/\\|?*]'
+    if re.search(invalid_chars, goods_art_no):
+        # 找出所有非法字符
+        invalid_found = re.findall(invalid_chars, goods_art_no)
+        invalid_str = ', '.join(set(invalid_found))
+        return False, f"货号包含非法字符: {invalid_str},请修改后再提交"
+    return True, ""
+
+
 # socket消息发送逻辑处理方法
 async def handlerSend(
-    manager: ConnectionManager,
-    receiveData: str,
-    websocket: WebSocket,
-    smart_shooter: SmartShooter,
+        manager: ConnectionManager,
+        receiveData: str,
+        websocket: WebSocket,
+        smart_shooter: SmartShooter,
 ):
     loop = asyncio.get_event_loop()
     receiveData = json.loads(receiveData)
@@ -245,7 +254,7 @@ async def handlerSend(
             device_name = data.get("device_name")
             value = data.get("value")
             if (device_name == "" or device_name == None) or (
-                value == "" or value == None
+                    value == "" or value == None
             ):
                 data = manager.jsonMessage(code=1, msg="参数错误", msg_type="mcu")
                 await manager.send_personal_message(data, websocket)
@@ -296,7 +305,7 @@ async def handlerSend(
             tab_id = action_configs_json.get(action_flag)
             photoRecord = CRUD(PhotoRecord)
             goods_art_record = photoRecord.read(
-                session, conditions={"goods_art_no": goods_art_no,"delete_time": None}
+                session, conditions={"goods_art_no": goods_art_no, "delete_time": None}
             )
             if goods_art_record != None:
                 data = manager.jsonMessage(
@@ -343,6 +352,8 @@ async def handlerSend(
             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:
@@ -352,40 +363,53 @@ async def handlerSend(
             device_ctrl = DeviceControl(
                 websocket_manager=manager, smart_shooter=smart_shooter
             )
-            print("收到单拍指令",'handler_take_picture')
+            print("收到单拍指令", 'handler_take_picture')
             await device_ctrl.controlDevice("laser_position", 0)
             blue_tooth = BlueToothMode(
                 websocket_manager=manager, smart_shooter=smart_shooter
             )
             session = SqlQuery()
             crud = CRUD(PhotoRecord)
-            record = crud.read(session=session, order_by="id", ascending=False,conditions={"delete_time": None})
+            record = crud.read(session=session, order_by="id", ascending=False, conditions={"delete_time": None})
             if record == None:
                 # 发送失败消息
                 data = manager.jsonMessage(
-                            code=1,
-                            msg="单拍失败,请先输入货号或扫码进行组合拍摄",
-                            msg_type="handler_take_picture",
-                        )
+                    code=1,
+                    msg="单拍失败,请先输入货号或扫码进行组合拍摄",
+                    msg_type="handler_take_picture",
+                )
                 await manager.send_personal_message(data, websocket)
                 return
             try:
                 limit_path = "{}/{}".format(settings.OUTPUT_DIR,
-                    time.strftime("%Y-%m-%d", time.localtime(time.time()))
-                )
-                move_folder_array = handlerFolderDelete(limit_path,[record.goods_art_no],False)
+                                            time.strftime("%Y-%m-%d", time.localtime(time.time()))
+                                            )
+                move_folder_array = handlerFolderDelete(limit_path, [record.goods_art_no], False)
             except UnicornException as e:
                 data = manager.jsonMessage(
-                        code=1,
-                        msg=e.msg,
-                        msg_type="handler_take_picture",
-                    )
+                    code=1,
+                    msg=e.msg,
+                    msg_type="handler_take_picture",
+                )
                 await manager.send_personal_message(data, websocket)
                 return
+            record_action_id = record.action_id
+            deviceConfig = CRUD(DeviceConfig)
+            deviceConfigSignle = deviceConfig.read(session, conditions={"id": record_action_id})
+            deviceConfigAll = deviceConfig.read_all(session, conditions={"tab_id": deviceConfigSignle.tab_id},
+                                                    order_by="action_index",
+                                                    ascending=True, )
+            lastConfig = deviceConfigAll[-1]
+            if not lastConfig.take_picture:
+                used_config_id = lastConfig.id
+            else:
+                used_config_id = deviceConfigAll[0].id
             loop.create_task(
-                blue_tooth.remote_control_v2.handlerTakePhoto(smart_shooter,session,record,PointName),
+                blue_tooth.remote_control_v2.handlerTakePhoto(smart_shooter, session, record, PointName,
+                                                              used_config_id),
                 name="run_mcu_config",
             )
+            session.close()
             await asyncio.sleep(2.5)
             await device_ctrl.controlDevice("laser_position", 1)
         case "re_take_picture":  # 重拍
@@ -393,7 +417,7 @@ async def handlerSend(
             record_id = data.get("record_id")
             session = SqlQuery()
             photoRecord = CRUD(PhotoRecord)
-            goods_art_record = photoRecord.read(session, conditions={"id": record_id,"delete_time": None})
+            goods_art_record = photoRecord.read(session, conditions={"id": record_id, "delete_time": None})
             if goods_art_record == None:
                 data = manager.jsonMessage(
                     code=1,
@@ -437,10 +461,7 @@ async def handlerSend(
             device_ctrl = DeviceControl(
                 websocket_manager=manager, smart_shooter=smart_shooter
             )
-            loop.create_task(
-                device_ctrl.getDeviationInfo(),
-                name="get_deviation",
-            )
+            device_ctrl.getDeviationInfo()
         case "set_deviation":
             device_ctrl = DeviceControl(
                 websocket_manager=manager, smart_shooter=smart_shooter
@@ -544,9 +565,9 @@ async def handlerSend(
             msg_type = "smart_shooter_auto_focus"
             temp_A_point = camera_configs.get(PointName, None)
             CameraKey = temp_A_point.get("CameraKey", None) if temp_A_point else None
-            status,msg = await  smart_shooter.CameraAutofocus(
-                    CameraKey=CameraKey,
-                )
+            status, msg = await  smart_shooter.CameraAutofocus(
+                CameraKey=CameraKey,
+            )
             if not status:
                 data = manager.jsonMessage(
                     code=1,
@@ -607,15 +628,15 @@ async def handlerSend(
             if goods_art_no:
                 try:
                     limit_path = "{}/{}".format(settings.OUTPUT_DIR,
-                        time.strftime("%Y-%m-%d", time.localtime(time.time()))
-                    )
-                    move_folder_array = handlerFolderDelete(limit_path,[goods_art_no],False)
+                                                time.strftime("%Y-%m-%d", time.localtime(time.time()))
+                                                )
+                    move_folder_array = handlerFolderDelete(limit_path, [goods_art_no], False)
                 except UnicornException as e:
                     data = manager.jsonMessage(
-                            code=1,
-                            msg=e.msg,
-                            msg_type="smart_shooter_photo_take",
-                        )
+                        code=1,
+                        msg=e.msg,
+                        msg_type="smart_shooter_photo_take",
+                    )
                     await manager.send_personal_message(data, websocket)
                     return
             is_af = True
@@ -637,7 +658,7 @@ async def handlerSend(
             goods_art_no = data.get("goods_art_no", "")
             session = SqlQuery()
             photoRecord = CRUD(PhotoRecord)
-            goods_art_record = photoRecord.read(session, conditions={"id": id,"delete_time": None})
+            goods_art_record = photoRecord.read(session, conditions={"id": id, "delete_time": None})
             if goods_art_record == None:
                 data = manager.jsonMessage(
                     code=1,
@@ -648,7 +669,7 @@ async def handlerSend(
                 return
             reset_data = {"image_path": None}
             photoRecord.update(session, id, **reset_data)
-            device_ctrl = DeviceControl(websocket_manager=manager)#
+            device_ctrl = DeviceControl(websocket_manager=manager)  #
             loop.create_task(
                 device_ctrl.only_take_photo(
                     goods_art_no=goods_art_no,
@@ -667,23 +688,23 @@ async def handlerSend(
             run_main = RunMain(obj, token, uuid)
             goods_art_no_arrays = data.get("goods_art_no", [])
             limit_path = "{}/{}".format(settings.OUTPUT_DIR,
-                time.strftime("%Y-%m-%d", time.localtime(time.time()))
-            )
+                                        time.strftime("%Y-%m-%d", time.localtime(time.time()))
+                                        )
             try:
-                move_folder_array = handlerFolderDelete(limit_path,goods_art_no_arrays,True)
+                move_folder_array = handlerFolderDelete(limit_path, goods_art_no_arrays, True)
             except UnicornException as e:
                 data = manager.jsonMessage(
-                        code=1,
-                        msg=e.msg,
-                        msg_type=msg_type,
-                    )
+                    code=1,
+                    msg=e.msg,
+                    msg_type=msg_type,
+                )
                 await manager.send_personal_message(data, websocket)
                 return
             # 该数组表示是否需要后面的移动文件夹操作,减少重复抠图,提升抠图时间和速度
             session = SqlQuery()
             for goods_art_no in goods_art_no_arrays:
                 pr = CRUD(PhotoRecord)
-                images = pr.read_all(session, conditions={"goods_art_no": goods_art_no,"delete_time": None})
+                images = pr.read_all(session, conditions={"goods_art_no": goods_art_no, "delete_time": None})
                 if not images:
                     data = manager.jsonMessage(
                         code=1,
@@ -729,7 +750,7 @@ async def handlerSend(
             cutOutMode = (
                 "1"
                 if settings.getSysConfigs("other_configs", "cutout_mode", "普通抠图")
-                == "普通抠图"
+                   == "普通抠图"
                 else "2"
             )
             config_data = {
@@ -767,7 +788,7 @@ async def handlerSend(
                     websocket=websocket,
                     msg_type=msg_type,
                 ),
-                 name="handlerCutOut",
+                name="handlerCutOut",
             )
         case _:
             data = manager.jsonMessage(code=1, msg="未知消息")

+ 49 - 20
python/sockets/socket_server.py

@@ -14,12 +14,15 @@ import traceback
 import logging
 from utils import common
 from utils.common import message_queue
+
 logger = logging.getLogger(__name__)
 conn_manager = ConnectionManager()
 active_connections = set()
 device_ctrl = DeviceControl(websocket_manager=conn_manager)
 blue_tooth = BlueToothMode(websocket_manager=conn_manager)
 smart_shooter = SmartShooter(websocket_manager=conn_manager)
+
+
 async def updateDataRecord(PhotoFilename, id):
     await asyncio.sleep(0.01)
     create_time = datetime.datetime.fromtimestamp(os.path.getctime(PhotoFilename))
@@ -27,15 +30,19 @@ async def updateDataRecord(PhotoFilename, id):
     # record_model = PhotoRecord(**data)
     session = SqlQuery()
     record_model = CRUD(PhotoRecord)
-    model = record_model.read(session, conditions={"id": id,"delete_time": None})
+    model = record_model.read(session, conditions={"id": id, "delete_time": None})
     if model == None:
         print(f"smart shooter 拍照记录更新失败,记录id:{id},不存在")
+        session.close()
+        return None
     else:
         # 走编辑逻辑
-        settings.syncPhotoRecord(data,action_type=3)
-        record_model.updateConditions(session, conditions={"id": id}, **data)
+        settings.syncPhotoRecord(data, action_type=3)
+        record_data = record_model.updateConditions(session, conditions={"id": id}, **data)
+        session.close()
         print(f"smart shooter 拍照记录更新成功,记录id:{id}")
-    session.close()
+        return record_data
+
 
 @app.websocket("/ws")
 async def websocket_endpoint(websocket: WebSocket):
@@ -52,21 +59,21 @@ async def websocket_endpoint(websocket: WebSocket):
     # 启动 smart_shooter.connect_listen 服务
     listen_task = None
     tasks = set()
-    send_task = None # <--- 新增
+    send_task = None  # <--- 新增
     try:
         # 初始化回调函数
         smart_shooter.callback_listen = MsgCallback
         # 创建任务来并发处理不同类型的消息
         handler_task = asyncio.create_task(handler_messages(websocket))
         # send_task = asyncio.create_task(send_message(websocket))
-        send_task = asyncio.create_task(send_message(websocket)) # <--- 启动消费者
+        send_task = asyncio.create_task(send_message(websocket))  # <--- 启动消费者
         loop = asyncio.get_event_loop()
         listen_task = loop.run_in_executor(None, smart_shooter.connect_listen)
         # send_task = loop.run_in_executor(None, send_message(websocket))
         # 创建任务来启动 connect_listen
         # listen_task = asyncio.create_task(restart_smart_shooter_listener())
         # 等待所有任务完成
-        await asyncio.gather(handler_task,listen_task, return_exceptions=True)
+        await asyncio.gather(handler_task, listen_task, return_exceptions=True)
 
     except WebSocketDisconnect:
         print("Client disconnected")
@@ -75,21 +82,21 @@ async def websocket_endpoint(websocket: WebSocket):
     finally:
         # 确保任务被正确取消和清理
         tasks_to_cancel = []
-        
+
         if handler_task and not handler_task.done():
             tasks_to_cancel.append(handler_task)
-        
+
         if send_task and not send_task.done():
             tasks_to_cancel.append(send_task)
-        
+
         # 取消所有待处理的任务
         for task in tasks_to_cancel:
             task.cancel()
-        
+
         # 等待任务取消完成
         if tasks_to_cancel:
             await asyncio.gather(*tasks_to_cancel, return_exceptions=True)
-        
+
         # 清理连接
         active_connections.discard(websocket)
         print("WebSocket connection cleaned up")
@@ -141,7 +148,7 @@ async def handler_messages(websocket):
                 )
             )
         except Exception as e:
-            print("socket error",e)
+            print("socket error", e)
             break
 
 
@@ -169,17 +176,33 @@ async def send_message(websocket):
         try:
             # 1. 异步等待消息,不会阻塞其他协程
             data = await message_queue.get()
-            
+
             # 2. 发送消息
             if common.websocket_manager:
                 # 假设 broadcast 或 send_personal_message 是异步方法
-                await common.websocket_manager.send_personal_message(data,common.websocket) 
+                await common.websocket_manager.send_personal_message(data, common.websocket)
                 # 或者根据你的 ConnectionManager 实现调用具体发送方法
-            
+
             message_queue.task_done()
         except Exception as e:
             print(f"消息消费错误: {e}")
-            await asyncio.sleep(1) # 防止死循环报错
+            await asyncio.sleep(1)  # 防止死循环报错
+
+
+async def getActionInfo(record_info):
+    await asyncio.sleep(0.01)
+    if not record_info:
+        return None
+    action_id = record_info.action_id
+    if not action_id:
+        return None
+    session = SqlQuery()
+    device_model = CRUD(DeviceConfig)
+    model = device_model.read(session, conditions={"id": action_id})
+    if not model:
+        session.close()
+        return None
+    return model
 
 
 async def MsgCallback(msg):
@@ -190,7 +213,7 @@ async def MsgCallback(msg):
             PhotoLocation = msg.get("PhotoLocation")
             PhotoOrigin = msg.get("PhotoOrigin")
             if (PhotoFilename != "" and PhotoFilename != None) and (
-                PhotoLocation == "Local Disk"
+                    PhotoLocation == "Local Disk"
             ):
                 # temp_photo_name = PhotoFilename
                 # 更新拍照记录
@@ -201,15 +224,20 @@ async def MsgCallback(msg):
                     if PhotoOrigin != "" and PhotoOrigin not in ["external", "ui"]:
                         goods_art_no, id = PhotoOrigin.split(",")
                     # 创建任务来处理数据库更新,避免阻塞回调
-                    await updateDataRecord(PhotoFilename, id)
+                    recordResult = await updateDataRecord(PhotoFilename, id)
                 except Exception as e:
                     print("拍照更新异常", e)
+                    recordResult = None
+                actionModel = None
+                if recordResult:
+                    actionModel = await getActionInfo(recordResult)
                 data = conn_manager.jsonMessage(
                     code=0,
                     msg=f"照片获取成功",
                     data={
                         "photo_file_name": PhotoFilename,
                         "goods_art_no": goods_art_no,
+                        "action_name": actionModel.action_name if actionModel else None,
                     },
                     msg_type="smart_shooter_photo_take",
                 )
@@ -228,6 +256,7 @@ async def MsgCallback(msg):
         # case _:
         #     print("收到未知数据:{}".format(msg))
 
+
 # @app.on_event("startup")
 # async def startup_event():
 #     loop = asyncio.get_event_loop()
@@ -257,7 +286,7 @@ async def shutdown_event():
     if len(diviceList) == 0:
         blue_tooth.bluetooth_exit = True
         blue_tooth.clearMyInstance()
-    diviceAddress = "" if len(list(diviceList.keys()))==0 else list(diviceList.keys())[0]
+    diviceAddress = "" if len(list(diviceList.keys())) == 0 else list(diviceList.keys())[0]
     if diviceAddress != "":
         print(diviceList.get(diviceAddress))
         diviceName = diviceList[diviceAddress]["name"]