Browse Source

Merge branch 'dev-python' of gitlab.pubdata.cn:liangyibo/CameraMachine into dev-python

# Conflicts:
#	python/api.py
zhangyh 8 months ago
parent
commit
2749c110e8

+ 118 - 34
python/api.py

@@ -1,18 +1,19 @@
 from natsort.natsort import order_by_index
+from sqlalchemy import func
 from models import *
 import requests
 import json
 from logger import logger
 from serial.tools import list_ports
-
+from model import PhotoRecord
 from utils.hlm_http_request import forward_request
 from sockets.socket_client import socket_manager
 from mcu.DeviceControl import DeviceControl
 import time
-# from service.base_deal import  BaseDealImage
-from databases import DeviceConfig,SqlQuery,CRUD
-from service.deal_image import DealImage
+from sqlalchemy import and_, asc, desc
 
+from service.deal_image import DealImage
+from databases import DeviceConfig, SqlQuery, CRUD, select
 @app.get("/")
 async def index():
     # await socket_manager.send_message(msg="测试")
@@ -117,33 +118,32 @@ async def handle_detail(request: Request):
     return result
 
 
-
-        #
-        # params = json.dump(request.query_params)
-        # #{'image_dir': 'D:/phpstudy_pro/WWW/auto_photo/output/2024-11-18', 'image_order': '俯视,侧视,后跟,鞋底,内里', 'is_check_number': True, 'resize_image_view': '后跟', 'cutout_mode': '1', 'logo_path': '', 'special_goods_art_no_folder_line': '', 'is_use_excel': True, 'excel_path': '', 'is_check_color_is_all': True, 'assigned_page_dict': {}, 'temp_class': {'huilima-2': <class 'detail_template.huilima.detail_huilima2.DetailPicGet'>, 'huilima-3': <class 'detail_template.huilima.detail_huilima3.DetailPicGet'>, 'huilima-4': <class 'detail_template.huilima.detail_huilima4.DetailPicGet'>, 'huilima-1': <class 'detail_template.huilima.detail_huilima1.DetailPicGet'>}, 'temp_name': 'huilima-2', 'temp_name_list': ['huilima-2', 'huilima-3', 'huilima-4', 'huilima-1'], 'target_error_folder': 'D:/phpstudy_pro/WWW/auto_photo/output/2024-11-18/软件-生成详情错误'}
-        #
-        # config_data = {
-        #     'image_dir': params['image_dir'],
-        #     'image_order': params['image_order'],
-        #     'is_check_number': params['is_check_number'],
-        #     'resize_image_view': params['resize_image_view'],
-        #     'cutout_mode': '1',
-        #     'logo_path': params['logo_path'],
-        #     'special_goods_art_no_folder_line': '',
-        #     'is_use_excel': params['is_use_excel'],
-        #     'excel_path': params['excel_path'],
-        #     'is_check_color_is_all': params['is_check_color_is_all'],
-        #     'assigned_page_dict': {},
-        #     'temp_class': {
-        #         'huilima-2': 'detail_template.huilima.detail_huilima2.DetailPicGet',
-        #         'huilima-3': 'detail_template.huilima.detail_huilima3.DetailPicGet',
-        #         'huilima-4': 'detail_template.huilima.detail_huilima4.DetailPicGet',
-        #         'huilima-1': 'detail_template.huilima.detail_huilima1.DetailPicGet'
-        #     },
-        #     'temp_name': 'huilima-2',
-        #     'temp_name_list': ['huilima-2', 'huilima-3', 'huilima-4', 'huilima-1'],
-        #     'target_error_folder': 'D:/phpstudy_pro/WWW/auto_photo/output/2024-11-18/软件-生成详情错误'
-        # }
+    #
+    # params = json.dump(request.query_params)
+    # #{'image_dir': 'D:/phpstudy_pro/WWW/auto_photo/output/2024-11-18', 'image_order': '俯视,侧视,后跟,鞋底,内里', 'is_check_number': True, 'resize_image_view': '后跟', 'cutout_mode': '1', 'logo_path': '', 'special_goods_art_no_folder_line': '', 'is_use_excel': True, 'excel_path': '', 'is_check_color_is_all': True, 'assigned_page_dict': {}, 'temp_class': {'huilima-2': <class 'detail_template.huilima.detail_huilima2.DetailPicGet'>, 'huilima-3': <class 'detail_template.huilima.detail_huilima3.DetailPicGet'>, 'huilima-4': <class 'detail_template.huilima.detail_huilima4.DetailPicGet'>, 'huilima-1': <class 'detail_template.huilima.detail_huilima1.DetailPicGet'>}, 'temp_name': 'huilima-2', 'temp_name_list': ['huilima-2', 'huilima-3', 'huilima-4', 'huilima-1'], 'target_error_folder': 'D:/phpstudy_pro/WWW/auto_photo/output/2024-11-18/软件-生成详情错误'}
+    #
+    # config_data = {
+    #     'image_dir': params['image_dir'],
+    #     'image_order': params['image_order'],
+    #     'is_check_number': params['is_check_number'],
+    #     'resize_image_view': params['resize_image_view'],
+    #     'cutout_mode': '1',
+    #     'logo_path': params['logo_path'],
+    #     'special_goods_art_no_folder_line': '',
+    #     'is_use_excel': params['is_use_excel'],
+    #     'excel_path': params['excel_path'],
+    #     'is_check_color_is_all': params['is_check_color_is_all'],
+    #     'assigned_page_dict': {},
+    #     'temp_class': {
+    #         'huilima-2': 'detail_template.huilima.detail_huilima2.DetailPicGet',
+    #         'huilima-3': 'detail_template.huilima.detail_huilima3.DetailPicGet',
+    #         'huilima-4': 'detail_template.huilima.detail_huilima4.DetailPicGet',
+    #         'huilima-1': 'detail_template.huilima.detail_huilima1.DetailPicGet'
+    #     },
+    #     'temp_name': 'huilima-2',
+    #     'temp_name_list': ['huilima-2', 'huilima-3', 'huilima-4', 'huilima-1'],
+    #     'target_error_folder': 'D:/phpstudy_pro/WWW/auto_photo/output/2024-11-18/软件-生成详情错误'
+    # }
 
 @app.post("/get_device_configs", description="获取可执行程序命令列表")
 def get_device_configs(params: ModelGetDeviceConfig):
