2025-11-06 10:55:29 +08:00
|
|
|
|
from PySide6.QtCore import QTimer, Signal, QObject, Slot
|
|
|
|
|
|
import threading
|
2026-04-07 16:07:36 +08:00
|
|
|
|
# from hardware.transmitter import TransmitterController
|
|
|
|
|
|
# from hardware.relay import RelayController
|
2025-11-06 10:55:29 +08:00
|
|
|
|
from view.widgets.hopper_widget import HopperWidget
|
|
|
|
|
|
from view.widgets.conveyor_system_widget import ConveyorSystemWidget
|
2026-01-11 18:00:32 +08:00
|
|
|
|
from enum import Enum
|
|
|
|
|
|
|
|
|
|
|
|
class UpperHopperPosition(Enum):
|
|
|
|
|
|
"""上料斗位置
|
2026-04-07 16:07:36 +08:00
|
|
|
|
- MIXING_TOWER: 搅拌楼处,对应数值 1
|
|
|
|
|
|
- VIBRATION_CHAMBER: 振捣室处,对应数值 2
|
|
|
|
|
|
- IN_TRANSIT: 途中,对应数值 3
|
2026-01-11 18:00:32 +08:00
|
|
|
|
"""
|
2026-04-07 16:07:36 +08:00
|
|
|
|
MIXING_TOWER = 1 # 搅拌楼处
|
|
|
|
|
|
VIBRATION_CHAMBER = 2 # 振捣室处
|
|
|
|
|
|
IN_TRANSIT = 3 # 运输途中
|
2025-11-06 10:55:29 +08:00
|
|
|
|
|
|
|
|
|
|
# 信号类:后台线程向主线程传递数据
|
|
|
|
|
|
class HopperSignals(QObject):
|
|
|
|
|
|
upper_weight_updated = Signal(int) # 上料斗重量更新信号
|
|
|
|
|
|
|
|
|
|
|
|
class HopperController:
|
|
|
|
|
|
def __init__(self, hopper_view:HopperWidget, conveyor_view:ConveyorSystemWidget):
|
|
|
|
|
|
self.hopper_view = hopper_view
|
|
|
|
|
|
self.conveyor_view = conveyor_view # 控制传送带中的上料斗
|
|
|
|
|
|
|
|
|
|
|
|
self.signals = HopperSignals() # 信号
|
|
|
|
|
|
|
|
|
|
|
|
# 下料斗夹爪测试用例数据
|
|
|
|
|
|
# 注意:目前只控制 下料斗的夹爪角度变化
|
2026-01-11 18:00:32 +08:00
|
|
|
|
self.angle = 10.33 # 夹爪当前角度
|
|
|
|
|
|
self.max_angle = 60.00 # 夹爪最大张开角度
|
|
|
|
|
|
self.min_angle = 10.00 # 夹爪最小张开角度
|
2025-11-06 10:55:29 +08:00
|
|
|
|
self.is_add = True # 角度增加/减小 控制
|
|
|
|
|
|
self.timer_angle = QTimer() # 角度更新定时器
|
|
|
|
|
|
self.timer_angle.setInterval(1000) # 1秒更新一次角度
|
|
|
|
|
|
|
|
|
|
|
|
# 重量读取定时器
|
2026-01-11 18:00:32 +08:00
|
|
|
|
# self.timer_weight = QTimer() # 重量读取定时器
|
|
|
|
|
|
# self.timer_weight.setInterval(2000) # 每2秒读取一次重量
|
2025-11-06 10:55:29 +08:00
|
|
|
|
|
|
|
|
|
|
# 绑定信号
|
|
|
|
|
|
self._connect_signals()
|
|
|
|
|
|
|
|
|
|
|
|
# 开启定时器
|
2026-04-07 16:07:36 +08:00
|
|
|
|
# self.timer_angle.start()
|
2026-01-11 18:00:32 +08:00
|
|
|
|
# self.timer_weight.start()
|
2025-11-06 10:55:29 +08:00
|
|
|
|
|
|
|
|
|
|
def _connect_signals(self):
|
|
|
|
|
|
# 更新上料斗重量
|
|
|
|
|
|
self.signals.upper_weight_updated.connect(self.onUpdateUpperHopperWeight)
|
|
|
|
|
|
|
|
|
|
|
|
# 上料斗重量定时读取
|
2026-01-11 18:00:32 +08:00
|
|
|
|
# self.timer_weight.timeout.connect(self.handleReadUpperHopperWeight)
|
2025-11-06 10:55:29 +08:00
|
|
|
|
|
|
|
|
|
|
# 下料斗夹爪定时更新
|
|
|
|
|
|
self.timer_angle.timeout.connect(self.handleLowerClampAngleUpdate)
|
|
|
|
|
|
|
|
|
|
|
|
# 上料斗 "开"按钮点击
|
|
|
|
|
|
self.hopper_view.upper_open_btn.clicked.connect(self.onUpperClampOpenBottonClicked)
|
|
|
|
|
|
|
2026-04-07 16:07:36 +08:00
|
|
|
|
# 上料斗 "破拱"按钮点击
|
|
|
|
|
|
self.hopper_view.upper_arch_breaking_signal.connect(self.onUpperArchBreakingClicked)
|
2025-11-06 10:55:29 +08:00
|
|
|
|
|
|
|
|
|
|
# 下料斗 "开"按钮点击
|
|
|
|
|
|
self.hopper_view.lower_open_btn.clicked.connect(self.onLowerClampOpenBottonClicked)
|
|
|
|
|
|
|
2026-04-07 16:07:36 +08:00
|
|
|
|
# 下料斗 "破拱"按钮点击
|
|
|
|
|
|
self.hopper_view.lower_arch_breaking_signal.connect(self.onLowerArchBreakingClicked)
|
2025-11-06 10:55:29 +08:00
|
|
|
|
|
|
|
|
|
|
def handleLowerClampAngleUpdate(self):
|
|
|
|
|
|
"""处理下料斗夹爪开合"""
|
|
|
|
|
|
# 角度增减逻辑
|
|
|
|
|
|
if self.is_add:
|
2026-01-11 18:00:32 +08:00
|
|
|
|
self.angle += 1.22
|
2025-11-06 10:55:29 +08:00
|
|
|
|
else:
|
2026-01-11 18:00:32 +08:00
|
|
|
|
self.angle -= 1.33
|
2025-11-06 10:55:29 +08:00
|
|
|
|
|
|
|
|
|
|
# 边界控制
|
|
|
|
|
|
if self.angle > self.max_angle:
|
|
|
|
|
|
self.is_add = False
|
|
|
|
|
|
self.angle = self.max_angle
|
|
|
|
|
|
if self.angle <= self.min_angle:
|
|
|
|
|
|
self.is_add = True
|
|
|
|
|
|
self.angle = self.min_angle
|
|
|
|
|
|
|
|
|
|
|
|
# 更新下料斗夹爪角度
|
|
|
|
|
|
self.onUpdateLowerClampAngle(self.angle)
|
|
|
|
|
|
|
|
|
|
|
|
@Slot(int)
|
|
|
|
|
|
def onUpdateUpperHopperWeight(self, weight:int):
|
|
|
|
|
|
"更新上料斗重量"
|
|
|
|
|
|
self.hopper_view.setUpperHopperWeight(weight)
|
|
|
|
|
|
|
|
|
|
|
|
# 注意:此时需要同步更新传送带中的上料斗的重量
|
|
|
|
|
|
self.conveyor_view.setConveyorHopperWeight(weight)
|
|
|
|
|
|
|
|
|
|
|
|
@Slot(int)
|
|
|
|
|
|
def onUpdateLowerHopperWeight(self, weight:int):
|
|
|
|
|
|
"更新下料斗重量"
|
|
|
|
|
|
self.hopper_view.setLowerHopperWeight(weight)
|
|
|
|
|
|
|
|
|
|
|
|
@Slot()
|
|
|
|
|
|
def handleReadUpperHopperWeight(self):
|
|
|
|
|
|
# 后台读取上料斗重量
|
2026-04-07 16:07:36 +08:00
|
|
|
|
# def upper_weight_task():
|
|
|
|
|
|
# loc_tra = TransmitterController(RelayController())
|
|
|
|
|
|
# # 上料斗重量 (目前只有上料斗安装变送器, 可以读取到重量)
|
|
|
|
|
|
# upper_weight = loc_tra.read_data(1)
|
|
|
|
|
|
# # 发送信号到主线程更新UI
|
|
|
|
|
|
# if upper_weight is not None:
|
|
|
|
|
|
# self.signals.upper_weight_updated.emit(upper_weight)
|
2025-11-06 10:55:29 +08:00
|
|
|
|
|
2026-04-07 16:07:36 +08:00
|
|
|
|
# threading.Thread(target=upper_weight_task, daemon=True).start()
|
|
|
|
|
|
pass
|
2025-11-06 10:55:29 +08:00
|
|
|
|
|
|
|
|
|
|
@Slot(float)
|
|
|
|
|
|
def onUpdateLowerClampAngle(self, angle:float):
|
|
|
|
|
|
"""更新下料斗夹爪角度"""
|
|
|
|
|
|
self.hopper_view.setLowerHopperOpeningAngle(angle)
|
|
|
|
|
|
|
|
|
|
|
|
@Slot(float)
|
|
|
|
|
|
def onUpdateUpperClampAngle(self, angle:float):
|
|
|
|
|
|
"""更新上料斗夹爪角度"""
|
|
|
|
|
|
self.hopper_view.setUpperHopperClampAngle(angle)
|
|
|
|
|
|
|
|
|
|
|
|
@Slot()
|
|
|
|
|
|
def onUpperClampOpenBottonClicked(self):
|
|
|
|
|
|
# 上料斗 夹爪 "开"按钮点击
|
|
|
|
|
|
print("hopper_controller: onUpperClampOpenBottonClicked")
|
|
|
|
|
|
# 测试上料斗夹爪,6秒打开60度
|
2026-01-11 18:00:32 +08:00
|
|
|
|
self.hopper_view.upper_clamp_widget.testAnimation(target_angle=60, duration=10)
|
|
|
|
|
|
|
|
|
|
|
|
@Slot(int)
|
|
|
|
|
|
def onUpdateUpperClampStatus(self, status:int):
|
2026-04-07 16:07:36 +08:00
|
|
|
|
# 上料斗夹爪状态 (1:半开,2全开,0:关闭)
|
|
|
|
|
|
if status == 1:
|
|
|
|
|
|
# 上料斗门半开
|
|
|
|
|
|
self.hopper_view.upper_clamp_widget.testAnimation(target_angle=30, duration=5)
|
|
|
|
|
|
elif status == 2:
|
|
|
|
|
|
# 上料斗门全开
|
2026-01-11 18:00:32 +08:00
|
|
|
|
self.hopper_view.upper_clamp_widget.testAnimation(target_angle=60, duration=10)
|
2026-04-07 16:07:36 +08:00
|
|
|
|
elif status == 0:
|
|
|
|
|
|
# 上料斗门关闭
|
|
|
|
|
|
self.hopper_view.upper_clamp_widget.testAnimation(target_angle=0, duration=6)
|
2026-01-11 18:00:32 +08:00
|
|
|
|
|
|
|
|
|
|
def onUpdateUpperHopperPosition(self, position:int):
|
|
|
|
|
|
# 上料斗位置
|
2026-04-07 16:07:36 +08:00
|
|
|
|
if position == UpperHopperPosition.MIXING_TOWER.value: # 上料斗到达搅拌楼
|
2026-01-11 18:00:32 +08:00
|
|
|
|
# 上料斗在搅拌楼下
|
|
|
|
|
|
self.hopper_view.hideUpperHopper() # 隐藏非传送带处的上料斗
|
|
|
|
|
|
self.conveyor_view.moveHopperBelowMixer() # 传送带处的上料斗移动到搅料楼下
|
|
|
|
|
|
self.conveyor_view.showHopper() # 显示传送带处的上料斗
|
2026-04-07 16:07:36 +08:00
|
|
|
|
elif position == UpperHopperPosition.VIBRATION_CHAMBER.value: # 上料斗就绪,到达振捣室
|
2026-01-11 18:00:32 +08:00
|
|
|
|
# 上料斗在振捣室处
|
|
|
|
|
|
self.conveyor_view.hideHopper() # 隐藏传送带处的上料斗
|
|
|
|
|
|
self.hopper_view.upper_clamp_widget.set_angle(0) # 上料斗夹爪角度设置为0,此时上料斗一定是关闭的
|
|
|
|
|
|
self.hopper_view.showUpperHopper() # 显示非传送带处的上料斗
|
2026-04-07 16:07:36 +08:00
|
|
|
|
elif position == UpperHopperPosition.IN_TRANSIT.value:
|
2026-01-11 18:00:32 +08:00
|
|
|
|
# 上料斗在途中
|
|
|
|
|
|
self.hopper_view.hideUpperHopper() # 隐藏非传送带处的上料斗 (下料斗处对应的上料斗叫非传送带处上料斗)
|
|
|
|
|
|
self.conveyor_view.moveHopperToTransition() # 传送带处上料斗移动到中间过渡位置
|
|
|
|
|
|
self.conveyor_view.showHopper() # 显示传送带处的上料斗
|
2025-11-06 10:55:29 +08:00
|
|
|
|
|
|
|
|
|
|
@Slot(bool)
|
2026-04-07 16:07:36 +08:00
|
|
|
|
def onUpperArchBreakingStatusChanged(self, status:bool):
|
|
|
|
|
|
"""上料斗破拱状态改变: status 为True表示 破拱状态, 为False表示 不破拱状态"""
|
|
|
|
|
|
if status == True: # 破拱状态
|
|
|
|
|
|
self.hopper_view.upper_arch_breaking_status = True
|
|
|
|
|
|
self.hopper_view.upper_arch_label.setHidden(False)
|
|
|
|
|
|
else: # 不破拱状态
|
|
|
|
|
|
self.hopper_view.upper_arch_breaking_status = False
|
|
|
|
|
|
self.hopper_view.upper_arch_label.setHidden(True)
|
|
|
|
|
|
|
|
|
|
|
|
@Slot(bool)
|
|
|
|
|
|
def onUpperArchBreakingClicked(self, status:bool):
|
|
|
|
|
|
"""上料斗破拱按钮点击: status 为True表示 开启破拱, 为False表示 关闭破拱"""
|
|
|
|
|
|
print("hopper_controller: onUpperArchBreakingClicked ", status)
|
|
|
|
|
|
# 这里需要控制网络继电器,开启上料斗破拱(可能还需要同步opc的上料斗破拱状态)
|
|
|
|
|
|
|
2025-11-06 10:55:29 +08:00
|
|
|
|
|
|
|
|
|
|
@Slot(int)
|
|
|
|
|
|
def onUpperHopperStatusChanged(self, status:int):
|
|
|
|
|
|
"""上料斗状态改变: status为 0=绿(正常), 1=黄(警告), 2=红(异常) """
|
|
|
|
|
|
# 料斗中的状态指示器
|
|
|
|
|
|
self.hopper_view.setUpperHopperStatus(status)
|
|
|
|
|
|
|
|
|
|
|
|
@Slot(float)
|
|
|
|
|
|
def onUpdateUpperHopperVolume(self, volume: float):
|
|
|
|
|
|
"""更新上料斗显示的方量,如: 2.0"""
|
|
|
|
|
|
self.hopper_view.setUpperHopperVolume(volume)
|
|
|
|
|
|
|
|
|
|
|
|
@Slot()
|
|
|
|
|
|
def onLowerClampOpenBottonClicked(self):
|
|
|
|
|
|
# 下料斗 夹爪 "开"按钮点击
|
|
|
|
|
|
print("hopper_controller: onLowerClampOpenBottonClicked")
|
|
|
|
|
|
|
|
|
|
|
|
@Slot(bool)
|
2026-04-07 16:07:36 +08:00
|
|
|
|
def onLowerArchBreakingStatusChanged(self, status:bool):
|
|
|
|
|
|
"""下料斗破拱状态改变: status 为True表示 破拱状态, 为False表示 不破拱状态"""
|
|
|
|
|
|
if status == True: # 破拱状态
|
|
|
|
|
|
self.hopper_view.lower_arch_breaking_status = True
|
|
|
|
|
|
self.hopper_view.lower_arch_label.setHidden(False)
|
|
|
|
|
|
else: # 不破拱状态
|
|
|
|
|
|
self.hopper_view.lower_arch_breaking_status = False
|
|
|
|
|
|
self.hopper_view.lower_arch_label.setHidden(True)
|
|
|
|
|
|
|
|
|
|
|
|
@Slot(bool)
|
|
|
|
|
|
def onLowerArchBreakingClicked(self, status:bool):
|
|
|
|
|
|
"""下料斗破拱按钮点击: status 为True表示 开启破拱, 为False表示 关闭破拱"""
|
|
|
|
|
|
print("hopper_controller: onLowerArchBreakingClicked ", status)
|
|
|
|
|
|
# 这里需要控制网络继电器,开启下料斗破拱(可能还需要同步opc的下料斗破拱状态)
|
2025-11-06 10:55:29 +08:00
|
|
|
|
|
|
|
|
|
|
@Slot(int)
|
|
|
|
|
|
def onLowerHopperStatusChanged(self, status:int):
|
|
|
|
|
|
"""下料斗状态改变: status为 0=绿(正常), 1=黄(警告), 2=红(异常) """
|
|
|
|
|
|
# 料斗中的状态指示器
|
|
|
|
|
|
self.hopper_view.setLowerHopperStatus(status)
|