241 lines
11 KiB
Python
241 lines
11 KiB
Python
|
||
from config.settings import app_set_config
|
||
from hardware.relay import RelayController
|
||
import time
|
||
import threading
|
||
from datetime import datetime
|
||
|
||
class VisualCallback:
|
||
# 类变量,用于存储实例引用,实现单例检测
|
||
_instance = None
|
||
_lock = threading.Lock()
|
||
|
||
def __new__(cls):
|
||
"""检测实例是否存在,实现单例模式"""
|
||
with cls._lock:
|
||
if cls._instance is None:
|
||
cls._instance = super().__new__(cls)
|
||
return cls._instance
|
||
|
||
def __init__(self):
|
||
"""初始化视觉回调处理器"""
|
||
# 避免重复初始化
|
||
if hasattr(self, '_initialized') and self._initialized:
|
||
return
|
||
|
||
self.angle_mode = "normal"
|
||
self.relay_controller = RelayController()
|
||
self.init_weight = 100
|
||
self.mould_finish_weight = 0
|
||
self.mould_need_weight = 100
|
||
self.finish_count = 0
|
||
self.overflow = False
|
||
|
||
# 线程安全的参数传递
|
||
self._current_angle = None
|
||
self._overflow_detected = None
|
||
self._new_data_available = threading.Event()
|
||
self._is_processing = threading.Lock()
|
||
self._stop_event = threading.Event()
|
||
|
||
# 创建并启动单个持续运行的线程
|
||
self.callback_thread = threading.Thread(
|
||
target=self._run_thread_loop,
|
||
daemon=True
|
||
)
|
||
self.callback_thread.start()
|
||
|
||
self._initialized = True
|
||
|
||
def angle_visual_callback(self, current_angle, overflow_detected):
|
||
"""
|
||
视觉控制主逻辑,供外部推送数据
|
||
使用单个持续运行的线程,通过参数设置传递数据
|
||
如果线程正在处理数据,则丢弃此次推送
|
||
"""
|
||
print(f"{datetime.now().strftime('%H:%M:%S.%f')[:-3]} 回调:{current_angle}")
|
||
# 尝试获取处理锁,若失败则说明正在处理,丢弃数据
|
||
if not self._is_processing.acquire(blocking=False):
|
||
print("回调线程仍在执行,丢弃此次推送数据")
|
||
return
|
||
|
||
try:
|
||
# 更新参数
|
||
self._current_angle = current_angle
|
||
if overflow_detected is not None:
|
||
self._overflow_detected = overflow_detected
|
||
# 通知线程有新数据可用
|
||
self._new_data_available.set()
|
||
finally:
|
||
# 释放处理锁
|
||
self._is_processing.release()
|
||
|
||
|
||
def _run_thread_loop(self):
|
||
"""
|
||
线程主循环,持续运行
|
||
等待新数据,然后调用处理方法
|
||
"""
|
||
while not self._stop_event.is_set():
|
||
# 等待新数据可用
|
||
self._new_data_available.wait()
|
||
|
||
# 重置事件
|
||
self._new_data_available.clear()
|
||
|
||
# 获取当前参数(使用临时变量避免被其他线程修改)
|
||
current_angle = self._current_angle
|
||
overflow_detected = self._overflow_detected
|
||
|
||
# 处理数据
|
||
self._process_angle_callback(current_angle, overflow_detected)
|
||
time.sleep(0.5)
|
||
|
||
|
||
|
||
|
||
def __del__(self):
|
||
self.relay_controller.close_all()
|
||
|
||
def _process_angle_callback(self, current_angle, overflow_detected):
|
||
"""
|
||
内部方法,实际处理视觉回调逻辑
|
||
在异步线程中执行
|
||
"""
|
||
try:
|
||
# print('current_angle:', current_angle, 'overflow_detected:', overflow_detected)
|
||
# return
|
||
# 检测溢出状态
|
||
print(f"{self.angle_mode}")
|
||
self.overflow = overflow_detected in ["大堆料", "小堆料"]
|
||
if current_angle is None:
|
||
print("无法获取当前角度,跳过本次调整")
|
||
return
|
||
|
||
print(f"{datetime.now().strftime('%H:%M:%S.%f')[:-3]} 当前角度: {current_angle:.2f}°")
|
||
|
||
if True:
|
||
# 状态机控制逻辑
|
||
if self.angle_mode == "normal":
|
||
# 正常模式大于app_set_config.angle_threshold=60度
|
||
if self.overflow:
|
||
self.angle_mode = "reducing"
|
||
else:
|
||
# 保持正常开门
|
||
# print(f'当前重量:{self.mould_finish_weight:.2f}kg, 目标重量:{self.mould_need_weight:.2f}kg')
|
||
if self.mould_need_weight > 0:
|
||
if self.mould_finish_weight / self.mould_need_weight >= 0.8:
|
||
print(f"完成重量占比{self.mould_finish_weight/self.mould_need_weight:.2f},半开出砼门")
|
||
# 半开出砼门
|
||
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')
|
||
time.sleep(0.3)
|
||
self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE, 'close')
|
||
else:
|
||
self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE, 'close')
|
||
self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'open')
|
||
time.sleep(0.32)
|
||
self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'close')
|
||
else:
|
||
# 全开砼门
|
||
if current_angle > app_set_config.angle_threshold:
|
||
#self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE,'close')
|
||
self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'close')
|
||
else:
|
||
self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE,'close')
|
||
self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN,'open')
|
||
else:
|
||
# 全开砼门
|
||
if current_angle > app_set_config.angle_threshold:
|
||
self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'close')
|
||
else:
|
||
self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'open')
|
||
elif self.angle_mode == "reducing":
|
||
# 角度减小模式
|
||
if self.overflow:
|
||
if current_angle <= app_set_config.target_angle:
|
||
# 角度已达到目标范围,仍有堆料,进入维持模式
|
||
print(f"角度已降至{current_angle:.2f}°,仍有堆料,进入维持模式")
|
||
if current_angle <= app_set_config.min_angle:
|
||
self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE, 'close')
|
||
self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'open')
|
||
time.sleep(0.1)
|
||
self.angle_mode = "maintaining"
|
||
else:
|
||
print(f"角度大于30,继续关闭")
|
||
self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'close')
|
||
self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE, 'open')
|
||
else:
|
||
# 无堆料,恢复正常模式
|
||
print(f"角度已降至{current_angle:.2f}°,无堆料,恢复正常模式")
|
||
self.angle_mode = "normal"
|
||
|
||
elif self.angle_mode == "maintaining":
|
||
# 维持模式 - 使用脉冲控制
|
||
if not self.overflow:
|
||
# 堆料已消除,恢复正常模式
|
||
print("堆料已消除,恢复正常模式")
|
||
self.angle_mode = "normal"
|
||
else:
|
||
# 继续维持角度控制
|
||
print("进入维持模式")
|
||
# 关门时间
|
||
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')
|
||
#time.sleep(0.3)
|
||
# self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE, 'close')
|
||
else:
|
||
self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE, 'close')
|
||
self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'open')
|
||
#time.sleep(0.32)
|
||
# self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'close')
|
||
|
||
elif self.angle_mode == "recovery":
|
||
# 恢复模式 - 逐步打开门
|
||
if self.overflow:
|
||
# 又出现堆料,回到角度减小模式
|
||
print("恢复过程中又检测到堆料,回到角度减小模式")
|
||
self.angle_mode = "maintaining"
|
||
else:
|
||
# 堆料已消除,恢复正常模式
|
||
print("堆料已消除,恢复正常模式")
|
||
self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'open')
|
||
self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE, 'close')
|
||
self.angle_mode = "normal"
|
||
# else:
|
||
# 浇筑满,关闭下料门
|
||
# self.relay_controller.control_lower_close()
|
||
except Exception as e:
|
||
print(f"处理视觉回调时发生异常: {e}")
|
||
|
||
|
||
@classmethod
|
||
def instance_exists(cls):
|
||
"""检测实例是否存在"""
|
||
return cls._instance is not None
|
||
|
||
def shutdown(self):
|
||
"""关闭线程,清理资源"""
|
||
# 设置停止事件
|
||
self._stop_event.set()
|
||
# 唤醒线程以便它能检测到停止事件
|
||
self._new_data_available.set()
|
||
# 等待线程结束
|
||
if self.callback_thread.is_alive():
|
||
self.callback_thread.join(timeout=1.0)
|
||
|
||
# 创建默认实例
|
||
visual_callback_instance = VisualCallback()
|
||
|
||
# 兼容层,保持原来的函数调用方式可用
|
||
def angle_visual_callback(current_angle, overflow_detected):
|
||
"""
|
||
兼容旧版本的函数调用方式
|
||
将调用转发到默认实例的angle_visual_callback方法
|
||
"""
|
||
visual_callback_instance.angle_visual_callback(current_angle, overflow_detected)
|