@@ -161,8 +161,10 @@ def get_device_configs(params: ModelGetDeviceConfigDetail):
     action_id = params.id
     session = SqlQuery()
     configModel = CRUD(DeviceConfig)
-    configList = configModel.read(session, obj_id=action_id)
-    return {"code": 0, "msg": "", "data": configList}
+    model = configModel.read(session, conditions={"id": action_id})
+    if model == None:
+        return {"code": 1, "msg": "数据不存在", "data": None}
+    return {"code": 0, "msg": "", "data": model}
 
 
 @app.post("/remove_config", description="删除一条可执行命令")
@@ -170,7 +172,10 @@ def get_device_configs(params: ModelGetDeviceConfigDetail):
     action_id = params.id
     session = SqlQuery()
     configModel = CRUD(DeviceConfig)
-    configList = configModel.delete(session, obj_id=action_id)
+    model = configModel.read(session, conditions={"id": action_id})
+    if model == None:
+        return {"code": 1, "msg": "数据不存在", "data": None}
+    configModel.delete(session, obj_id=action_id)
     return {"code": 0, "msg": "删除成功", "data": None}
 
 
@@ -184,7 +189,86 @@ def save_device_config(params: SaveDeviceConfig):
         params.id = None
         save_device_config = deviceConfig.create(session, obj_in=params)
     else:
+        model = deviceConfig.read(session, conditions={"id": action_id})
+        if model == None:
+            return {"code": 1, "msg": "数据不存在", "data": None}
         # 走编辑逻辑
         kwargs = params.__dict__
         save_device_config = deviceConfig.update(session, obj_id=action_id, **kwargs)
     return {"code": 0, "msg": "操作成功", "data": save_device_config}
+
+
+@app.post("/reset_config", description="创建或修改一条可执行命令")
+def reset_config(params: ModelGetDeviceConfig):
+    mode_type = params.mode_type
+    if  mode_type == None or mode_type == "":
+        return {"code": 1, "msg": "参数错误", "data": None}
+    session = SqlQuery()
+    deviceConfig = CRUD(DeviceConfig)
+    res = deviceConfig.deleteConditions(session, conditions={"mode_type": mode_type})
+    if res is False:
+        return {"code": 1, "msg": "操作失败", "data": None}
+    actions = json.load(open("action.json", encoding="utf-8"))
+    act = []
+    for item in actions:
+        if item.get("mode_type") == mode_type:
+            act.append(item)
+    batch_insert_device_configs(session, act)
+    return {"code": 0, "msg": "操作成功", "data": None}
+
+
+@app.get("/get_photo_records", description="获取拍照记录")
+def get_photo_records(page: int=1,size:int=5):
+
+    session = SqlQuery()
+    photos = CRUD(PhotoRecord)
+    statement = (
+        select(PhotoRecord)
+        .offset((page - 1) * size)
+        .limit(size)
+        .order_by(desc("id"))
+        .group_by("goods_art_no")
+    )
+    list = []
+    result = session.exec(statement).all()
+    for item in result:
+        list_item = photos.read_all(session, conditions={"goods_art_no": item.goods_art_no})
+        list.append(
+            {
+                "goods_art_no": item.goods_art_no,
+                "action_time": item.create_time,
+                "items": list_item,
+            }
+        )
+    return {
+        "code": 0,
+        "msg": "",
+        "data": {"list": list, "page": page, "size": size},
+    }
+
+
+@app.get("/get_photo_record_detail", description="通过货号获取拍照记录详情")
+def get_photo_records(goods_art_no: str = None):
+    if goods_art_no == None: return {"code": 1, "msg": "参数错误", "data": None}
+    session = SqlQuery()
+    photos = CRUD(PhotoRecord)
+    items = photos.read_all(session, conditions={"goods_art_no": goods_art_no})
+    return {
+        "code": 0,
+        "msg": "",
+        "data": {"list": items},
+    }
+
+
+@app.post("/delect_goods_arts", description="通过货号删除记录")
+def delect_goods_arts(params: PhotoRecordDelete):
+
+    session = SqlQuery()
+    photos = CRUD(PhotoRecord)
+    for item in params.goods_art_nos:
+        photos.deleteConditions(session, conditions={"goods_art_no": item})
+    return {
+        "code": 0,
+        "msg": "操作成功",
+        "data": None,
+    }

+ 37 - 0
python/databases.py

@@ -30,6 +30,20 @@ def batch_insert_device_configs(session: Session, data_list: list):
     session.commit()
 
 
+def insert_photo_records(image_deal_mode: int, goods_art_no: str, image_index:int):
+    session = SqlQuery()
+    """批量插入数据到照片记录"""
+    data = {
+        "image_deal_mode": image_deal_mode,
+        "goods_art_no": goods_art_no,
+        "image_index": image_index,
+    }
+    device_config = PhotoRecord(**data)
+    session.add(device_config)
+    session.commit()
+    return True
+
+
 # 创建一个通用的 CRUD 类
 class CRUD:
     def __init__(self, model):
@@ -87,11 +101,34 @@ class CRUD:
         session.refresh(db_obj)
         return db_obj
 
