diff --git a/.gitignore b/.gitignore index 62b0bd5..054d570 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,5 @@ PySide2_Fluent_Widgets.egg-info/ __pycache__ /core/__pycache__ /vision/__pycache__ +/opcua/__pycache__ +/feeding/__pycache__ diff --git a/busisness/blls.py b/busisness/blls.py index f4f8153..098e8c4 100644 --- a/busisness/blls.py +++ b/busisness/blls.py @@ -60,7 +60,7 @@ class ArtifactBll: def get_artifacting_task(self) -> ArtifactInfoModel: """获取正在进行的管片任务数据""" - loc_item= self.dal.get_top_artifact(1,"","Status=2") + loc_item= self.dal.get_top_artifact(1,"ID desc","Status=2") if loc_item: return loc_item[0] else: diff --git a/busisness/models.py b/busisness/models.py index 6b9b36f..e0b68d3 100644 --- a/busisness/models.py +++ b/busisness/models.py @@ -308,3 +308,41 @@ class PDRecordModel: CreateTime: str="" #派单时间(下发) OptTime: str="" + +@dataclass +class LEDInfo: + """LED信息模型""" + # 任务单号 + TaskID: str + # 盘方量 + PlateVolume: str + # 模具编号 + MouldCode: str + # 生产投料时间 + ProduceStartTime: str + # 管片编号 + ArtifactID: str + # 环类型编码(无) + RingTypeCode: str + # 累计盘次 + PlateIDSerial: str + # 检验结果 + CheckResult: str + # 高斗称值(无) + UpperWeight:float + # 砼出料温度温度 + Temper: str + # 车间温度 + WorkshopTemperature: str + # 低桶称量值 + LowBucketWeighingValue: str + # 振动频率 + VibrationFrequency: str + # 总计量 + TotMete: str + # 已浇筑方量 + BetonVolumeAlready: str + # 水温 + WaterTemperature: str + # 配方比例 + FormulaProportion: str \ No newline at end of file diff --git a/config/ini_manager.py b/config/ini_manager.py index f58499c..4f62c01 100644 --- a/config/ini_manager.py +++ b/config/ini_manager.py @@ -141,7 +141,31 @@ class IniManager: def log_path(self): """获取日志文件路径""" return self._read_config_value('app', 'LOG_PATH', 'logs/app.log', str) + + @property + def opcua_endpoint(self): + """获取OPC UA服务器端点""" + return self._read_config_value('app', 'OPCUA_ENDPOINT', 'opc.tcp://192.168.250.64:4840/zjsh_feed/server/', str) + @property + def upper_transmitter_ip(self): + """获取上料斗变送器IP""" + return self._read_config_value('app', 'UPPER_TRANSMITTER_IP', '192.168.250.63', str) + + @property + def upper_transmitter_port(self): + """获取上料斗变送器端口""" + return self._read_config_value('app', 'UPPER_TRANSMITTER_PORT', 502, int) + + @property + def lower_transmitter_ip(self): + """获取下料斗变送器IP""" + return self._read_config_value('app', 'LOWER_TRANSMITTER_IP', '192.168.250.66', str) + + @property + def lower_transmitter_port(self): + """获取下料斗变送器端口""" + return self._read_config_value('app', 'LOWER_TRANSMITTER_PORT', 8234, int) ini_manager = IniManager() diff --git a/config/settings.py b/config/settings.py index f86cc91..3c26960 100644 --- a/config/settings.py +++ b/config/settings.py @@ -10,7 +10,9 @@ class Settings: self.relay_host = '192.168.250.62' self.relay_port = 50000 - self.debug_feeding=True + self.debug_feeding=False + #调试模式上,网络继点器和摄像头禁用,模型推理禁用 + self.debug_mode=False # 摄像头配置 self.camera_type = "ip" @@ -46,7 +48,7 @@ class Settings: self.single_batch_weight = 2500 # 单次下料重量(kg) # 角度控制参数 - self.target_angle = 20.0 # 目标角度 + self.target_angle = 30.0 # 目标角度 self.min_angle = 10.0 # 最小角度 self.max_angle = 80.0 # 最大角度 self.angle_threshold = 60.0 # 角度阈值 @@ -83,5 +85,9 @@ class Settings: self.max_upper_volume = 2.4 # 上料斗容量(方) #下料到下料斗最大下到多少,并非最大容量 self.max_lower_volume = 2.4 # 下料斗容量(方) - + + #led + self.led_interval = 2 # LED闪烁间隔(秒) + +app_set_config = Settings() diff --git a/controller/main_controller.py b/controller/main_controller.py index 1eb4045..31be6ca 100644 --- a/controller/main_controller.py +++ b/controller/main_controller.py @@ -7,7 +7,6 @@ from .bottom_control_controller import BottomControlController from .hopper_controller import HopperController from service.msg_recorder import MessageRecorder -from config.settings import Settings from core.system import FeedingControlSystem from opc.opcua_server import SimpleOPCUAServer @@ -15,8 +14,7 @@ class MainController: def __init__(self): # 主界面 self.main_window = MainWindow() - self.settings = Settings() - self.system = FeedingControlSystem(self.settings) + self.system = FeedingControlSystem() self.system.initialize() self.system.state.state_updated.connect(self.update_ui_notify) self.opcua_server = SimpleOPCUAServer(self.system.state) @@ -63,7 +61,7 @@ class MainController: def update_ui_notify(self, prop:str,value): """更新UI状态""" - print(f"更新UI状态: {prop} = {value}") + # print(f"更新UI状态: {prop} = {value}") def __connectSignals(self): self.main_window.about_to_close.connect(self.handleMainWindowClose) # 处理主界面关闭 @@ -71,6 +69,9 @@ class MainController: def handleMainWindowClose(self): """主界面关闭""" self.msg_recorder.normal_record("关闭自动智能浇筑系统") + self.system.stop() + self.opcua_server.stop() + # 停止系统底部控制器中的线程 if hasattr(self, 'bottom_control_controller'): self.bottom_control_controller.stop_threads() \ No newline at end of file diff --git a/core/state.py b/core/state.py index 58d4425..7ceb71a 100644 --- a/core/state.py +++ b/core/state.py @@ -23,7 +23,7 @@ class SystemState(QObject): self._upper_volume=0.0 #下料斗状态想着 - self._lower_feeding_stage = 0 # 0:未下料, 1:第一阶段, 2:第二阶段, 3:第三阶段, 4:等待模具车对齐 + self.lower_feeding_stage = 0 # 0:未下料, 1:第一阶段, 2:第二阶段, 3:第三阶段, 4:等待模具车对齐 self._lower_is_arch_=False self._lower_weight=0 self._lower_angle=0.0 @@ -69,11 +69,8 @@ class SystemState(QObject): #当前生产状态 self._feed_status=FeedStatus.FNone #每方重量 - self.density=2500 - - - - # + self.density=2416.4 + # self._watched_props = [k for k in self.__dict__ if k.startswith('_')] def __setattr__(self, name, value): diff --git a/core/system.py b/core/system.py index d8bea07..0f41802 100644 --- a/core/system.py +++ b/core/system.py @@ -2,7 +2,6 @@ import threading import time import cv2 -from config.settings import Settings from core.state import SystemState from hardware.relay import RelayController from hardware.inverter import InverterController @@ -11,31 +10,32 @@ from hardware.RFID.rfid_service import rfid_service from vision.camera import DualCameraController from vision.detector import VisionDetector from feeding.controller import FeedingController +from service.mould_service import app_web_service +from config.settings import app_set_config class FeedingControlSystem: - def __init__(self, settings: Settings): - self.settings = settings + def __init__(self): self.state = SystemState() # 初始化硬件控制器 self.relay_controller = RelayController( - host=settings.relay_host, - port=settings.relay_port + host=app_set_config.relay_host, + port=app_set_config.relay_port ) self.inverter_controller = InverterController(self.relay_controller) self.transmitter_controller = TransmitterController(self.relay_controller) # 初始化视觉系统 - self.camera_controller = DualCameraController(settings.camera_configs) + self.camera_controller = DualCameraController(app_set_config.camera_configs) - self.vision_detector = VisionDetector(settings) + self.vision_detector = VisionDetector() # 初始化RFID控制器 self.rfid_controller = rfid_service( - host=settings.rfid_host, - port=settings.rfid_port + host=app_set_config.rfid_host, + port=app_set_config.rfid_port ) # 初始化下料控制器 self.feeding_controller = FeedingController( @@ -45,8 +45,7 @@ class FeedingControlSystem: self.vision_detector, self.camera_controller, self.rfid_controller, - self.state, - settings + self.state ) @@ -56,6 +55,7 @@ class FeedingControlSystem: self.visual_control_thread = None self.alignment_check_thread = None self.lower_feeding_thread = None + self.led_thread = None def initialize(self): """初始化系统""" @@ -63,12 +63,12 @@ class FeedingControlSystem: # 设置摄像头配置 # self.camera_controller.set_config( - # camera_type=self.settings.camera_type, - # ip=self.settings.camera_ip, - # port=self.settings.camera_port, - # username=self.settings.camera_username, - # password=self.settings.camera_password, - # channel=self.settings.camera_channel + # camera_type=app_set_config.camera_type, + # ip=app_set_config.camera_ip, + # port=app_set_config.camera_port, + # username=app_set_config.camera_username, + # password=app_set_config.camera_password, + # channel=app_set_config.camera_channel # ) # # 初始化摄像头 @@ -78,30 +78,33 @@ class FeedingControlSystem: # 加载视觉模型 # if not self.vision_detector.load_models(): # raise Exception("视觉模型加载失败") - if not self.settings.debug_feeding: - if not self.check_device_connectivity(): - raise Exception("设备连接失败") - self.camera_controller.start_cameras() + + self.check_device_connectivity() + + # self.camera_controller.start_cameras() + # if not app_set_config.debug_feeding: # 启动系统监控(要料,破拱)线程 - self.start_monitoring() + self.start_monitoring() + # 启动视觉控制(角度、溢出)线程 + # self.start_visual_control() - # 启动视觉控制(角度、溢出)线程 - self.start_visual_control() - - # 启动对齐检查线程 - self.start_alignment_check() + # 启动对齐检查线程 + # self.start_alignment_check() # 启动下料线程 self.start_lower_feeding() + #LED屏 + # self.start_led() print("控制系统初始化完成") def start_monitoring(self): """启动系统监控""" - self.state.running = True + print('振动和要料监控线程启动') self.monitor_thread = threading.Thread( target=self._monitor_loop, - daemon=True + daemon=True, + name='monitor' ) self.monitor_thread.start() @@ -109,7 +112,7 @@ class FeedingControlSystem: """监控循环""" while self.state.running: try: - self.feeding_controller.check_upper_material_request() + # self.feeding_controller.check_upper_material_request() self.feeding_controller.check_arch_blocking() time.sleep(1) except Exception as e: @@ -117,9 +120,11 @@ class FeedingControlSystem: def start_visual_control(self): """启动视觉控制""" + print('视觉控制线程启动') self.visual_control_thread = threading.Thread( target=self._visual_control_loop, - daemon=True + daemon=True, + name='visual_control' ) self.visual_control_thread.start() @@ -132,37 +137,79 @@ class FeedingControlSystem: if current_frame is not None: # 执行视觉控制逻辑 self.feeding_controller.visual_control(current_frame) - time.sleep(self.settings.visual_check_interval) + time.sleep(app_set_config.visual_check_interval) except Exception as e: print(f"视觉控制循环错误: {e}") - time.sleep(self.settings.visual_check_interval) + time.sleep(app_set_config.visual_check_interval) def start_alignment_check(self): """启动对齐检查""" + print('对齐检查线程启动') self.alignment_check_thread = threading.Thread( target=self._alignment_check_loop, - daemon=True + daemon=True, + name='align_check' ) self.alignment_check_thread.start() def _alignment_check_loop(self): """对齐检查循环""" + loc_align_status=False + loc_before_status=None while self.state.running: try: - if self.state._lower_feeding_stage == 4: # 等待对齐阶段 + if self.state.lower_feeding_stage == 4: # 等待对齐阶段 current_frame = self.camera_controller.get_single_latest_frame() if current_frame is not None: - self.state.vehicle_aligned = self.vision_detector.detect_vehicle_alignment(current_frame) + self.state.vehicle_aligned = self.alignment_check_status() if self.state.vehicle_aligned: + # loc_count+=1 print("检测到模具车对齐") else: print("模具车未对齐") - else: - print('未检测到图像') - time.sleep(self.settings.alignment_check_interval) + # time.sleep(app_set_config.alignment_check_interval) + # loc_align_status=self.alignment_check_status() + # if loc_align_status and not loc_before_status: + # print("模具车由未对齐到对齐") + # self.state.vehicle_aligned=True + # elif not loc_align_status and loc_before_status: + # print("模具车由对齐到未对齐") + # self.state.vehicle_aligned=False + + # if loc_before_status!=loc_align_status: + # loc_before_status=loc_align_status + except Exception as e: print(f"对齐检查循环错误: {e}") - time.sleep(self.settings.alignment_check_interval) + finally: + time.sleep(app_set_config.alignment_check_interval) + + + def alignment_check_status(self)->bool: + """对齐检查循环""" + loc_aligned=False + loc_count=0 + for i in range(4): + try: + current_frame = self.camera_controller.get_single_latest_frame() + if current_frame is not None: + loc_aligned = self.vision_detector.detect_vehicle_alignment(current_frame) + if loc_aligned: + loc_count+=1 + print("检测到模具车对齐") + else: + loc_count=0 + print("模具车未对齐") + time.sleep(app_set_config.alignment_check_interval) + except Exception as e: + print(f"对齐检查循环错误: {e}") + time.sleep(app_set_config.alignment_check_interval) + + if loc_count>=3: + loc_aligned=True + else: + loc_aligned=False + return loc_aligned def start_lower_feeding(self): """启动下料流程""" @@ -177,7 +224,34 @@ class FeedingControlSystem: """启动下料流程""" while self.state.running: self.feeding_controller.start_feeding() - time.sleep(self.settings.lower_feeding_interval) + time.sleep(app_set_config.lower_feeding_interval) + + + + def start_led(self): + """启动LED流程""" + self.led_thread = threading.Thread( + target=self._start_led, + name="LED", + daemon=True + ) + self.led_thread.start() + + def _start_led(self): + """启动LED流程""" + while self.state.running: + led_info = app_web_service.get_pouring_led() + if led_info and self.state.current_artifact: + if self.state.current_artifact.MouldCode==led_info.MouldCode: + + led_info.RingTypeCode=self.state.current_artifact.RingTypeCode + led_info.UpperWeight=self.state._upper_weight + led_info.LowerWeight=self.state._lower_weight + led_info.VibrationFrequency=self.state._mould_frequency + + #发送到LED屏 + + time.sleep(app_set_config.led_interval) def check_device_connectivity(self) -> bool: """检查关键设备连接状态""" @@ -194,15 +268,15 @@ class FeedingControlSystem: return False # 尝试读取变频器一个寄存器(测试连接) - test_result = self.relay_controller.modbus_client.read_holding_registers( - address=0x00, - count=1, - slave=self.inverter_controller.config['slave_id'] - ) + # test_result = self.relay_controller.modbus_client.read_holding_registers( + # address=0x00, + # count=1, + # slave=self.inverter_controller.config['slave_id'] + # ) - if isinstance(test_result, Exception): - print("变频器连接测试失败") - return False + # if isinstance(test_result, Exception): + # print("变频器连接测试失败") + # return False # 检查下料斗变送器连接 test_weight = self.transmitter_controller.read_data(2) diff --git a/db/messages.db b/db/messages.db index 20aab85..f160244 100644 Binary files a/db/messages.db and b/db/messages.db differ diff --git a/db/three.db b/db/three.db index 047574e..f3c3c06 100644 Binary files a/db/three.db and b/db/three.db differ diff --git a/db/three.db-shm b/db/three.db-shm deleted file mode 100644 index fe9ac28..0000000 Binary files a/db/three.db-shm and /dev/null differ diff --git a/db/three.db-wal b/db/three.db-wal deleted file mode 100644 index e69de29..0000000 diff --git a/doc/~$ble表设计.doc b/doc/~$ble表设计.doc deleted file mode 100644 index f600800..0000000 Binary files a/doc/~$ble表设计.doc and /dev/null differ diff --git a/doc/~$宝-生产信息接口文档-20250911.doc b/doc/~$宝-生产信息接口文档-20250911.doc deleted file mode 100644 index 7018ce2..0000000 Binary files a/doc/~$宝-生产信息接口文档-20250911.doc and /dev/null differ diff --git a/doc/~$系统对接接口文档-20251020.doc b/doc/~$系统对接接口文档-20251020.doc deleted file mode 100644 index 702be87..0000000 Binary files a/doc/~$系统对接接口文档-20251020.doc and /dev/null differ diff --git a/doc/控制程序对接.docx b/doc/控制程序对接.docx new file mode 100644 index 0000000..d65a2da Binary files /dev/null and b/doc/控制程序对接.docx differ diff --git a/doc/控制程序对接文档.docx b/doc/控制程序对接文档.docx deleted file mode 100644 index e2292dd..0000000 Binary files a/doc/控制程序对接文档.docx and /dev/null differ diff --git a/feeding/__pycache__/controller.cpython-39.pyc b/feeding/__pycache__/controller.cpython-39.pyc index ff4f143..54fd215 100644 Binary files a/feeding/__pycache__/controller.cpython-39.pyc and b/feeding/__pycache__/controller.cpython-39.pyc differ diff --git a/feeding/__pycache__/process.cpython-39.pyc b/feeding/__pycache__/process.cpython-39.pyc index 07f329e..25b86f1 100644 Binary files a/feeding/__pycache__/process.cpython-39.pyc and b/feeding/__pycache__/process.cpython-39.pyc differ diff --git a/feeding/controller.py b/feeding/controller.py index cb7e9fa..327cb62 100644 --- a/feeding/controller.py +++ b/feeding/controller.py @@ -3,12 +3,13 @@ import time from core.state import FeedStatus from feeding.process import FeedingProcess from busisness.blls import ArtifactBll +from config.settings import app_set_config class FeedingController: def __init__(self, relay_controller, inverter_controller, transmitter_controller, vision_detector, - camera_controller, rfid_controller,state, settings): + camera_controller, rfid_controller,state): self.relay_controller = relay_controller self.inverter_controller = inverter_controller self.transmitter_controller = transmitter_controller @@ -16,14 +17,13 @@ class FeedingController: self.camera_controller = camera_controller self.rfid_controller = rfid_controller self.state = state - self.settings = settings self.artifact_bll = ArtifactBll() # 初始化下料流程 self.process = FeedingProcess( relay_controller, inverter_controller, transmitter_controller, vision_detector, - camera_controller, state, settings + camera_controller, state ) @@ -42,19 +42,21 @@ class FeedingController: if current_weight is None: self.state.upper_weight_error_count += 1 print(f"上料斗重量读取失败,错误计数: {self.state.upper_weight_error_count}") - if self.state.upper_weight_error_count >= self.settings.max_error_count: + if self.state.upper_weight_error_count >= app_set_config.max_error_count: print("警告:上料斗传感器连续读取失败,请检查连接") return False #需要搅拌楼通知下完料后移到上料斗上方 - if self.state._upper_door_position != 'over_lower': - self.state._upper_door_position = 'over_lower' + self.state.upper_weight_error_count = 0 # 判断是否需要要料:当前重量 < 目标重量 + 缓冲重量 - if self.state.feed_status != FeedStatus.FUpperToLower: - if current_weight < (self.settings.min_required_weight): + if self.state._feed_status != FeedStatus.FUpperToLower: + if current_weight < (app_set_config.min_required_weight): print("上料斗重量不足,通知搅拌楼要料") self.request_material_from_mixing_building() # 请求搅拌楼下料 return True + else: + if self.state._upper_door_position != 'over_lower': + self.state._upper_door_position = 'over_lower' return False def request_material_from_mixing_building(self): @@ -62,7 +64,7 @@ class FeedingController: 请求搅拌楼下料 """ print("发送要料请求至搅拌楼...") - # self.settings. + # self.process.return_upper_door_to_default() @@ -73,35 +75,35 @@ class FeedingController: def check_arch_blocking(self): """检查是否需要破拱""" current_time = time.time() - + # 检查下料斗破拱(只有在下料过程中才检查) - if self.state._lower_feeding_stage in [1, 2, 3]: # 在所有下料阶段检查 - lower_weight = self.transmitter_controller.read_data(2) - if lower_weight is not None: - # 检查重量变化是否过慢(小于0.1kg变化且时间超过10秒) - if (abs(lower_weight - self.state.last_lower_weight) < 0.1) and \ - (current_time - self.state.last_weight_time) > 10: - print("下料斗可能堵塞,启动破拱") - self.state._lower_is_arch_=True - self.relay_controller.control(self.relay_controller.BREAK_ARCH_LOWER, 'open') - time.sleep(2) - self.relay_controller.control(self.relay_controller.BREAK_ARCH_LOWER, 'close') - self.state._lower_is_arch_=False + # if self.state.lower_feeding_stage in [1, 2, 3]: # 在所有下料阶段检查 + # lower_weight = self.transmitter_controller.read_data(2) + # if lower_weight is not None: + # # 检查重量变化是否过慢(小于0.1kg变化且时间超过10秒) + # if (abs(lower_weight - self.state.last_lower_weight) < 0.1) and \ + # (current_time - self.state.last_weight_time) > 10: + # print("下料斗可能堵塞,启动破拱") + # self.state._lower_is_arch_=True + # self.relay_controller.control(self.relay_controller.BREAK_ARCH_LOWER, 'open') + # time.sleep(2) + # self.relay_controller.control(self.relay_controller.BREAK_ARCH_LOWER, 'close') + # self.state._lower_is_arch_=False - self.state.last_lower_weight = lower_weight + # self.state.last_lower_weight = lower_weight # 检查上料斗破拱(在上料斗向下料斗下料时检查) - if (self.state._upper_door_position == 'over_lower' and - self.state._lower_feeding_stage in [0, 1, 2, 3, 4]): # 在任何阶段都可能需要上料斗破拱 + if self.state._feed_status == FeedStatus.FUpperToLower: # 在任何阶段都可能需要上料斗破拱 + print('上料斗振动线程启用中...') upper_weight = self.transmitter_controller.read_data(1) if upper_weight is not None: - # 检查重量变化是否过慢(小于0.1kg变化且时间超过10秒) - if (abs(upper_weight - self.state.last_upper_weight) < 0.1) and \ - (current_time - self.state.last_weight_time) > 10: + # 检查重量变化是否过慢(小于0.1kg变化且时间超过10秒),觉得有点小。改成 + if (abs(upper_weight - self.state.last_upper_weight) < 100) and \ + (current_time - self.state.last_weight_time) > 5: print("上料斗可能堵塞,启动破拱") self.state._upper_is_arch_=True self.relay_controller.control(self.relay_controller.BREAK_ARCH_UPPER, 'open') - time.sleep(2) + time.sleep(5) self.relay_controller.control(self.relay_controller.BREAK_ARCH_UPPER, 'close') self.state._upper_is_arch_=False @@ -124,6 +126,7 @@ class FeedingController: print("无法获取当前角度,跳过本次调整") return self.state.last_angle = current_angle + self.state._lower_angle=current_angle print(f"当前角度: {current_angle:.2f}°, 溢料状态: {overflow}, 溢料返回: {self.state.overflow_detected}") return if self.state.overflow_detected!="浇筑满": @@ -135,17 +138,17 @@ class FeedingController: # 状态机控制逻辑 if self.state.angle_control_mode == "normal": - # 正常模式大于self.settings.angle_threshold=60度 - if overflow and current_angle > self.settings.angle_threshold: + # 正常模式大于app_set_config.angle_threshold=60度 + if overflow and current_angle > app_set_config.angle_threshold: # 检测到堆料且角度过大,进入角度减小模式 print("检测到堆料且角度过大,关闭出砼门开始减小角度") self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'close') self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE, 'open') self.state.angle_control_mode = "reducing" else: - # 保持正常开门 - self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'open') - if current_angle >self.settings.target_angle: + # 保持正常开门 30 + # self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'open') + if current_angle >app_set_config.target_angle: # 角度已降至目标范围,关闭出砼门 self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'close') self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE, 'open') @@ -155,7 +158,7 @@ class FeedingController: elif self.state.angle_control_mode == "reducing": # 角度减小模式 - if current_angle <= self.settings.target_angle + self.settings.angle_tolerance: + if current_angle <= app_set_config.target_angle + app_set_config.angle_tolerance: # 角度已达到目标范围 if overflow: # 仍有堆料,进入维持模式 @@ -196,9 +199,7 @@ class FeedingController: self.state.angle_control_mode = "normal" else: self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'close') - self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE, 'open') - time.sleep(5) - self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE, 'close') + self.relay_controller.control_lower_close() def pulse_control_door_for_maintaining(self): diff --git a/feeding/process.py b/feeding/process.py index 6da32ef..5e4d88d 100644 --- a/feeding/process.py +++ b/feeding/process.py @@ -5,11 +5,12 @@ from busisness.blls import ArtifactBll from busisness.models import ArtifactInfoModel import time from datetime import datetime +from config.settings import app_set_config class FeedingProcess: def __init__(self, relay_controller, inverter_controller, transmitter_controller, vision_detector, - camera_controller, state, settings): + camera_controller, state): self.relay_controller = relay_controller self.artifact_bll = ArtifactBll() self.mould_service = MouldService() @@ -18,50 +19,60 @@ class FeedingProcess: self.vision_detector = vision_detector self.camera_controller = camera_controller self.state = state - self.state.feed_status = FeedStatus.FNone + self.state._feed_status = FeedStatus.FCheckM #标志位用,是否是第一次运行 self.is_first_flag=True - self.settings = settings def start_feeding(self): loc_state=self.state + loc_state._upper_weight=self.transmitter_controller.read_data(1) + loc_state._lower_weight=self.transmitter_controller.read_data(2) """开始生产管片""" - if loc_state.feed_status == FeedStatus.FNone: - loc_state.feed_status = FeedStatus.FCheckM + if loc_state._feed_status == FeedStatus.FNone: + # loc_state._feed_status = FeedStatus.FCheckM return - elif loc_state.feed_status == FeedStatus.FCheckM: + elif loc_state._feed_status == FeedStatus.FCheckM: """开始生产管片""" - loc_state._lower_feeding_stage = 4 + loc_state.lower_feeding_stage = 4 - if self.settings.debug_feeding: - loc_state.feed_status = FeedStatus.FApiCheck + # if app_set_config.debug_feeding: + # loc_state._feed_status = FeedStatus.FApiCheck if self.state.vehicle_aligned: - loc_state.feed_status = FeedStatus.FCheckGB + loc_state._feed_status = FeedStatus.FApiCheck print("检查模车") return - elif loc_state.feed_status == FeedStatus.FApiCheck: + elif loc_state._feed_status == FeedStatus.FApiCheck: print("生产已开始") - time.sleep(2) + # time.sleep(2) loc_modules = self.mould_service.get_not_pour_artifacts() if loc_modules: # 取第一个未浇筑的管片 #后续放入队列处理 + loc_module = loc_modules[0] #API loc_module.Source = 1 loc_module.BeginTime=datetime.now() self.artifact_bll.insert_artifact_task(loc_module) - self.state.current_artifact = loc_module - + loc_state.current_artifact = loc_module + loc_state._mould_need_weight=loc_module.BetonVolume*self.state.density + print(f"已获取到未浇筑的管片:{loc_module.MouldCode}") # self.artifact_bll.finish_artifact_task(loc_state.current_artifact.ArtifactID,1.92) - self.state.feed_status = FeedStatus.FCheckGB + loc_state._feed_status = FeedStatus.FCheckGB else: #未读取到AIP接口数据. - self.state.current_artifact = None + print("未获取到未浇筑的管片") + loc_artifacting_task=self.artifact_bll.get_artifacting_task() + if loc_artifacting_task: + loc_state.current_artifact = loc_artifacting_task + loc_state._mould_need_weight=loc_artifacting_task.BetonVolume*self.state.density + loc_state._feed_status = FeedStatus.FCheckGB + else: + self.state.current_artifact = None return - elif loc_state.feed_status == FeedStatus.FRFID: + elif loc_state._feed_status == FeedStatus.FRFID: print("生产已检查RFID") #RFID格式:模具编号,分块号,尺寸规格,方量 rfid_info ='' @@ -71,7 +82,7 @@ class FeedingProcess: loc.BetonVolume=1.56 if self.state.current_artifact: #检测是否和RFID识别的管理一致 - self.state.feed_status = FeedStatus.FCheckGB + loc_state._feed_status = FeedStatus.FCheckGB else: #以RFID为准 loc_module= ArtifactInfoModel() @@ -82,23 +93,23 @@ class FeedingProcess: self.state.current_artifact = loc_module #确认是否保存到数据库 - self.state.feed_status = FeedStatus.FCheckGB + loc_state._feed_status = FeedStatus.FCheckGB return - elif loc_state.feed_status == FeedStatus.FCheckGB: + elif loc_state._feed_status == FeedStatus.FCheckGB: print("检查盖板对齐,") - time.sleep(10) - loc_state.feed_status = FeedStatus.FUpperToLower + # time.sleep(5) + loc_state._feed_status = FeedStatus.FUpperToLower #计算本次生产需要的总重量 print(f"本次生产需要的总重量:{self.state._mould_need_weight}") return - elif loc_state.feed_status == FeedStatus.FUpperToLower: + elif loc_state._feed_status == FeedStatus.FUpperToLower: print("上料斗向下料斗转移") #上料斗重量 - loc_state.initial_upper_weight=self.transmitter_controller.read_data(1) + loc_state.initial_upper_weight=loc_state._upper_weight #下料斗重量 - loc_state.initial_lower_weight=self.transmitter_controller.read_data(2) + loc_state.initial_lower_weight=loc_state._lower_weight #需要的总重量 - loc_state._mould_need_weight=loc_state.current_artifact.BetonVolume*loc_state.density + # loc_state._mould_need_weight=loc_state.current_artifact.BetonVolume*loc_state.density if loc_state._mould_need_weight > loc_state.initial_upper_weight + loc_state.initial_lower_weight: # 等待上料斗重量增加(多久不够报警,可能出现F块不足的情况) print('重量不够,需要增加') @@ -114,40 +125,46 @@ class FeedingProcess: # 最后一块F块,前面多要0.25,0.3,F块直接下料(先多下0.3后续) # loc_FWeight=0.3*loc_state.density # loc_feed_weight=loc_state.need_total_weight-loc_state.initial_lower_weight-loc_FWeight - self.transfer_material_from_upper_to_lower(loc_state.initial_upper_weight,loc_state.initial_lower_weight) + self.transfer_material_from_upper_to_lower(loc_state,loc_state.initial_upper_weight,loc_state.initial_lower_weight) #完成了上料斗重量转移才进入下料斗 - loc_state.feed_status = FeedStatus.FFeed1 + #测试返回 + # loc_state._feed_status = FeedStatus.FFeed1 + loc_state._feed_status = FeedStatus.FNone + + # time.sleep(10) return - elif loc_state.feed_status == FeedStatus.FFeed1: + elif loc_state._feed_status == FeedStatus.FFeed1: #下料 # self._start_feeding_stage() self.feeding_stage_one(loc_state) print("下料1") return - elif loc_state.feed_status == FeedStatus.FFeed2: + elif loc_state._feed_status == FeedStatus.FFeed2: #上料 # self._start_feeding_stage() self.feeding_stage_two(loc_state) print("下料2") return - elif loc_state.feed_status == FeedStatus.FFeed3: + elif loc_state._feed_status == FeedStatus.FFeed3: #下料 # self._start_feeding_stage() self.feeding_stage_three(loc_state) print("下料3") return - elif loc_state.feed_status == FeedStatus.FFinished: + elif loc_state._feed_status == FeedStatus.FFinished: """完成当前批次下料""" print("当前批次下料完成,关闭出砼门") if loc_state.overflow_detected=="浇筑满": self.inverter_controller.control('stop') - self.relay_controller.control(self.relay_controller.DOOR_LOWER_1, 'close') - self.relay_controller.control(self.relay_controller.DOOR_LOWER_2, 'close') + + loc_state._mould_vibrate_status=0 + self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'close') + self.relay_controller.control_upper_close() #更新数据库状态 self.artifact_bll.finish_artifact_task(loc_state.current_artifact.ArtifactID,loc_state._mould_finish_weight/loc_state.density) - loc_state.feed_status = FeedStatus.FCheckM + loc_state._feed_status = FeedStatus.FCheckM return def _start_feeding_stage(self): @@ -156,34 +173,35 @@ class FeedingProcess: print("开始分步下料过程") self.transfer_material_from_upper_to_lower() - def transfer_material_from_upper_to_lower(self,initial_upper_weight,initial_lower_weight): + def transfer_material_from_upper_to_lower(self,loc_state,initial_upper_weight,initial_lower_weight): """target_upper_weight:转移后剩下的上料斗重量""" # 如果低于单次,全部卸掉 - _max_lower_weight=self.settings.max_lower_volume*self.state.density - if (initial_lower_weight+feed_weight>_max_lower_weight): - feed_weight=_max_lower_weight-initial_lower_weight + _max_lower_weight=app_set_config.max_lower_volume*self.state.density + # if (initial_lower_weight+feed_weight>_max_lower_weight): + feed_weight=_max_lower_weight-initial_lower_weight target_upper_weight=initial_upper_weight-feed_weight target_upper_weight = max(target_upper_weight, 0) # 确保下料斗出砼门关闭 - self.relay_controller.control(self.relay_controller.DOOR_LOWER_2, 'close') + self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'close') + # self.relay_controller.control_lower_close() # 打开上料斗出砼门 - self.relay_controller.control(self.relay_controller.DOOR_LOWER_1, 'open') + self.relay_controller.control(self.relay_controller.DOOR_UPPER_OPEN, 'open') # 等待物料流入下料斗,基于上料斗重量变化控制 import time start_time = time.time() # timeout = 30 # 30秒超时 - while self.state.running: + while loc_state.running: current_upper_weight = self.transmitter_controller.read_data(1) # 如果无法读取重量,继续尝试 if current_upper_weight is None: print("无法读取上料斗重量,继续尝试...") time.sleep(1) continue - + loc_state._upper_weight=current_upper_weight print(f"上料斗当前重量: {current_upper_weight:.2f}kg") # 如果达到目标重量,则关闭上料斗出砼门 @@ -198,35 +216,40 @@ class FeedingProcess: print("重量变化过小,可能存在堵塞") time.sleep(1) # 关闭上料斗出砼门 - self.relay_controller.control(self.relay_controller.DOOR_LOWER_1, 'close') + self.relay_controller.control_upper_close() + + #测试用 print("上料斗下料完成") + def wait_for_vehicle_alignment(self): """等待模具车对齐""" print("等待模具车对齐...") - self.state._lower_feeding_stage = 4 + self.state.lower_feeding_stage = 4 import time - while self.state._lower_feeding_stage == 4 and self.state.running: + while self.state.lower_feeding_stage == 4 and self.state.running: if self.state.vehicle_aligned: print("模具车已对齐,开始下料") - self.state._lower_feeding_stage = 1 + self.state.lower_feeding_stage = 1 # self.feeding_stage_one() break - time.sleep(self.settings.alignment_check_interval) + time.sleep(app_set_config.alignment_check_interval) def feeding_stage_one(self,loc_state): """第一阶段下料:下料斗向模具车下料(低速)""" print("开始第一阶段下料:下料斗低速下料") if self.is_first_flag: - self.inverter_controller.set_frequency(self.settings.frequencies[0]) - self.inverter_controller.control('start') + # self.inverter_controller.set_frequency(app_set_config.frequencies[0]) + # self.inverter_controller.control('start') + loc_state._mould_frequency=app_set_config.frequencies[0] + loc_state._mould_vibrate_status=1 # 确保上料斗出砼门关闭 - self.relay_controller.control(self.relay_controller.DOOR_LOWER_1, 'close') + # self.relay_controller.control(self.relay_controller.DOOR_UPPER_CLOSE, 'close') # 打开下料斗出砼门 - self.relay_controller.control(self.relay_controller.DOOR_LOWER_2, 'open') - loc_cur_weight = self.transmitter_controller.read_data(2) + self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'open') + loc_cur_weight = loc_state._lower_weight if loc_cur_weight is None: #报警处理 print("无法获取初始重量,取消下料") @@ -238,7 +261,7 @@ class FeedingProcess: start_time = time.time() - current_weight = self.transmitter_controller.read_data(2) + current_weight = loc_state._lower_weight if current_weight is None: #报警处理 print("无法获取当前重量,取消下料") @@ -246,25 +269,28 @@ class FeedingProcess: return loc_state._mould_finish_weight=loc_state.initial_lower_weight-current_weight target_weight = loc_state._mould_need_weight/3 - - if (current_weight is not None and loc_state._mould_finish_weight >= target_weight) or (time.time() - start_time) > 30: - loc_state.feed_status = FeedStatus.FFeed2 - loc_state._lower_feeding_stage = 2 + # or (time.time() - start_time) > 30 + if (current_weight is not None and loc_state._mould_finish_weight >= target_weight): + loc_state._feed_status = FeedStatus.FFeed2 + loc_state.lower_feeding_stage = 2 self.is_first_flag=True return else: time.sleep(1) - def feeding_stage_two(self): + def feeding_stage_two(self,loc_state): """第二阶段下料:下料斗向模具车下料(中速)""" if self.is_first_flag: print("开始第二阶段下料:下料斗中速下料") - self.inverter_controller.set_frequency(self.settings.frequencies[1]) - + # self.inverter_controller.set_frequency(app_set_config.frequencies[1]) + # self.inverter_controller.control('start') # 保持下料斗出砼门打开 - self.relay_controller.control(self.relay_controller.DOOR_LOWER_2, 'open') + self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'open') # 确保上料斗出砼门关闭 - self.relay_controller.control(self.relay_controller.DOOR_LOWER_1, 'close') + self.relay_controller.control_upper_close() + + loc_state._mould_frequency=app_set_config.frequencies[1] + loc_state._mould_vibrate_status=1 # loc_cur_weight = self.transmitter_controller.read_data(2) # if loc_cur_weight is None: # #报警处理 @@ -275,7 +301,7 @@ class FeedingProcess: self.is_first_flag=False start_time = time.time() - current_weight = self.transmitter_controller.read_data(2) + current_weight = loc_state._lower_weight if current_weight is None: #报警处理 print("无法获取当前重量,取消下料") @@ -283,26 +309,29 @@ class FeedingProcess: return loc_state._mould_finish_weight=loc_state.initial_lower_weight-current_weight target_weight = (loc_state._mould_need_weight/3)*2 - - if (current_weight is not None and loc_state._mould_finish_weight >= target_weight) or (time.time() - start_time) > 30: - loc_state.feed_status = FeedStatus.FFeed3 - loc_state._lower_feeding_stage = 3 + # or (time.time() - start_time) > 30 + if (current_weight is not None and loc_state._mould_finish_weight >= target_weight): + loc_state._feed_status = FeedStatus.FFeed3 + loc_state.lower_feeding_stage = 3 self.is_first_flag=True return else: time.sleep(1) - def feeding_stage_three(self): + + def feeding_stage_three(self,loc_state): """第三阶段下料:下料斗向模具车下料(高速)""" if self.is_first_flag: print("开始第三阶段下料:下料斗高速下料") - self.inverter_controller.set_frequency(self.settings.frequencies[2]) + # self.inverter_controller.set_frequency(app_set_config.frequencies[2]) + + loc_state._mould_frequency=app_set_config.frequencies[2] # 保持下料斗出砼门打开 - self.relay_controller.control(self.relay_controller.DOOR_LOWER_2, 'open') + self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'open') # 确保上料斗出砼门关闭 - self.relay_controller.control(self.relay_controller.DOOR_LOWER_1, 'close') + self.relay_controller.control_upper_close() self.is_first_flag=False - current_weight = self.transmitter_controller.read_data(2) + current_weight = loc_state._lower_weight if current_weight is None: #报警处理 print("无法获取当前重量,取消下料") @@ -312,8 +341,8 @@ class FeedingProcess: target_weight = loc_state._mould_need_weight if (current_weight is not None and loc_state._mould_finish_weight >= target_weight): - loc_state.feed_status = FeedStatus.FFinished - loc_state._lower_feeding_stage = 5 + loc_state._feed_status = FeedStatus.FFinished + loc_state.lower_feeding_stage = 5 self.is_first_flag=True return else: @@ -348,13 +377,13 @@ class FeedingProcess: # 继续等待当前模具车对齐(不需要重新等待对齐,因为是同一辆模具车) print("第二次上料完成,继续三阶段下料") - self.state._lower_feeding_stage = 1 # 直接进入第一阶段下料 + self.state.lower_feeding_stage = 1 # 直接进入第一阶段下料 self.feeding_stage_one() # 开始第二轮第一阶段下料 def finish_feeding_process(self): """完成整个下料流程""" print("整个下料流程完成") - self.state._lower_feeding_stage = 0 + self.state.lower_feeding_stage = 0 self.state.lower_feeding_cycle = 0 self.state.upper_feeding_count = 0 # self.return_upper_door_to_default() diff --git a/hardware/RFID/command_hex.py b/hardware/RFID/command_hex.py index 3448350..ff38a5b 100644 --- a/hardware/RFID/command_hex.py +++ b/hardware/RFID/command_hex.py @@ -470,9 +470,10 @@ class command_hex: """ # 寻找连续三个0x2C的情况 - for idx in range(len(response) - 2): - if response[idx] == 0x2C and response[idx+1] == 0x2C and response[idx+2] == 0x2C: - response=response[:idx] + # for idx in range(len(response) - 2): + # if response[idx] == 0x2C and response[idx+1] == 0x2C and response[idx+2] == 0x2C: + # response=response[:idx] + # break # 验证响应长度 if len(response) <= 1 or len(response) != response[0] + 1: raise ValueError("应答数据长度不正确") @@ -499,7 +500,12 @@ class command_hex: loc_string = '' if data_part: try: + loc_string = data_part.decode('ascii') + first_empty = loc_string.find(',,,') + if first_empty != -1: + loc_string = loc_string[:first_empty] + print('收到数据:',loc_string) except UnicodeDecodeError: print(f"无法将数据转换为ASCII字符串: {data_part}") return loc_string diff --git a/hardware/RFID/rfid_service.py b/hardware/RFID/rfid_service.py index 6eaa2b3..87953a9 100644 --- a/hardware/RFID/rfid_service.py +++ b/hardware/RFID/rfid_service.py @@ -380,7 +380,7 @@ class rfid_service: 接收线程的主循环,用于接收RFID推送的数据 """ while self._thread_signal: - + self._pause_receive=False if self._pause_receive: time.sleep(1) continue diff --git a/hardware/relay.py b/hardware/relay.py index 39f6a5e..5f82b12 100644 --- a/hardware/relay.py +++ b/hardware/relay.py @@ -1,14 +1,16 @@ # hardware/relay.py import socket import binascii +import time +import threading from pymodbus.client import ModbusTcpClient from pymodbus.exceptions import ModbusException -from config.settings import Settings +from config.settings import app_set_config class RelayController: # 继电器映射 - FAST_STOP = 'fast_stop' # DO1 - 急停 + RING = 'ring' # DO1 - 响铃 UPPER_TO_JBL = 'upper_to_jbl' # DO2 - 上料斗到搅拌楼 UPPER_TO_ZD = 'upper_to_zd' # DO3 - 上料斗到振捣室 # DOOR_UPPER = 'door_upper' # DO0 - 上料斗滑动 @@ -20,38 +22,37 @@ class RelayController: BREAK_ARCH_LOWER = 'break_arch_lower' # DO4 - 下料斗震动 DIRECT_LOWER_FRONT = 'direct_lower_front' # DO5 - 下料斗前 DIRECT_LOWER_BEHIND = 'direct_lower_behind' # DO6 - 下料斗后 - DIRECT_LOWER_LEFT = 'direct_lower_left' # DO7 - 下料斗左 - DIRECT_LOWER_RIGHT = 'direct_lower_right' # DO8 - 下料斗右 + DIRECT_LOWER_TOP = 'direct_lower_top' # DO7 - 下料斗上 + DIRECT_LOWER_BELOW = 'direct_lower_below' # DO8 - 下料斗下 def __init__(self, host='192.168.250.62', port=50000): self.host = host self.port = port self.modbus_client = ModbusTcpClient(host, port=port) -#遥1 DO 7 左 DO8 右 角度 摇2:DO 12上 13下 14 往后 15往前 +#遥1 DO 7 左 DO8 右 角度 摇2:DO 15下 13上 12 往后 14往前 下料斗DO7开 D09关 # 继电器命令(原始Socket) self.relay_commands = { - self.FAST_STOP: {'open': '00000000000601050000FF00', 'close': '000000000006010500000000'}, + self.RING: {'open': '00000000000601050000FF00', 'close': '000000000006010500000000'}, self.UPPER_TO_JBL: {'open': '00000000000601050001FF00', 'close': '000000000006010500010000'}, self.UPPER_TO_ZD: {'open': '00000000000601050002FF00', 'close': '000000000006010500020000'}, self.DOOR_LOWER_OPEN: {'open': '00000000000601050006FF00', 'close': '00000000000601050006FF00'}, - self.DOOR_LOWER_CLOSE: {'open': '00000000000601050007FF00', 'close': '000000000006010500070000'}, + self.DOOR_LOWER_CLOSE: {'open': '00000000000601050008FF00', 'close': '000000000006010500080000'}, self.DOOR_UPPER_OPEN: {'open': '00000000000601050003FF00', 'close': '000000000006010500030000'}, self.DOOR_UPPER_CLOSE: {'open': '00000000000601050004FF00', 'close': '000000000006010500040000'}, self.BREAK_ARCH_UPPER: {'open': '0000000000060105000AFF00', 'close': '0000000000060105000A0000'}, self.BREAK_ARCH_LOWER: {'open': '00000000000601050005FF00', 'close': '000000000006010500050000'}, - self.DIRECT_LOWER_FRONT: {'open': '00000000000601050005FF00', 'close': '000000000006010500050000'}, - self.DIRECT_LOWER_BEHIND: {'open': '00000000000601050005FF00', 'close': '000000000006010500050000'}, - self.DIRECT_LOWER_LEFT: {'open': '00000000000601050005FF00', 'close': '000000000006010500050000'}, - self.DIRECT_LOWER_RIGHT: {'open': '00000000000601050005FF00', 'close': '000000000006010500050000'} + self.DIRECT_LOWER_FRONT: {'open': '0000000000060105000DFF00', 'close': '0000000000060105000D0000'}, + self.DIRECT_LOWER_BEHIND: {'open': '0000000000060105000BFF00', 'close': '0000000000060105000B0000'}, + self.DIRECT_LOWER_TOP: {'open': '0000000000060105000CFF00', 'close': '0000000000060105000C0000'}, + self.DIRECT_LOWER_BELOW: {'open': '0000000000060105000EFF00', 'close': '0000000000060105000E0000'} } - self.settings = Settings() # 读取状态命令 self.read_status_command = '000000000006010100000008' # 设备位映射 self.device_bit_map = { - self.FAST_STOP: 0, + self.RING: 0, self.UPPER_TO_JBL: 1, self.UPPER_TO_ZD: 2, self.BREAK_ARCH_UPPER: 3, @@ -60,7 +61,7 @@ class RelayController: def send_command(self, command_hex): """发送原始Socket命令""" - if not self.settings.debug_feeding: + if app_set_config.debug_mode: return None try: @@ -93,7 +94,40 @@ class RelayController: def control(self, device, action): """控制继电器""" if device in self.relay_commands and action in self.relay_commands[device]: - print(f"控制继电器 {device} {action}") + print(f"发送控制继电器命令 {device} {action}") self.send_command(self.relay_commands[device][action]) else: print(f"无效设备或动作: {device}, {action}") + + def control_upper_close(self): + """控制上料斗关""" + # 关闭上料斗出砼门 + self.control(self.DOOR_UPPER_CLOSE, 'open') + # 异步5秒后关闭 + threading.Thread(target=self._close_upper_5s, daemon=True,name="close_upper_5s").start() + + def control_lower_close(self): + """控制下料斗关""" + # 关闭下料斗出砼门 + self.control(self.DOOR_LOWER_CLOSE, 'open') + # 异步5秒后关闭 + threading.Thread(target=self._close_lower_5s, daemon=True,name="close_lower_5s").start() + + def control_ring_open(self): + """控制下料斗关""" + # 关闭下料斗出砼门 + self.control(self.RING, 'open') + # 异步5秒后关闭 + threading.Thread(target=self._close_ring, daemon=True,name="close_ring").start() + + def _close_upper_5s(self): + time.sleep(5) + self.control(self.DOOR_UPPER_CLOSE, 'close') + + def _close_lower_5s(self): + time.sleep(5) + self.control(self.DOOR_LOWER_CLOSE, 'close') + + def _close_ring(self): + time.sleep(3) + self.control(self.RING, 'close') diff --git a/hardware/transmitter.py b/hardware/transmitter.py index 09e2b66..cb764e4 100644 --- a/hardware/transmitter.py +++ b/hardware/transmitter.py @@ -1,11 +1,14 @@ # hardware/transmitter.py from pymodbus.exceptions import ModbusException +import socket +from config.ini_manager import ini_manager +from config.settings import app_set_config + class TransmitterController: def __init__(self, relay_controller): self.relay_controller = relay_controller - # 变送器配置 self.config = { 1: { # 上料斗 @@ -75,88 +78,57 @@ class TransmitterController: Args: transmitter_id 为1 表示上料斗, 为2 表示下料斗 return: 读取成功返回重量 weight: int, 失败返回 None """ + TIMEOUT = 2 # 超时时间为 2秒 + BUFFER_SIZE= 1024 + IP = None + PORT = None + weight = 0 + + if transmitter_id == 1: # 上料斗变送器的信息: - IP = "192.168.250.63" - PORT = 502 - TIMEOUT = 2 # 超时时间为 2秒 - BUFFER_SIZE= 1024 - weight = None - if self.relay_controller.settings.debug_feeding: - return 0 - - import socket - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - try: - s.settimeout(TIMEOUT) - s.connect((IP, PORT)) - # print(f"连接上料斗变送器 {IP}:{PORT} 成功") - - # 接收数据(变送器主动推送,recv即可获取数据) - data = s.recv(BUFFER_SIZE) - if data: - # print(f"收到原始数据:{data}") - - # 提取出完整的一个数据包 (\r\n结尾) - packet = self.get_latest_valid_packet(data) - if not packet: - print("未获取到有效数据包!!") - return None - # 解析重量 - weight = self.parse_weight(packet) - else: - print("未收到设备数据") - - except ConnectionRefusedError: - print(f"变送器连接失败:{IP}:{PORT} 拒绝连接(设备离线/端口错误)") - except socket.timeout: - print(f"读取变送器数据超时:{TIMEOUT}秒内未收到数据") - except Exception as e: - print(f"读取异常:{e}") - - # 成功返回重量(int),失败返回None - return weight - + IP = ini_manager.upper_transmitter_ip + PORT = ini_manager.upper_transmitter_port elif transmitter_id == 2: - # 上料斗变送器的信息: - IP = "192.168.250.66" - PORT = 8234 - TIMEOUT = 2 # 超时时间为 2秒 - BUFFER_SIZE= 1024 - weight = None - if self.relay_controller.settings.debug_feeding: - return 0 - import socket - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - try: - s.settimeout(TIMEOUT) - s.connect((IP, PORT)) - # print(f"连接上料斗变送器 {IP}:{PORT} 成功") - - # 接收数据(变送器主动推送,recv即可获取数据) - data = s.recv(BUFFER_SIZE) - if data: - # print(f"收到原始数据:{data}") - - # 提取出完整的一个数据包 (\r\n结尾) - packet = self.get_latest_valid_packet(data) - if not packet: - print("未获取到有效数据包!!") - return None - # 解析重量 - weight = self.parse_weight(packet) - else: - print("未收到设备数据") - - except ConnectionRefusedError: - print(f"变送器连接失败:{IP}:{PORT} 拒绝连接(设备离线/端口错误)") - except socket.timeout: - print(f"读取变送器数据超时:{TIMEOUT}秒内未收到数据") - except Exception as e: - print(f"读取异常:{e}") + # 下料斗变送器的信息: + IP = ini_manager.lower_transmitter_ip + PORT = ini_manager.lower_transmitter_port - # 成功返回重量(int),失败返回None - return weight + if not IP or not PORT: + print(f"未配置变送器 {transmitter_id} 的IP或PORT") + return 0 + if app_set_config.debug_mode: + print(f"调试模式,未读数据({transmitter_id},IP: {IP}:{PORT})") + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + try: + s.settimeout(TIMEOUT) + s.connect((IP, PORT)) + # print(f"连接上料斗变送器 {IP}:{PORT} 成功") + + # 接收数据(变送器主动推送,recv即可获取数据) + data = s.recv(BUFFER_SIZE) + if data: + # print(f"收到原始数据:{data}") + + # 提取出完整的一个数据包 (\r\n结尾) + packet = self.get_latest_valid_packet(data) + if not packet: + print("未获取到有效数据包!!") + return None + # 解析重量 + weight = self.parse_weight(packet) + else: + print("未收到设备数据") + + except ConnectionRefusedError: + print(f"变送器连接失败:{IP}:{PORT} 拒绝连接(设备离线/端口错误)") + except socket.timeout: + print(f"读取变送器数据超时:{TIMEOUT}秒内未收到数据") + except Exception as e: + print(f"读取异常:{e}") + + # 成功返回重量(int),失败返回None + return weight def get_latest_valid_packet(self, raw_data): """ diff --git a/main.py b/main.py index ee63ec2..670259c 100644 --- a/main.py +++ b/main.py @@ -1,17 +1,49 @@ # main.py import time -from config.settings import Settings +from config.settings import app_set_config from core.system import FeedingControlSystem +from hardware import relay +from hardware.relay import RelayController +import threading +import time def main(): # 加载配置 - settings = Settings() - # 初始化系统 - system = FeedingControlSystem(settings) - # system.camera_controller.start_cameras() + # relay_c=RelayController() + # self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'close') + system = FeedingControlSystem() + # system.relay_controller.control(system.relay_controller.DOOR_UPPER_OPEN, 'open') + + # system.relay_controller.control(system.relay_controller.DOOR_UPPER_CLOSE, 'close') + system.relay_controller.control(system.relay_controller.DOOR_UPPER_OPEN, 'open') + # system.relay_controller._close_upper_5s() + + # system.relay_controller.control(system.relay_controller.DOOR_UPPER_OPEN, 'open') + # system.state.vehicle_aligned=True + # system.state._upper_door_position='over_lower' + # system.initialize() + + # system.state._feed_status=FeedStatus.FCheckM + while True: + time.sleep(2) + + # system.camera_controller.start_cameras() + # loc_all_close=True + # if loc_all_close: + # relay=RelayController() + # relay.control(relay.UPPER_TO_JBL, 'close') + # relay.control(relay.UPPER_TO_ZD, 'close') + # relay.control(relay.DOOR_LOWER_OPEN, 'close') + # relay.control(relay.DOOR_LOWER_CLOSE, 'close') + # relay.control(relay.DOOR_UPPER_OPEN, 'close') + # relay.control(relay.DOOR_UPPER_CLOSE, 'close') + # relay.control(relay.BREAK_ARCH_UPPER, 'close') + # relay.control(relay.BREAK_ARCH_LOWER, 'close') + + # time.sleep(2) # system._alignment_check_loop() # system.relay_controller.control(system.relay_controller.DOOR_LOWER_OPEN, 'open') @@ -27,29 +59,17 @@ def main(): # time.sleep(5) # system.relay_controller.control(system.relay_controller.DOOR_LOWER_CLOSE, 'close') # system._visual_control_loop() + # system.transmitter_controller.test_upper_weight=2*2500 + # system.transmitter_controller.test_lower_weight=1000 + # system.state.vehicle_aligned=True + # # 启动调整线程 + # weight_thread = threading.Thread(target=adjust_weights, args=(system,), daemon=True) + # weight_thread.start() + # system.state._upper_door_position='over_lower' + # system._start_lower_feeding() + - try: - # 系统初始化 - # system.initialize() - - print("系统准备就绪,5秒后开始下料...") - time.sleep(5) - - # 启动下料流程 - # system.start_lower_feeding() - - # 保持运行 - while True: - time.sleep(1) - - except KeyboardInterrupt: - print("收到停止信号") - except Exception as e: - print(f"系统错误: {e}") - finally: - system.stop() - - + if __name__ == "__main__": main() diff --git a/opc/opcua_client_subscription.py b/opc/opcua_client_subscription.py new file mode 100644 index 0000000..ff896ed --- /dev/null +++ b/opc/opcua_client_subscription.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 +""" +OPC UA 客户端订阅模式示例 +使用订阅机制实现实时数据更新 +""" + +from opcua import Client, Subscription +from opcua.ua import DataChangeNotification +import time +import sys +import threading + +class SubHandler: + """ + 订阅处理器,处理数据变化通知 + """ + def __init__(self): + self.data_changes = {} + self.change_count = 0 + + def datachange_notification(self, node, val, data): + """ + 数据变化时的回调函数 + """ + self.change_count += 1 + node_name = node.get_display_name().Text + self.data_changes[node_name] = { + 'value': val, + 'timestamp': time.strftime('%Y-%m-%d %H:%M:%S'), + 'node_id': str(node) + } + + print(f"🔔 数据变化 #{self.change_count}") + print(f" 节点: {node_name}") + print(f" 数值: {val}") + print(f" 时间: {self.data_changes[node_name]['timestamp']}") + print(f" 节点ID: {node}") + print("-" * 50) + +class OPCUAClientSubscription: + """ + 使用订阅机制的 OPC UA 客户端 + """ + def __init__(self, server_url="opc.tcp://localhost:4840/zjsh_feed/server/"): + self.client = Client(server_url) + self.connected = False + self.subscription = None + self.handler = SubHandler() + self.monitored_nodes = {} + + def connect(self): + """连接到服务器""" + try: + self.client.connect() + self.connected = True + print(f"✅ 成功连接到 OPC UA 服务器: {self.client.server_url}") + return True + except Exception as e: + print(f"❌ 连接服务器失败: {e}") + return False + + def disconnect(self): + """断开连接""" + if self.subscription: + self.subscription.delete() + print("🗑️ 已删除订阅") + + if self.connected: + self.client.disconnect() + self.connected = False + print("🔌 已断开与 OPC UA 服务器的连接") + + def setup_subscription(self, publishing_interval=500): + """ + 设置订阅 + + Args: + publishing_interval: 发布间隔(毫秒) + """ + if not self.connected: + print("请先连接到服务器") + return False + + try: + # 创建订阅 + self.subscription = self.client.create_subscription(publishing_interval, self.handler) + print(f"📡 已创建订阅,发布间隔: {publishing_interval}ms") + + # 获取要监控的节点 + objects = self.client.get_objects_node() + upper_device = objects.get_child("2:upper") + lower_device = objects.get_child("2:lower") + + # 订阅重量数据 + upper_weight_node = upper_device.get_child("2:upper_weight") + lower_weight_node = lower_device.get_child("2:lower_weight") + + # 开始监控 + upper_handle = self.subscription.subscribe_data_change(upper_weight_node) + lower_handle = self.subscription.subscribe_data_change(lower_weight_node) + + self.monitored_nodes = { + 'upper_weight': {'node': upper_weight_node, 'handle': upper_handle}, + 'lower_weight': {'node': lower_weight_node, 'handle': lower_handle} + } + + print(f"📊 已订阅 {len(self.monitored_nodes)} 个数据节点") + return True + + except Exception as e: + print(f"❌ 设置订阅失败: {e}") + return False + + def get_current_values(self): + """获取当前值""" + if not self.monitored_nodes: + return {} + + values = {} + for name, info in self.monitored_nodes.items(): + try: + values[name] = info['node'].get_value() + except Exception as e: + values[name] = f"读取失败: {e}" + + return values + + def run_subscription_test(self, duration=30): + """ + 运行订阅测试 + + Args: + duration: 测试持续时间(秒) + """ + if not self.setup_subscription(): + return + + print(f"\n🚀 开始订阅模式测试,持续 {duration} 秒...") + print("💡 等待数据变化通知...") + print("=" * 60) + + start_time = time.time() + last_stats_time = start_time + + try: + while time.time() - start_time < duration: + current_time = time.time() + + # 每5秒显示一次统计信息 + if current_time - last_stats_time >= 5: + elapsed = current_time - start_time + changes_per_minute = (self.handler.change_count / elapsed) * 60 + + print(f"\n📈 统计信息 (运行时间: {elapsed:.1f}s)") + print(f" 总变化次数: {self.handler.change_count}") + print(f" 变化频率: {changes_per_minute:.1f} 次/分钟") + + if self.handler.data_changes: + print(f" 最新数据:") + for name, data in list(self.handler.data_changes.items())[-2:]: # 显示最后2个 + print(f" {name}: {data['value']} ({data['timestamp']})") + + last_stats_time = current_time + + time.sleep(0.1) # 小延迟避免CPU占用过高 + + except KeyboardInterrupt: + print("\n⏹️ 测试被用户中断") + + finally: + print(f"\n🏁 测试完成") + print(f"📊 总变化次数: {self.handler.change_count}") + print(f"⏱️ 平均变化频率: {(self.handler.change_count / duration) * 60:.1f} 次/分钟") + +def main(): + """主函数""" + client = OPCUAClientSubscription("opc.tcp://localhost:4840/zjsh_feed/server/") + + try: + # 连接到服务器 + if not client.connect(): + return + + # 运行订阅测试 + client.run_subscription_test(duration=60) # 运行60秒 + + except Exception as e: + print(f"❌ 客户端运行错误: {e}") + finally: + client.disconnect() + +if __name__ == "__main__": + if len(sys.argv) > 1: + server_url = sys.argv[1] + client = OPCUAClientSubscription(server_url) + else: + client = OPCUAClientSubscription() + + try: + main() + except KeyboardInterrupt: + print("\n👋 用户中断程序") + sys.exit(0) \ No newline at end of file diff --git a/opc/opcua_client_test.py b/opc/opcua_client_test.py index 303b193..852ef84 100644 --- a/opc/opcua_client_test.py +++ b/opc/opcua_client_test.py @@ -52,36 +52,30 @@ class OPCUAClientTest: objects = self.client.get_objects_node() print(f"对象节点: {objects}") - # 浏览 IndustrialDevice 节点 upper_device = objects.get_child("2:upper") - print(f"\n工业设备节点: {upper_device}") + print(f"\n上料斗对象: {upper_device}") - # 获取传感器节点 lower_device = objects.get_child("2:lower") - print(f"传感器节点: {lower_device}") + print(f"下料斗对象: {lower_device}") - - print(f"温度传感器: {upper_device}") - print(f"压力传感器: {lower_device}") - - # 获取变量值 - print("\n=== 当前传感器数据 ===") - self.read_sensor_values(upper_device, lower_device) + print("\n=== 当前对象属性===") + self.read_object_properties(upper_device, lower_device) except Exception as e: print(f"浏览节点时出错: {e}") - def read_sensor_values(self, upper_device, lower_device): - """读取传感器数值""" + def read_object_properties(self, upper_device, lower_device): + """读取重量数值""" try: - # 读取温度 - temp_value = upper_device.get_child("2:upper_weight").get_value() - temp_unit = upper_device.get_child("2:lower_weight").get_value() - print(f"温度: {temp_value} {temp_unit}") + # 读取重量 + upper_weight = upper_device.get_child("2:upper_weight").get_value() + lower_weight = lower_device.get_child("2:lower_weight").get_value() + print(f"上料斗重量: {upper_weight}") + print(f"下料斗重量: {lower_weight}") except Exception as e: - print(f"读取传感器数据时出错: {e}") + print(f"读取数据时出错: {e}") def monitor_data(self, duration=30): """监控数据变化""" diff --git a/opc/opcua_server.py b/opc/opcua_server.py index 98cbcff..52988b7 100644 --- a/opc/opcua_server.py +++ b/opc/opcua_server.py @@ -10,9 +10,10 @@ import random import threading from datetime import datetime from core.system import SystemState +from config.ini_manager import ini_manager class SimpleOPCUAServer: - def __init__(self, state, endpoint="opc.tcp://0.0.0.0:4840/zjsh_feed/server/", name="Feed_Server"): + def __init__(self, state, endpoint=ini_manager.opcua_endpoint, name="Feed_Server"): """ 初始化OPC UA服务器 @@ -54,8 +55,8 @@ class SimpleOPCUAServer: self.lower_weight = self.lower.add_variable(self.namespace, "lower_weight", 0.0) # 设置变量为可写 - self.upper_weight.set_writable() - self.lower_weight.set_writable() + # self.upper_weight.set_writable() + # self.lower_weight.set_writable() def setup_state_listeners(self): """设置状态监听器 - 事件驱动更新""" @@ -83,9 +84,7 @@ class SimpleOPCUAServer: try: self.server.start() self.running = True - print(f"OPC UA服务器启动成功!") print(f"服务器端点: opc.tcp://0.0.0.0:4840/freeopcua/server/") - print(f"命名空间: {self.namespace}") # 初始化当前值 if self.state: @@ -109,8 +108,6 @@ class SimpleOPCUAServer: def stop(self): """停止服务器""" self.running = False - if hasattr(self, 'simulation_thread'): - self.simulation_thread.join(timeout=2) self.server.stop() print("OPC UA服务器已停止") @@ -121,21 +118,6 @@ class SimpleOPCUAServer: except: pass - def simulate_data(self): - """模拟数据更新""" - while self.running: - try: - # 更新变量值 - self.upper_weight.set_value(self.state.upper_weight) - self.lower_weight.set_value(self.state.lower_weight) - - # 模拟延迟 - time.sleep(1) - - except Exception as e: - print(f"数据更新错误: {e}") - continue - def main(): """主函数""" diff --git a/service/mould_service.py b/service/mould_service.py index 86263c4..028adec 100644 --- a/service/mould_service.py +++ b/service/mould_service.py @@ -2,7 +2,7 @@ from datetime import datetime, timedelta from common.sqlite_handler import SQLiteHandler from typing import Optional, List from .api_http_client import api_http_client -from busisness.models import ArtifactInfo, TaskInfo, LoginRequest +from busisness.models import ArtifactInfo, TaskInfo, LoginRequest, LEDInfo from config.ini_manager import ini_manager @@ -80,59 +80,114 @@ class MouldService: except Exception as e: print(f"请求未浇筑管片信息异常: {e}") return None - - -if __name__ == "__main__": - # 创建模具服务实例 - mould_service = MouldService() - db = SQLiteHandler.get_instance("db/three.db", max_readers=50, busy_timeout=4000) + def get_pouring_led(self) -> Optional[LEDInfo]: + """ + 获取生产动态信息 + + Returns: + LEDInfo对象,如果失败返回None + """ + url = f"{self._host}/api/ext/produce/pouring_led" + + try: + # 调用API获取数据 + response_data = self._api_client.get(url, auth=True) + + # 检查响应状态 + if response_data.get('Code') != 200: + print(f"获取生产动态信息失败: {response_data.get('Message')}") + return None + + # 解析数据 + data = response_data.get('Data', {}) + if not data: + print(f"未获取到 pouring_led 信息") + return None + + # 转换为管片信息对象列表 - 使用更安全的字段过滤方式 + # 只提取 LEDInfo 类中定义的字段,忽略多余字段和缺失字段 + led_info = LEDInfo( + TaskID=data.get('TaskID', ''), + PlateVolume=data.get('PlateVolume', ''), + MouldCode=data.get('MouldCode', ''), + ProduceStartTime=data.get('ProduceStartTime', ''), + ArtifactID=data.get('ArtifactID', ''), + RingTypeCode=data.get('RingTypeCode', ''), + PlateIDSerial=data.get('PlateIDSerial', ''), + CheckResult=data.get('CheckResult', ''), + UpperWeight=data.get('UpperWeight', 0.0), + Temper=data.get('Temper', ''), + WorkshopTemperature=data.get('WorkshopTemperature', ''), + LowBucketWeighingValue=data.get('LowBucketWeighingValue', ''), + VibrationFrequency=data.get('VibrationFrequency', ''), + TotMete=data.get('TotMete', ''), + BetonVolumeAlready=data.get('BetonVolumeAlready', ''), + WaterTemperature=data.get('WaterTemperature', ''), + FormulaProportion=data.get('FormulaProportion', '') + ) + return led_info + + except Exception as e: + print(f"请求 pouring_led 信息异常: {e}") + return None + +app_web_service = MouldService() + +# if __name__ == "__main__": +# # 创建模具服务实例 +# mould_service = MouldService() +# led_info = mould_service.get_pouring_led() +# if led_info: +# print(led_info) + + # db = SQLiteHandler.get_instance("db/three.db", max_readers=50, busy_timeout=4000) # 测试获取未浇筑管片信息 - not_poured = mould_service.get_not_pour_artifacts() - if not_poured: - for item in not_poured: - artifact = db.fetch_one("SELECT * FROM ArtifactTask WHERE ArtifactID = ?", (item.ArtifactID,)) - if not artifact: - dict={ - "ArtifactID": item.ArtifactID, - "ArtifactActionID": item.ArtifactActionID, - "ArtifactIDVice1": item.ArtifactIDVice1, - "ProduceRingNumber": item.ProduceRingNumber, - "MouldCode": item.MouldCode, - "SkeletonID": item.SkeletonID, - "RingTypeCode": item.RingTypeCode, - "SizeSpecification": item.SizeSpecification, - "BuriedDepth": item.BuriedDepth, - "BlockNumber": item.BlockNumber, - "BetonVolume": item.BetonVolume, - "BetonTaskID": item.BetonTaskID, - "HoleRingMarking": item.HoleRingMarking, - "GroutingPipeMarking": item.GroutingPipeMarking, - "PolypropyleneFiberMarking": item.PolypropyleneFiberMarking, - "Status": 1, - "Source": 1 - } - db.insert("ArtifactTask", dict) + # not_poured = mould_service.get_not_pour_artifacts() + # if not_poured: + # for item in not_poured: + # artifact = db.fetch_one("SELECT * FROM ArtifactTask WHERE ArtifactID = ?", (item.ArtifactID,)) + # if not artifact: + # dict={ + # "ArtifactID": item.ArtifactID, + # "ArtifactActionID": item.ArtifactActionID, + # "ArtifactIDVice1": item.ArtifactIDVice1, + # "ProduceRingNumber": item.ProduceRingNumber, + # "MouldCode": item.MouldCode, + # "SkeletonID": item.SkeletonID, + # "RingTypeCode": item.RingTypeCode, + # "SizeSpecification": item.SizeSpecification, + # "BuriedDepth": item.BuriedDepth, + # "BlockNumber": item.BlockNumber, + # "BetonVolume": item.BetonVolume, + # "BetonTaskID": item.BetonTaskID, + # "HoleRingMarking": item.HoleRingMarking, + # "GroutingPipeMarking": item.GroutingPipeMarking, + # "PolypropyleneFiberMarking": item.PolypropyleneFiberMarking, + # "Status": 1, + # "Source": 1 + # } + # db.insert("ArtifactTask", dict) - dict={ - "TaskID": item.BetonTaskID, - "ProjectName": "上海市轨道交通19号线工程盾构区间管片生产2标", - "ProduceMixID": "20251030-02", - "VinNo": "", - "BetonVolume": item.BetonVolume, - "MouldCode": item.MouldCode, - "SkeletonID": item.SkeletonID, - "RingTypeCode": item.RingTypeCode, - "SizeSpecification": item.SizeSpecification, - "BuriedDepth": item.BuriedDepth, - "BlockNumber": item.BlockNumber, - "Mode": 1, - "Status": 1, - "Source": 1, - "OptTime": str(datetime.now() - timedelta(minutes=5)), - "CreateTime": str(datetime.now()) - } - db.insert("PDRecord", dict) + # dict={ + # "TaskID": item.BetonTaskID, + # "ProjectName": "上海市轨道交通19号线工程盾构区间管片生产2标", + # "ProduceMixID": "20251030-02", + # "VinNo": "", + # "BetonVolume": item.BetonVolume, + # "MouldCode": item.MouldCode, + # "SkeletonID": item.SkeletonID, + # "RingTypeCode": item.RingTypeCode, + # "SizeSpecification": item.SizeSpecification, + # "BuriedDepth": item.BuriedDepth, + # "BlockNumber": item.BlockNumber, + # "Mode": 1, + # "Status": 1, + # "Source": 1, + # "OptTime": str(datetime.now() - timedelta(minutes=5)), + # "CreateTime": str(datetime.now()) + # } + # db.insert("PDRecord", dict) # for i in range(2, 5): # row = db.fetch_one("SELECT * FROM ArtifactTask WHERE ID = ?", (i,)) # if row: diff --git a/settings.ini b/settings.ini index c491aa3..d7010b7 100644 --- a/settings.ini +++ b/settings.ini @@ -9,4 +9,9 @@ login_model = {"Program": 11, "SC": "1000000001", "loginName": "leduser", "passw [app] log_path = logs/app.log db_path = db/three.db +opcua_endpoint = opc.tcp://192.168.250.64:4840/zjsh_feed/server/ +upper_transmitter_ip = 192.168.250.63 +upper_transmitter_port = 502 +lower_transmitter_ip = 192.168.250.66 +lower_transmitter_port = 8234 diff --git a/tests/test_feeding_process.py b/tests/test_feeding_process.py index fd8f25b..c45fe5c 100644 --- a/tests/test_feeding_process.py +++ b/tests/test_feeding_process.py @@ -3,6 +3,7 @@ import unittest from unittest.mock import patch, MagicMock import sys import os +from config.settings import app_set_config # 添加项目根目录到Python路径 sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) @@ -40,13 +41,13 @@ class TestFeedingProcess(unittest.TestCase): patch('feeding.process.InverterController'), \ patch('feeding.process.TransmitterController'): system = FeedingProcess() - # 通过settings修改参数 system.settings.single_batch_weight = 1500 - system.settings.min_required_weight = 300 - system.settings.target_vehicle_weight = 3000 + #修改参数 app_set_config.single_batch_weight = 1500 + app_set_config.min_required_weight = 300 + app_set_config.target_vehicle_weight = 3000 - self.assertEqual(system.settings.target_vehicle_weight, 3000) - self.assertEqual(system.settings.min_required_weight, 300) - self.assertEqual(system.settings.single_batch_weight, 1500) + self.assertEqual(app_set_config.target_vehicle_weight, 3000) + self.assertEqual(app_set_config.min_required_weight, 300) + self.assertEqual(app_set_config.single_batch_weight, 1500) if __name__ == '__main__': diff --git a/tests/test_rfid.py b/tests/test_rfid.py index 75ef5eb..301bb4b 100644 --- a/tests/test_rfid.py +++ b/tests/test_rfid.py @@ -19,7 +19,7 @@ def test_rfid_functions(): 测试RFIDHardware的主要功能 """ # 初始化RFID控制器 - rfid = rfid_service(host='192.168.1.190', port=6000) + rfid = rfid_service(host='192.168.250.67', port=6000) # print("=== RFID硬件测试开始 ===") @@ -41,6 +41,12 @@ def test_rfid_functions(): # rfid.set_working_mode(address=0x00, mode_params={ # 'word_num': 0x1E # }) + + # mode_data = rfid.read_working_mode(address=0x00) + # if mode_data: + # print("读取到工作模式参数:") + # for key, value in mode_data.items(): + # print(f" {key}: {value:02X} ({value})") # # 测试读取读写器信息 # print("\n3. 测试读取读写器信息:") @@ -54,7 +60,7 @@ def test_rfid_functions(): # 测试设置功率 (仅演示,实际使用时根据需要调整) # print("\n3. 测试设置功率 (演示,不实际执行):") - # power_success = rfid.set_power(address=0x00, power_value=6) + # power_success = rfid.set_power(address=0x00, power_value=16) # print(f"功率设置{'成功' if power_success else '失败'}") # # 测试设置读卡间隔 (仅演示) @@ -87,7 +93,7 @@ def test_rfid_functions(): # rfid._process_collected_data() rfid.start_receiver(callback=test_data_callback) # print("接收线程已启动,等待接收数据...") - # 等待5秒模拟接收过程 + # 等待5秒模拟接收过程1111111111111 time.sleep(60*60) finally: # 确保停止接收线程 diff --git a/vision/camera.py b/vision/camera.py index 540a780..664108e 100644 --- a/vision/camera.py +++ b/vision/camera.py @@ -62,6 +62,30 @@ class DualCameraController: # print('aaaaa') ret, frame = cap.read() if ret and frame is not None: + # 在帧右上角添加时间戳 + current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3] + # 获取帧尺寸 + height, width = frame.shape[:2] + # 设置文字参数 + font = cv2.FONT_HERSHEY_SIMPLEX + font_scale = 0.6 + thickness = 2 + color = (0, 255, 0) # 绿色 + + # 计算文字位置(右上角) + text_size = cv2.getTextSize(current_time, font, font_scale, thickness)[0] + text_x = width - text_size[0] - 10 # 距离右边10像素 + text_y = 30 # 距离顶部30像素 + + # 添加文字背景(半透明) + overlay = frame.copy() + cv2.rectangle(overlay, (text_x - 5, text_y - text_size[1] - 5), + (text_x + text_size[0] + 5, text_y + 5), (0, 0, 0), -1) + cv2.addWeighted(overlay, 0.7, frame, 0.3, 0, frame) + + # 添加时间戳文字 + cv2.putText(frame, current_time, (text_x, text_y), font, font_scale, color, thickness) + # 使用高精度时间戳 timestamp = time.time() # 检查队列是否已满 @@ -216,11 +240,11 @@ class DualCameraController: dt_t1, frame_latest = self.frame_queues['cam1'].queue[-1] # 获取cam2的最新帧,选择时间戳更新的那个 - if frame_latest is None: - if not self.frame_queues['cam2'].empty(): - dt_t2, frame2 = self.frame_queues['cam2'].queue[-1] - if dt_t1 is None or dt_t2 > dt_t1: - frame_latest = frame2 + # if frame_latest is None: + if not self.frame_queues['cam2'].empty(): + dt_t2, frame2 = self.frame_queues['cam2'].queue[-1] + if dt_t1 is None or dt_t2 > dt_t1: + frame_latest = frame2 # 返回最新帧的副本(如果找到) return frame_latest.copy() if frame_latest is not None else None diff --git a/vision/detector.py b/vision/detector.py index d1568f0..d87c5a7 100644 --- a/vision/detector.py +++ b/vision/detector.py @@ -4,11 +4,12 @@ import cv2 from vision.angle_detector import get_current_door_angle from vision.overflow_detector import detect_overflow_from_image from vision.alignment_detector import detect_vehicle_alignment +from config.settings import app_set_config class VisionDetector: - def __init__(self, settings): - self.settings = settings + def __init__(self): + pass #model路径在对应的模型里面 # self.alignment_model = os.path.join(current_dir, "align_model/yolov11_cls_640v6.rknn") @@ -21,36 +22,36 @@ class VisionDetector: # 加载夹角检测模型 try: - if not os.path.exists(self.settings.angle_model_path): - print(f"夹角检测模型不存在: {self.settings.angle_model_path}") + if not os.path.exists(app_set_config.angle_model_path): + print(f"夹角检测模型不存在: {app_set_config.angle_model_path}") success = False else: # 注意:angle.pt模型通过predict_obb_best_angle函数使用,不需要预加载 - print(f"夹角检测模型路径: {self.settings.angle_model_path}") + print(f"夹角检测模型路径: {app_set_config.angle_model_path}") except Exception as e: print(f"检查夹角检测模型失败: {e}") success = False # 加载堆料检测模型 try: - if not os.path.exists(self.settings.overflow_model_path): - print(f"堆料检测模型不存在: {self.settings.overflow_model_path}") + if not os.path.exists(app_set_config.overflow_model_path): + print(f"堆料检测模型不存在: {app_set_config.overflow_model_path}") success = False else: - self.overflow_model = YOLO(self.settings.overflow_model_path) - print(f"成功加载堆料检测模型: {self.settings.overflow_model_path}") + self.overflow_model = YOLO(app_set_config.overflow_model_path) + print(f"成功加载堆料检测模型: {app_set_config.overflow_model_path}") except Exception as e: print(f"加载堆料检测模型失败: {e}") success = False # 加载对齐检测模型 try: - if not os.path.exists(self.settings.alignment_model_path): - print(f"对齐检测模型不存在: {self.settings.alignment_model_path}") + if not os.path.exists(app_set_config.alignment_model_path): + print(f"对齐检测模型不存在: {app_set_config.alignment_model_path}") success = False else: - self.alignment_model = YOLO(self.settings.alignment_model_path) - print(f"成功加载对齐检测模型: {self.settings.alignment_model_path}") + self.alignment_model = YOLO(app_set_config.alignment_model_path) + print(f"成功加载对齐检测模型: {app_set_config.alignment_model_path}") except Exception as e: print(f"加载对齐检测模型失败: {e}") success = False @@ -64,7 +65,7 @@ class VisionDetector: # image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) image=cv2.flip(image, 0) return get_current_door_angle( - model=self.settings.angle_model_path, + model=app_set_config.angle_model_path, image=image ) @@ -72,21 +73,21 @@ class VisionDetector: """ 通过图像检测是否溢料 """ - # image_array=cv2.flip(image_array, 0) + image_array=cv2.flip(image_array, 0) # cv2.imwrite('test.jpg', image_array) - cv2.namedWindow("Alignment", cv2.WINDOW_NORMAL) - cv2.resizeWindow("Alignment", 640, 480) - cv2.imshow("Alignment", image_array) - cv2.waitKey(1) - print('path:', self.settings.overflow_model_path) - print('roi:', self.settings.roi_file_path) + # cv2.namedWindow("Alignment", cv2.WINDOW_NORMAL) + # cv2.resizeWindow("Alignment", 640, 480) + # cv2.imshow("Alignment", image_array) + # cv2.waitKey(1) + # print('path:', app_set_config.overflow_model_path) + # print('roi:', app_set_config.roi_file_path) return detect_overflow_from_image( - self.settings.overflow_model_path, - self.settings.roi_file_path, + app_set_config.overflow_model_path, + app_set_config.roi_file_path, image_array ) - def detect_vehicle_alignment(self, image_array): + def detect_vehicle_alignment(self, image_array)->bool: """ 通过图像检测模具车是否对齐 """ diff --git a/vision/roi_coordinates/1_rois.txt b/vision/roi_coordinates/1_rois.txt index bb8f71d..c9a20a1 100644 --- a/vision/roi_coordinates/1_rois.txt +++ b/vision/roi_coordinates/1_rois.txt @@ -1 +1 @@ -859,810,696,328 +644,608,522,246