This commit is contained in:
2025-11-21 14:55:52 +08:00
parent e3ecd0550f
commit 26ed8df502
36 changed files with 908 additions and 433 deletions

2
.gitignore vendored
View File

@ -37,3 +37,5 @@ PySide2_Fluent_Widgets.egg-info/
__pycache__
/core/__pycache__
/vision/__pycache__
/opcua/__pycache__
/feeding/__pycache__

View File

@ -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:

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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):

View File

@ -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)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

Binary file not shown.

BIN
doc/控制程序对接.docx Normal file

Binary file not shown.

Binary file not shown.

View File

@ -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):

View File

@ -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.3F块直接下料先多下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()

View File

@ -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

View File

@ -380,7 +380,7 @@ class rfid_service:
接收线程的主循环用于接收RFID推送的数据
"""
while self._thread_signal:
self._pause_receive=False
if self._pause_receive:
time.sleep(1)
continue

View File

@ -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 右 角度 摇2DO 12上 13 14 往后 15往前
#遥1 DO 7 左 DO8 右 角度 摇2DO 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')

View File

@ -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):
"""

74
main.py
View File

@ -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()

View File

@ -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)

View File

@ -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):
"""监控数据变化"""

View File

@ -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():
"""主函数"""

View File

@ -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:

View File

@ -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

View File

@ -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__':

View File

@ -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:
# 确保停止接收线程

View File

@ -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

View File

@ -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:
"""
通过图像检测模具车是否对齐
"""

View File

@ -1 +1 @@
859,810,696,328
644,608,522,246