+    def deleteConditions(
+        self,
+        session: Session,
+        conditions: Optional[Dict] = None,
+    ):
+        query = select(self.model)
+        if conditions == None:
+            return False
+        query = query.where(
+            and_(
+                *(
+                    getattr(self.model, key) == value
+                    for key, value in conditions.items()
+                )
+            )
+        )
+        objects_to_delete = session.exec(query).all()
+        for obj in objects_to_delete:
+            session.delete(obj)
+        session.commit()
+        return True
+
     def delete(self, session: Session, obj_id: int):
         db_obj = session.get(self.model, obj_id)
         session.delete(db_obj)
         session.commit()
 
+
 def SqlQuery():
     return next(__get_session())
 

+ 68 - 3
python/docs/socket命令.md

@@ -6,7 +6,7 @@
 * code:为0时代表成功操作
 * msg:为接口执行命令时的提示信息
 * status:-1连接失败  0未连接 1连接中  2链接成功  3端口占用
-* msg_type:mcu为设备控制,blue_tooth为蓝牙控制
+* msg_type:mcu为设备控制,blue_tooth为蓝牙控制,blue_tooth_scan 为蓝牙扫码时发送的消息数据
 * data:为返回的数据信息,无实际交互需求时,前端可忽略
 ```python
 {
@@ -84,6 +84,71 @@ _(该命令用于单独自定义配置中某一项的单独调整测试,不进
     "type": "run_mcu"
 }
 ```
-
-
+### 执行拍摄任务命令发送完成后-服务端响应消息
+* msg_type 为image_process时,代表上述命令已发送给设备执行任务
+* data参数释义:
+    * image_counts : 执行拍照任务的数量,可理解为照片张数
+    * goods_art_no : 货号
+    * current_time : 当前时间
+* msg 消息提示
+#### 响应示例
+```python
+{
+    "msg":"MCU 命令已发送完成",
+    "data": {
+        "image_counts": 4,
+        "goods_art_no": "1234556",
+        "current_time": "年月日时分秒"
+    },
+    "msg_type":"image_process"
+}
+```
+### 单张照片拍照完成后-服务端响应消息
+* msg_type 为photo_take时,代表拍照已完成
+* data参数释义:
+    * image_counts : 执行拍照任务的数量,可理解为照片张数
+    * goods_art_no : 货号
+    * current_time : 当前时间
+* msg 消息提示
+#### 响应示例-拍照成功
+```python
+{
+    "code": 0,
+    "msg": "拍鞋底 执行完成~",
+    "status": 2,
+    "data": null,
+    "msg_type":"photo_take"
+}
+```
+#### 响应示例-拍照成功
+```python
+{
+    "code": 1,
+    "msg": "拍鞋底 执行失败~",
+    "status": 2,
+    "data": null,
+    "msg_type":"photo_take"
+}
+```
+### 接收遥控器扫码货号动作处理
+#### 消息示例
+* data中的子data参数释义:
+    * action:【执行左脚程序】或【执行右脚程序】
+    * goods_art_no :扫描到的货号
+* msg_type:当该字段为blue_tooth_scan时,前端可直接解析得到data数据包,将数据直接发送给socket服务端即可【执行拍摄任务】
+```python
+{
+    "code": 0,
+    "msg": "准备执行[左脚程序]",
+    "status": 2,
+    "data": {
+            "data": {
+                    "action": "执行左脚程序",
+                    "goods_art_no": "货号信息",
+                    },
+                    "type": "run_mcu",
+    },
+    "msg_type": "blue_tooth_scan"
+}
+```
 ### 未完待续.....

+ 60 - 15
python/mcu/DeviceControl.py

@@ -1,4 +1,5 @@
 import asyncio
+import datetime
 import serial.tools.list_ports
 import time, json
 from .SerialIns import SerialIns
@@ -10,6 +11,7 @@ import threading
 import settings
 from .ProgramItem import ProgramItem
 from .capture.module_digicam import DigiCam
+from databases import insert_photo_records
 
 # mcu命令
 class DeviceControl(BaseClass, metaclass=SingletonType):
@@ -20,7 +22,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
         self.msg_type = "mcu"
         self.m_t = 1
         # 0未开始  1进行中 2已结束  99异常
-        self.action_state = 1
+        self.action_state = 2
         self._mcu_move_state = 0
         self.state_camera_motor = 3
         self.state_camera_steering = 3
@@ -741,12 +743,12 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             time.sleep(2)
             loop = asyncio.get_event_loop()
             loop.create_task(self.initDevice(), name="init_mcu")
-            async def getBaseInfo():
-                while True:
-                    await asyncio.sleep(1)
-                    # 异步循环获取设备信息
-                    self.to_get_mcu_base_info()
-            asyncio.gather(getBaseInfo())
+            # async def getBaseInfo():
+            #     while True:
+            #         await asyncio.sleep(1)
+            #         # 异步循环获取设备信息
+            #         self.to_get_mcu_base_info()
+            # asyncio.gather(getBaseInfo())
         else:
             print("串口无法识别")
             # 走其他途径处理
@@ -1068,7 +1070,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                 print(device_name, value)
                 self.to_device_move(
                     device_name=device_name,
-                    value=value,
+                    value=float(value),
                     max_speed=1400,
                     up_speed=400,
                     down_speed=100,
@@ -1080,7 +1082,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                 # 相机舵机
                 self.to_device_move(
                     device_name=device_name,
-                    value=value,
+                    value=float(value),
                     _is_debug=_is_debug,
                     is_deviation=0,
                 )
@@ -1088,7 +1090,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                 # 转盘舵机
                 self.to_device_move(
                     device_name=device_name,
-                    value=value,
+                    value=float(value),
                     _is_debug=_is_debug,
                     is_deviation=0,
                 )
@@ -1096,7 +1098,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                 # 转盘舵机
                 self.to_device_move(
                     device_name=device_name,
-                    value=value,
+                    value=float(value),
                     max_speed=1400,
                     up_speed=400,
                     down_speed=100,
@@ -1110,8 +1112,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                 )
             case "laser_position":
                 self.to_deal_device(
-                    device_name="laser_position",
-                    value=0 if value <= 0 else 1
+                    device_name="laser_position", value=0 if int(value) <= 0 else 1
                 )
             case "take_picture":
                 capture_one = DigiCam()
@@ -1128,24 +1129,68 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                 pass
             # case "photograph":
             #     self.photograph(goods_art_no=None)
+    def checkDevice(self):
+        if not self.is_running:
+            self.sendSocketMessage(
+                code=1, msg="mcu设备未连接,请先连接设备", device_status=0
+            )
+            return False
+        if self.init_state is not True:
+            self.sendSocketMessage(code=1, msg="mcu设备未初始化", device_status=4)
+            return False
+        if self.action_state != 2:
+            self.sendSocketMessage(
+                code=1,
+                msg="当前有未完成的任务,请稍后再试",
+                device_status=0,
+            )
+            return False
 
-    async def run_mcu_config(self,config_list,goods_art_no):
+    async def run_mcu_config(self, config_list, goods_art_no, action_info):
+        if self.checkDevice() == False:
+            return
+        image_counts = 0
         if config_list:
+            for idx, item in enumerate(config_list):
+                is_take_picture = item["take_picture"]
+                if is_take_picture:
+                    image_counts += 1
+                    # 批量插入
+                    image_deal_mode = 0 if action_info == "执行左脚程序" else 1
+                    insert_photo_records(image_deal_mode=image_deal_mode, goods_art_no=goods_art_no, image_index=idx)
             total_len = len(config_list)
             self.action_state = 1
+            self.msg_type = "image_process"
+            self.sendSocketMessage(
+                code=0,
+                msg="MCU 命令已发送完成",
+                device_status=2,
+                data={
+                    "goods_art_no": goods_art_no,
+                    "image_counts": image_counts,
+                    "current_time":datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
+                },
+            )
+            self.msg_type = "mcu"
             for index, action in enumerate(config_list):
                 await asyncio.sleep(0.1)
+                action_is_take_picture = action["take_picture"]
+                image_index = -1
+                if action_is_take_picture:
+                    image_index = index
                 program_item = ProgramItem(
                     websocket_manager=self.websocket_manager,
                     action_data=action,
                     mcu=self,
                     goods_art_no=goods_art_no,
+                    image_index=image_index,
                 )
                 print("self.action_state===>", self.action_state)
                 if self.action_state != 1:
                     # 异常终止
                     print("action异常终止")
                     break
+                self.msg_type = "photo_take"
                 if not program_item.run(total_len):
                     self.sendSocketMessage(
                         code=1,
@@ -1161,7 +1206,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                         msg="{} 执行完成~".format(program_item.action_name),
                         device_status=2,
                     )
-
+                self.msg_type = "mcu"
                 # 在第三张图时检查是否有对应图片生成
                 # if index == 3:
                 #     # if not self.image_process_data.check_photo_is_get():

+ 79 - 0
python/mcu/McuDebug.py

@@ -0,0 +1,79 @@
+import time
+
+class McuDebug(object):
+
+    def __init__(self, mcu, is_debug=True, is_deviation=False):
+        self.mcu = mcu
+        self.is_debug = 1 if is_debug else 0
+        self.is_deviation = 1 if is_deviation else 0
+
+    def camera_high_motor(self, value):
+        # 相机电机
+        self.mcu.to_device_move(
+            device_name="camera_high_motor",
+            value=value,
+            max_speed=1400,
+            up_speed=400,
+            down_speed=100,
+            _is_debug=self.is_debug,
+            is_deviation=self.is_deviation,
+        )
+
+    def camera_steering(self, value):
+        # 相机舵机
+        self.mcu.to_device_move(
+            device_name="camera_steering",
+            value=value,
+            _is_debug=self.is_debug,
+            is_deviation=self.is_deviation,
+        )
+
+    def turntable_steering(self, value):
+        # 转盘舵机
+        self.mcu.to_device_move(
+            device_name="turntable_steering",
+            value=value,
+            _is_debug=self.is_debug,
+            is_deviation=self.is_deviation,
+        )
+
+    def turntable_position_motor(self, value):
+        # 转盘舵机
+        self.mcu.to_device_move(
+            device_name="turntable_position_motor",
+            value=value,
+            max_speed=1400,
+            up_speed=400,
+            down_speed=100,
+            _is_debug=self.is_debug,
+            is_deviation=self.is_deviation,
+        )
+
+    def overturn_steering(self, value):
+        # 翻板舵机中位
+        self.mcu.to_device_move(
+            device_name="overturn_steering",
+            value=value,
+            _is_debug=self.is_debug,
+            is_deviation=self.is_deviation,
+        )
+
+    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
+        )
+
+    def photograph(self, goods_art_no=None):
+        self.mcu.photograph(goods_art_no=goods_art_no)
+
+    def __move_equipment(self, data):
+        func_class = {
+            "相机电机": self.camera_high_motor,
+            "相机舵机": self.camera_steering,
+            "转盘舵机": self.turntable_steering,
+            "转盘前后电机": self.turntable_position_motor,
+        }
+        for key, value in data.items():
+            if key in func_class:
+                func_class[key](value=value)
+                time.sleep(0.1)

+ 292 - 0
python/mcu/McuDeviationSet.py

@@ -0,0 +1,292 @@
+from collections import defaultdict
+from .McuDebug import McuDebug
+from functools import partial
+import time
+import settings
+
+
+class McuDeviationSet():
+    # 调试处理
+    # data_sign = Signal(dict)
+    # info_sign = Signal(str)
+    # is_get_offset = Signal(dict)
+
+    def __init__(self, mcu):
+        super().__init__()
+        self.mcu = mcu
+        # self.setupUi(self)
+        # self.init()
+        # self.set_enable_by_mcu()
+        # self.show()
+        self.last_value = defaultdict(float)
+        self.mcu_debug = McuDebug(mcu, is_debug=True, is_deviation=False)
+        self.get_mcu_deviation()
+        # # 运动到设定位
+        # QTimer.singleShot(2500, self.init_pos)
+
+    # def set_enable_by_mcu(self):
+    #     if self.mcu.state_camera_motor != 2:
+    #         self.doubleSpinBox.setEnabled(False)
+    #         self.pushButton_3.setEnabled(False)
+
+    #     if self.mcu.state_camera_steering != 2:
+    #         self.doubleSpinBox_2.setEnabled(False)
+    #         self.pushButton_16.setEnabled(False)
+
+    #     if self.mcu.state_turntable_steering != 2:
+    #         self.doubleSpinBox_3.setEnabled(False)
+    #         self.pushButton_22.setEnabled(False)
+    #         self.doubleSpinBox_7.setEnabled(False)
+
+    def init(self):
+        self.is_get_offset.connect(self.init_pos)
+
+        self.windows.data_info.connect(self.get_mcu_deviation_info)
+        # 相机电机
+        self.doubleSpinBox.setSingleStep(1)
+        self.doubleSpinBox.setMinimum(0)
+        self.doubleSpinBox.setMaximum(400)
+        self.doubleSpinBox.valueChanged.connect(
+            lambda x: self.change_value(x, "相机电机")
+        )
+
+        # 相机舵机
+        self.doubleSpinBox_2.setSingleStep(0.1)
+        self.doubleSpinBox_2.setMinimum(-40)
+        self.doubleSpinBox_2.setMaximum(40)
+        self.doubleSpinBox_2.valueChanged.connect(
+            lambda x: self.change_value(x, "相机舵机")
+        )
+
+        # 转盘电机
+        self.doubleSpinBox_3.setSingleStep(1)
+        self.doubleSpinBox_3.setMinimum(-720)
+        self.doubleSpinBox_3.setMaximum(720)
+        self.doubleSpinBox_3.valueChanged.connect(
+            lambda x: self.change_value(x, "转盘舵机")
+        )
+
+        # 转盘前后电机
+        self.doubleSpinBox_7.setSingleStep(1)
+        self.doubleSpinBox_7.setMinimum(0)
+        self.doubleSpinBox_7.setMaximum(950)
+        self.doubleSpinBox_7.setValue(300)
+        self.doubleSpinBox_7.valueChanged.connect(
+            lambda x: self.change_value(x, "转盘前后电机")
+        )
+
+        # 翻板舵机中位
+        self.doubleSpinBox_4.setSingleStep(0.5)
+        self.doubleSpinBox_4.setValue(90)
+        self.doubleSpinBox_4.setMinimum(0)
+        self.doubleSpinBox_4.setMaximum(180)
+        self.doubleSpinBox_4.valueChanged.connect(
+            lambda x: self.change_value(x, "翻板舵机中位")
+        )
+
+        # 翻板舵机高位
+        self.doubleSpinBox_5.setSingleStep(0.5)
+        self.doubleSpinBox_5.setValue(10)
+        self.doubleSpinBox_5.setMinimum(0)
+        self.doubleSpinBox_5.setMaximum(180)
+        self.doubleSpinBox_5.valueChanged.connect(
+            lambda x: self.change_value(x, "翻板舵机高位")
+        )
+
+        # 翻板舵机上升速度
+        self.doubleSpinBox_6.setSingleStep(1)
+        self.doubleSpinBox_6.setValue(5)
+        self.doubleSpinBox_6.setMinimum(1)
+        self.doubleSpinBox_6.setMaximum(10)
+        self.doubleSpinBox_6.valueChanged.connect(
+            lambda x: self.change_value(x, "翻板舵机上升速度")
+        )
+
+        # 翻板舵机下降速度
+        self.doubleSpinBox_8.setSingleStep(1)
+        self.doubleSpinBox_8.setValue(5)
+        self.doubleSpinBox_8.setMinimum(1)
+        self.doubleSpinBox_8.setMaximum(10)
+        self.doubleSpinBox_8.valueChanged.connect(
+            lambda x: self.change_value(x, "翻板舵机下降速度")
+        )
+
+        # 获取偏移量
+        self.pushButton_2.clicked.connect(self.get_mcu_deviation)
+        # 设定相机高度偏移量
+        self.pushButton_3.clicked.connect(lambda *args: self.set_deviation("相机电机"))
+        # 设定相机舵机偏移量
+        self.pushButton_16.clicked.connect(lambda *args: self.set_deviation("相机舵机"))
+        # 设定转盘偏移量
+        self.pushButton_22.clicked.connect(lambda *args: self.set_deviation("转盘舵机"))
+        # 设定翻版舵机偏移量
+        self.pushButton_8.clicked.connect(
+            lambda *args: self.set_deviation("翻板舵机中位")
+        )
+        # 设定翻版舵机偏移量
+        self.pushButton_9.clicked.connect(
+            lambda *args: self.set_deviation("翻板舵机高位")
+        )
+        self.pushButton_10.clicked.connect(
+            lambda *args: self.set_deviation("翻板舵机上升速度")
+        )
+        self.pushButton_12.clicked.connect(
+            lambda *args: self.set_deviation("翻板舵机下降速度")
+        )
+
+        # 转盘重新获取转速比
+        # self.pushButton_7.clicked.connect(self.set_turntable_to_ratio_init)
+        # 转盘重新定位到原点
+        self.pushButton_11.clicked.connect(self._to_init_all)
+
+    def init_pos(self, data):
+        time.sleep(0.6)
+        func = partial(self._move_equipment, data=data)
+        self.do_thread_run(func, call_back=None, time_out=30)
+
+    def _move_equipment(self, data):
+        self.mcu_debug.move_equipment(data=data)
+        # MCU运动是否有停止检查,设定超时时间
+        time.sleep(1.5)
+        if self.check_mcu_move_is_stop() is False:
+            return
+
+    def check_mcu_move_is_stop(self, out_time=15):
+        _s = time.time()
+        while 1:
+            time.sleep(0.1)
+            if time.time() - _s > out_time:
+                return True
+            if self.mcu.mcu_move_state == 2:
+                return True
+            if settings.IS_LIN_SHI_TEST:
+                time.sleep(3)
+                return True
+
+    def change_value(self, value, name):
+        value = round(value, 2)
+        if self.last_value[name] - 1 <= value <= self.last_value[name] + 1:
+            if name == "相机电机":
+                self.mcu_debug.camera_high_motor(value=value)
+            if name == "相机舵机":
+                self.mcu_debug.camera_steering(value=value)
+            if name == "转盘舵机":
+                self.mcu_debug.turntable_steering(value=value)
+            if name == "转盘前后电机":
+                self.mcu_debug.turntable_position_motor(value=value)
+            if name == "翻板舵机中位":
+                self.mcu_debug.overturn_steering(value=value)
+            if name == "翻板舵机高位":
+                self.mcu_debug.overturn_steering(value=value)
+            if name == "翻板舵机上升速度":
+                pass
+            print(value, name)
+        self.last_value[name] = value
+
+    def set_deviation(self, name):
+        if name == "相机电机":
+            # 设定相机高度偏移量 单位mm
+            camera_high_motor_deviation = int(self.doubleSpinBox.value())
+            device_name = "camera_high_motor"
+            self.mcu.set_deviation(
+                device_name=device_name, _type=0, deviation=camera_high_motor_deviation
+            )
+
+        if name == "相机舵机":
+            # 设定相机舵机偏移量
+            camera_steering_deviation = self.doubleSpinBox_2.value()
+            device_name = "camera_steering"
+            self.mcu.set_deviation(
+                device_name=device_name, _type=0, deviation=camera_steering_deviation
+            )
+            self.doubleSpinBox_2.setValue(0.0)
+
+        if name == "转盘舵机":
+            # 设定转盘舵机偏移角度  单位 度
+            turntable_steering_deviation = self.doubleSpinBox_3.value()
+            device_name = "turntable_steering"
+            self.mcu.set_deviation(
+                device_name=device_name, _type=1, deviation=turntable_steering_deviation
+            )
+
+        if name == "翻板舵机中位":
+            # 设定翻版舵机偏移量
+            value = self.doubleSpinBox_4.value()
+            device_name = "overturn_steering"
+            self.mcu.set_deviation(
+                device_name=device_name, _type=0, deviation=value
+            )
+        if name == "翻板舵机高位":
+            # 设定翻版舵机偏移量
+            value = self.doubleSpinBox_5.value()
+            device_name = "overturn_steering"
+            self.mcu.set_deviation(
+                device_name=device_name, _type=1, deviation=value
+            )
+        if name == "翻板舵机上升速度":
+            # 设定翻版舵机偏移量
+            value = self.doubleSpinBox_6.value()
+            device_name = "overturn_steering"
+            self.mcu.set_deviation(
+                device_name=device_name, _type=2, deviation=value
+            )
+        if name == "翻板舵机下降速度":
+            # 设定翻版舵机偏移量
+            value = self.doubleSpinBox_8.value()
+            device_name = "overturn_steering"
+            self.mcu.set_deviation(
+                device_name=device_name, _type=3, deviation=value
+            )
+
+    def _to_init_all(self, *args):
+        self.mcu.to_init_device_origin_point(device_name="mcu", is_force=True)
+
+    def get_mcu_deviation(self):
+        self.mcu.get_deviation()
+
+    def get_mcu_deviation_info(self, data):
+        if "_type" not in data:
+            return
+        if data["_type"] == "get_deviation_data":
+            data = data["data"]
+            print("偏移量信息")
+            # overturn_steering_up_speed  overturn_steering_down_speed
+            # 相机电机 单位mm
+            if "camera_high_motor_deviation" in data:
+                self.doubleSpinBox.setValue(float(data["camera_high_motor_deviation"]))
+            # 相机舵机
+            if "camera_steering_deviation" in data:
+                self.doubleSpinBox_2.setValue(float(data["camera_steering_deviation"]))
+            # 转盘偏移位
+            if "turntable_steering_deviation" in data:
+                self.doubleSpinBox_3.setValue(
+                    float(data["turntable_steering_deviation"])
+                )
+            # 翻板舵机中位
+            if "overturn_steering_middle" in data:
+                self.doubleSpinBox_4.setValue(float(data["overturn_steering_middle"]))
+            # 翻板舵机高位
+            if "overturn_steering_high" in data:
+                self.doubleSpinBox_5.setValue(float(data["overturn_steering_high"]))
+
+            # 翻板舵机
+            if "overturn_steering_up_speed" in data:
+                self.doubleSpinBox_6.setValue(float(data["overturn_steering_up_speed"]))
+
+            if "overturn_steering_down_speed" in data:
+                self.doubleSpinBox_8.setValue(
+                    float(data["overturn_steering_down_speed"])
+                )
+
+            # 初始化位置
+            data = {
+                "相机电机": self.doubleSpinBox.value(),
+                "相机舵机": self.doubleSpinBox_2.value(),
+                "转盘舵机": self.doubleSpinBox_3.value(),
+                "转盘前后电机": self.doubleSpinBox_7.value(),
+            }
+            self.is_get_offset.emit(data)
+
+    def __del__(self):
+        self.state = 2
+        self.t = None

+ 3 - 1
python/mcu/ProgramItem.py

@@ -11,7 +11,7 @@ class ProgramItem(BaseClass):
     # program_sign = Signal(dict)
     # program_refresh_photo_list_sign = Signal()
 
-    def __init__(self,websocket_manager, action_data:any, mcu, goods_art_no:str=None):
+    def __init__(self,websocket_manager, action_data:any, mcu, goods_art_no:str=None,image_index:int=-1):
         super().__init__(BaseClass)
         # 1 表示等待中,2表示没有等待
         self.wait_state = 2
@@ -19,6 +19,8 @@ class ProgramItem(BaseClass):
         self.capture_one = DigiCam()
         captrure_folder_path = self.capture_one.getCaptureFolderPath()
         self.watch_dog = FileEventHandler()
+        self.watch_dog.goods_art_no = goods_art_no
+        self.watch_dog.image_index = image_index
         self.watch_dog.start_observer(captrure_folder_path)
         print("21 =========ProgramItem=======action_data=====")
         print(action_data)

+ 2 - 21
python/mcu/RemoteControlV2.py

@@ -237,33 +237,14 @@ class RemoteControlV2(BaseClass):
                     },
                     "type": "run_mcu",
                 }
-                # loop = asyncio.get_event_loop()
-                # session = SqlQuery()
-                # crud = CRUD(DeviceConfig)
-                # condtions = {"mode_type": control_program, "action_status": True}
-                # all_devices = crud.read_all(
-                #     session, conditions=condtions, order_by="action_index", ascending=True
-                # )
-                # if len(all_devices) == 0:
-                #     self.sendSocketMessage(
-                #         code=1,
-                #         msg=f"当前没有可用配置",
-                #         data=input_data,
-                #         device_status=2,
-                #     )
-                #     return
-                # action_list = [device.model_dump() for device in all_devices]
-                # print("action_list", action_list)
-                # loop.create_task(
-                #     device_ctrl.run_mcu_config(action_list, self.goods_art_no),
-                #     name="run_mcu_config",
-                # )
+                self.msg_type = "blue_tooth_scan"
                 self.sendSocketMessage(
                     code=0,
                     msg=f"准备执行[{control_program}]",
                     data=input_data,
                     device_status=2,
                 )
+                self.msg_type = "blue_tooth"
             self.sendSocketMessage(code=0, msg="", data=message, device_status=2)
             if settings.IS_DEBUG:
                 print("收到按键", button_value)

+ 13 - 13
python/mcu/capture/capture_basic_mode.py

@@ -29,14 +29,19 @@ class CaptureBasic():
         self.is_get_photo = False
         # self.keyboard = Controller()
         self.hwnd = None
-    def getPreviewImage(self)->Image:
-        '''获取预览图'''
-        yzmdata = requests.get(self.base_host + "/preview.jpg")
-        tempIm = BytesIO(yzmdata.content)
-        im = Image.open(tempIm)
-        return im
-
-    def getCaptureFolderPath(self) -> Image:
+    # def getPreviewImage(self)->Image:
+    #     '''获取预览图'''
+    #     yzmdata = requests.get(self.base_host + "/preview.jpg")
+    #     tempIm = BytesIO(yzmdata.content)
+    #     im = Image.open(tempIm)
+    #     return im
+
+    def auto_focus(self):
+        # 执行拍照
+        time.sleep(0.2)
+        self.run_capture_action("LiveView_Focus")
+
+    def getCaptureFolderPath(self):
         """获取预览图"""
         response = httpGetHandler(
             url=self.base_host,
@@ -140,11 +145,6 @@ class CaptureBasic():
     #     copy_file(srcfile, dst_path, dst_file)
     #     # print("修改图片耗时:{}".format(time.time() - s))
 
-    def auto_focus(self):
-        # 执行拍照
-        time.sleep(0.2)
-        self.run_capture_action("LiveView_Focus")
-
     def last_images(self):
         if not self.hwnd:
             return

+ 37 - 1
python/mcu/capture/module_watch_dog.py

@@ -6,6 +6,30 @@ import os
 from utils.utils_func import get_folder, check_path
 import datetime
 from utils.SingletonType import SingletonType
+from databases import CRUD,SqlQuery,PhotoRecord
+from databases import CRUD, SqlQuery, PhotoRecord
+def updateImageRaw(time_str, image_path, goods_art_no, image_index):
+    session = SqlQuery()
+    crud = CRUD(PhotoRecord)
+    res = crud.read(
+        session,
+        conditions={
+            "image_path": None,
+            "goods_art_no": goods_art_no,
+            "image_index": image_index,
+        },
+    )
+    if res:
+        # 格式化为年月日时分
+        update = {
+            "image_path": image_path,
+            "photo_create_time": time_str,
+        }
+        crud.update(
+            session,
+            res.id,
+            **update,
+        )
 
 
 class FileEventHandler(FileSystemEventHandler, metaclass=SingletonType):
@@ -17,6 +41,8 @@ class FileEventHandler(FileSystemEventHandler, metaclass=SingletonType):
         #     return
         # else:
         #     self.init_flag = True
+        self.goods_art_no = None
+        self.image_index = -1
         super().__init__()
         # self.window = window
         FileSystemEventHandler.__init__(self)
@@ -78,7 +104,10 @@ class FileEventHandler(FileSystemEventHandler, metaclass=SingletonType):
         #     if os.path.split(event.dest_path)[0] == settings.PhotoOutputDir:
         #         print("1111111")
         pass
-
+    def updatePhotoRecord(self):
+        session = SqlQuery()
+        crud = CRUD(PhotoRecord)
+        crud.read()
     def on_created(self, event):
         if not event.is_directory:
             file_path = event.src_path
@@ -94,6 +123,13 @@ class FileEventHandler(FileSystemEventHandler, metaclass=SingletonType):
                 # print("获取文件create_time:{}".format(create_time))
                 self.get_photo_info(raw_path=file_path, create_time=create_time, take_time=take_time)
                 self.send_log("获取文件create_time:{}".format(create_time))
+                if self.goods_art_no == None:
+                    print("货号不存在,监听不写入")
+                    return
+                if file_path == None:
+                    print("file_path不存在,监听不写入")
+                    return
+                updateImageRaw(create_time, file_path, self.goods_art_no,self.image_index)
             except BaseException as e:
                 print("获取文件create_time失败", e)
                 self.send_log("获取文件处理失败{}".format(e))

+ 1 - 1
python/middleware.py

@@ -38,7 +38,7 @@ def on_startup():
     create_all_database()
     session = SqlQuery()
     device_config_crud = CRUD(DeviceConfig)
-    all_devices = device_config_crud.read_all(session, conditions={"id": 2})
+    all_devices = device_config_crud.read_all(session)
     if len(all_devices) == 0:
         # 如果配置表中一条数据都没有,就将初始化数据全部插入到数据表中
         actions = json.load(open("action.json", encoding="utf-8"))

+ 10 - 9
python/model/photo_record.py

@@ -1,14 +1,15 @@
 from sqlmodel import SQLModel, Field
 from datetime import datetime
+from typing import Optional
 
 class PhotoRecord(SQLModel, table=True):
     __tablename__ = "photo_record"
-
-    id: int = Field(default=None, primary_key=True, index=True)
-    goods_art_no: str = Field(max_length=128, nullable=False)
-    image_index: int = Field(default=None)
-    image_deal_mode: int = Field(default=None)
-    photo_create_time: datetime = Field(default=None)
-    update_time: datetime = Field(default=None)
-    create_time: datetime = Field(default_factory=datetime.utcnow)
-    delete_time: datetime = Field(default=None)
+    id: Optional[int] = Field(default=None, primary_key=True, index=True)
+    goods_art_no: Optional[str] = Field(max_length=128, nullable=False)
+    image_path: Optional[str] = Field(default=None)
+    image_index: Optional[int] = Field(default=None)
+    image_deal_mode: Optional[int] = Field(default=None)
+    photo_create_time: Optional[datetime] = Field(default=None)
+    update_time: Optional[datetime] = Field(default=None)
+    create_time: Optional[datetime] = Field(default_factory=datetime.utcnow)
+    delete_time: Optional[datetime] = Field(default=None)

+ 6 - 0
python/models.py

@@ -44,3 +44,9 @@ 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)
+
+
+class PhotoRecordDelete(BaseModel):
+    """获取可执行程序命令列表"""
+
+    goods_art_nos: list[str] = Field(default=None, description="货号数组")

+ 13 - 1
python/sockets/message_handler.py

@@ -4,6 +4,8 @@ import json, asyncio
 from mcu.DeviceControl import DeviceControl, checkMcuConnection
 from mcu.BlueToothMode import BlueToothMode
 from databases import DeviceConfig,SqlQuery,CRUD
+from mcu.capture.module_digicam import DigiCam
+
 # socket消息发送逻辑处理方法
 async def handlerSend(
     manager: ConnectionManager, receiveData: str, websocket: WebSocket
@@ -56,9 +58,18 @@ async def handlerSend(
             device_ctrl = DeviceControl(websocket_manager=manager)
             device_ctrl.controlDevice(device_name, value)
         case "run_mcu":
+            try:
+                # 判断拍照软件是否初始化
+                digicam = DigiCam()
+                digicam.getCaptureFolderPath()
+            except:
+                data = manager.jsonMessage(code=1, msg="digicam未初始化,请检查")
+                await manager.send_personal_message(data, websocket)
+                return
             action_info = data.get("action", "执行左脚程序")
             goods_art_no = data.get("goods_art_no", None)
             if goods_art_no == None or goods_art_no =="":
+                # 判断货号是否存在
                 data = manager.jsonMessage(code=1, msg="goods_art_no不能为空")
                 await manager.send_personal_message(data, websocket)
                 return
@@ -69,6 +80,7 @@ async def handlerSend(
                 session, conditions=condtions, order_by="action_index", ascending=True
             )
             if len(all_devices) == 0:
+                # 判断是否有可用配置
                 data = manager.jsonMessage(code=1, msg="当前没有可用配置")
                 await manager.send_personal_message(data, websocket)
                 return
@@ -76,7 +88,7 @@ async def handlerSend(
             print("action_list", action_list)
             device_ctrl = DeviceControl(websocket_manager=manager)
             loop.create_task(
-                device_ctrl.run_mcu_config(action_list, goods_art_no),
+                device_ctrl.run_mcu_config(action_list, goods_art_no, action_info),
                 name="run_mcu_config",
             )
         case _:

+ 21 - 0
python/temp.py

@@ -0,0 +1,21 @@
+from databases import CRUD, SqlQuery, PhotoRecord
+from datetime import datetime
+
+session = SqlQuery()
+crud = CRUD(PhotoRecord)
+res = crud.read(session,conditions={"image_path":None})
+if res:
+    time_str = "2025-03-26 19:29:39.954187"
+    dt = datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S.%f")
+    # 格式化为年月日时分
+    formatted_time_str = dt.strftime("%Y-%m-%d %H:%M:%S")
+    update = {
+        "image_path": "sadasda",
+        "image_index": 0,
+        "photo_create_time": datetime.strptime(formatted_time_str, "%Y-%m-%d %H:%M:%S"),
+    }
+    crud.update(
+        session,
+        res.id,
+        **update,
+    )