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__ __pycache__
/core/__pycache__ /core/__pycache__
/vision/__pycache__ /vision/__pycache__
/opcua/__pycache__
/feeding/__pycache__

View File

@ -60,7 +60,7 @@ class ArtifactBll:
def get_artifacting_task(self) -> ArtifactInfoModel: 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: if loc_item:
return loc_item[0] return loc_item[0]
else: else:

View File

@ -308,3 +308,41 @@ class PDRecordModel:
CreateTime: str="" CreateTime: str=""
#派单时间(下发) #派单时间(下发)
OptTime: 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): def log_path(self):
"""获取日志文件路径""" """获取日志文件路径"""
return self._read_config_value('app', 'LOG_PATH', 'logs/app.log', str) 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() ini_manager = IniManager()

View File

@ -10,7 +10,9 @@ class Settings:
self.relay_host = '192.168.250.62' self.relay_host = '192.168.250.62'
self.relay_port = 50000 self.relay_port = 50000
self.debug_feeding=True self.debug_feeding=False
#调试模式上,网络继点器和摄像头禁用,模型推理禁用
self.debug_mode=False
# 摄像头配置 # 摄像头配置
self.camera_type = "ip" self.camera_type = "ip"
@ -46,7 +48,7 @@ class Settings:
self.single_batch_weight = 2500 # 单次下料重量(kg) self.single_batch_weight = 2500 # 单次下料重量(kg)
# 角度控制参数 # 角度控制参数
self.target_angle = 20.0 # 目标角度 self.target_angle = 30.0 # 目标角度
self.min_angle = 10.0 # 最小角度 self.min_angle = 10.0 # 最小角度
self.max_angle = 80.0 # 最大角度 self.max_angle = 80.0 # 最大角度
self.angle_threshold = 60.0 # 角度阈值 self.angle_threshold = 60.0 # 角度阈值
@ -83,5 +85,9 @@ class Settings:
self.max_upper_volume = 2.4 # 上料斗容量(方) self.max_upper_volume = 2.4 # 上料斗容量(方)
#下料到下料斗最大下到多少,并非最大容量 #下料到下料斗最大下到多少,并非最大容量
self.max_lower_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 .hopper_controller import HopperController
from service.msg_recorder import MessageRecorder from service.msg_recorder import MessageRecorder
from config.settings import Settings
from core.system import FeedingControlSystem from core.system import FeedingControlSystem
from opc.opcua_server import SimpleOPCUAServer from opc.opcua_server import SimpleOPCUAServer
@ -15,8 +14,7 @@ class MainController:
def __init__(self): def __init__(self):
# 主界面 # 主界面
self.main_window = MainWindow() self.main_window = MainWindow()
self.settings = Settings() self.system = FeedingControlSystem()
self.system = FeedingControlSystem(self.settings)
self.system.initialize() self.system.initialize()
self.system.state.state_updated.connect(self.update_ui_notify) self.system.state.state_updated.connect(self.update_ui_notify)
self.opcua_server = SimpleOPCUAServer(self.system.state) self.opcua_server = SimpleOPCUAServer(self.system.state)
@ -63,7 +61,7 @@ class MainController:
def update_ui_notify(self, prop:str,value): def update_ui_notify(self, prop:str,value):
"""更新UI状态""" """更新UI状态"""
print(f"更新UI状态: {prop} = {value}") # print(f"更新UI状态: {prop} = {value}")
def __connectSignals(self): def __connectSignals(self):
self.main_window.about_to_close.connect(self.handleMainWindowClose) # 处理主界面关闭 self.main_window.about_to_close.connect(self.handleMainWindowClose) # 处理主界面关闭
@ -71,6 +69,9 @@ class MainController:
def handleMainWindowClose(self): def handleMainWindowClose(self):
"""主界面关闭""" """主界面关闭"""
self.msg_recorder.normal_record("关闭自动智能浇筑系统") self.msg_recorder.normal_record("关闭自动智能浇筑系统")
self.system.stop()
self.opcua_server.stop()
# 停止系统底部控制器中的线程 # 停止系统底部控制器中的线程
if hasattr(self, 'bottom_control_controller'): if hasattr(self, 'bottom_control_controller'):
self.bottom_control_controller.stop_threads() self.bottom_control_controller.stop_threads()

View File

@ -23,7 +23,7 @@ class SystemState(QObject):
self._upper_volume=0.0 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_is_arch_=False
self._lower_weight=0 self._lower_weight=0
self._lower_angle=0.0 self._lower_angle=0.0
@ -69,11 +69,8 @@ class SystemState(QObject):
#当前生产状态 #当前生产状态
self._feed_status=FeedStatus.FNone self._feed_status=FeedStatus.FNone
#每方重量 #每方重量
self.density=2500 self.density=2416.4
#
#
self._watched_props = [k for k in self.__dict__ if k.startswith('_')] self._watched_props = [k for k in self.__dict__ if k.startswith('_')]
def __setattr__(self, name, value): def __setattr__(self, name, value):

View File

@ -2,7 +2,6 @@
import threading import threading
import time import time
import cv2 import cv2
from config.settings import Settings
from core.state import SystemState from core.state import SystemState
from hardware.relay import RelayController from hardware.relay import RelayController
from hardware.inverter import InverterController from hardware.inverter import InverterController
@ -11,31 +10,32 @@ from hardware.RFID.rfid_service import rfid_service
from vision.camera import DualCameraController from vision.camera import DualCameraController
from vision.detector import VisionDetector from vision.detector import VisionDetector
from feeding.controller import FeedingController from feeding.controller import FeedingController
from service.mould_service import app_web_service
from config.settings import app_set_config
class FeedingControlSystem: class FeedingControlSystem:
def __init__(self, settings: Settings): def __init__(self):
self.settings = settings
self.state = SystemState() self.state = SystemState()
# 初始化硬件控制器 # 初始化硬件控制器
self.relay_controller = RelayController( self.relay_controller = RelayController(
host=settings.relay_host, host=app_set_config.relay_host,
port=settings.relay_port port=app_set_config.relay_port
) )
self.inverter_controller = InverterController(self.relay_controller) self.inverter_controller = InverterController(self.relay_controller)
self.transmitter_controller = TransmitterController(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控制器 # 初始化RFID控制器
self.rfid_controller = rfid_service( self.rfid_controller = rfid_service(
host=settings.rfid_host, host=app_set_config.rfid_host,
port=settings.rfid_port port=app_set_config.rfid_port
) )
# 初始化下料控制器 # 初始化下料控制器
self.feeding_controller = FeedingController( self.feeding_controller = FeedingController(
@ -45,8 +45,7 @@ class FeedingControlSystem:
self.vision_detector, self.vision_detector,
self.camera_controller, self.camera_controller,
self.rfid_controller, self.rfid_controller,
self.state, self.state
settings
) )
@ -56,6 +55,7 @@ class FeedingControlSystem:
self.visual_control_thread = None self.visual_control_thread = None
self.alignment_check_thread = None self.alignment_check_thread = None
self.lower_feeding_thread = None self.lower_feeding_thread = None
self.led_thread = None
def initialize(self): def initialize(self):
"""初始化系统""" """初始化系统"""
@ -63,12 +63,12 @@ class FeedingControlSystem:
# 设置摄像头配置 # 设置摄像头配置
# self.camera_controller.set_config( # self.camera_controller.set_config(
# camera_type=self.settings.camera_type, # camera_type=app_set_config.camera_type,
# ip=self.settings.camera_ip, # ip=app_set_config.camera_ip,
# port=self.settings.camera_port, # port=app_set_config.camera_port,
# username=self.settings.camera_username, # username=app_set_config.camera_username,
# password=self.settings.camera_password, # password=app_set_config.camera_password,
# channel=self.settings.camera_channel # channel=app_set_config.camera_channel
# ) # )
# # 初始化摄像头 # # 初始化摄像头
@ -78,30 +78,33 @@ class FeedingControlSystem:
# 加载视觉模型 # 加载视觉模型
# if not self.vision_detector.load_models(): # if not self.vision_detector.load_models():
# raise Exception("视觉模型加载失败") # raise Exception("视觉模型加载失败")
if not self.settings.debug_feeding:
if not self.check_device_connectivity(): self.check_device_connectivity()
raise Exception("设备连接失败")
self.camera_controller.start_cameras() # 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() self.start_lower_feeding()
#LED屏
# self.start_led()
print("控制系统初始化完成") print("控制系统初始化完成")
def start_monitoring(self): def start_monitoring(self):
"""启动系统监控""" """启动系统监控"""
self.state.running = True print('振动和要料监控线程启动')
self.monitor_thread = threading.Thread( self.monitor_thread = threading.Thread(
target=self._monitor_loop, target=self._monitor_loop,
daemon=True daemon=True,
name='monitor'
) )
self.monitor_thread.start() self.monitor_thread.start()
@ -109,7 +112,7 @@ class FeedingControlSystem:
"""监控循环""" """监控循环"""
while self.state.running: while self.state.running:
try: try:
self.feeding_controller.check_upper_material_request() # self.feeding_controller.check_upper_material_request()
self.feeding_controller.check_arch_blocking() self.feeding_controller.check_arch_blocking()
time.sleep(1) time.sleep(1)
except Exception as e: except Exception as e:
@ -117,9 +120,11 @@ class FeedingControlSystem:
def start_visual_control(self): def start_visual_control(self):
"""启动视觉控制""" """启动视觉控制"""
print('视觉控制线程启动')
self.visual_control_thread = threading.Thread( self.visual_control_thread = threading.Thread(
target=self._visual_control_loop, target=self._visual_control_loop,
daemon=True daemon=True,
name='visual_control'
) )
self.visual_control_thread.start() self.visual_control_thread.start()
@ -132,37 +137,79 @@ class FeedingControlSystem:
if current_frame is not None: if current_frame is not None:
# 执行视觉控制逻辑 # 执行视觉控制逻辑
self.feeding_controller.visual_control(current_frame) 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: except Exception as e:
print(f"视觉控制循环错误: {e}") print(f"视觉控制循环错误: {e}")
time.sleep(self.settings.visual_check_interval) time.sleep(app_set_config.visual_check_interval)
def start_alignment_check(self): def start_alignment_check(self):
"""启动对齐检查""" """启动对齐检查"""
print('对齐检查线程启动')
self.alignment_check_thread = threading.Thread( self.alignment_check_thread = threading.Thread(
target=self._alignment_check_loop, target=self._alignment_check_loop,
daemon=True daemon=True,
name='align_check'
) )
self.alignment_check_thread.start() self.alignment_check_thread.start()
def _alignment_check_loop(self): def _alignment_check_loop(self):
"""对齐检查循环""" """对齐检查循环"""
loc_align_status=False
loc_before_status=None
while self.state.running: while self.state.running:
try: try:
if self.state._lower_feeding_stage == 4: # 等待对齐阶段 if self.state.lower_feeding_stage == 4: # 等待对齐阶段
current_frame = self.camera_controller.get_single_latest_frame() current_frame = self.camera_controller.get_single_latest_frame()
if current_frame is not None: 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: if self.state.vehicle_aligned:
# loc_count+=1
print("检测到模具车对齐") print("检测到模具车对齐")
else: else:
print("模具车未对齐") print("模具车未对齐")
else: # time.sleep(app_set_config.alignment_check_interval)
print('未检测到图像') # loc_align_status=self.alignment_check_status()
time.sleep(self.settings.alignment_check_interval) # 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: except Exception as e:
print(f"对齐检查循环错误: {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): def start_lower_feeding(self):
"""启动下料流程""" """启动下料流程"""
@ -177,7 +224,34 @@ class FeedingControlSystem:
"""启动下料流程""" """启动下料流程"""
while self.state.running: while self.state.running:
self.feeding_controller.start_feeding() 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: def check_device_connectivity(self) -> bool:
"""检查关键设备连接状态""" """检查关键设备连接状态"""
@ -194,15 +268,15 @@ class FeedingControlSystem:
return False return False
# 尝试读取变频器一个寄存器(测试连接) # 尝试读取变频器一个寄存器(测试连接)
test_result = self.relay_controller.modbus_client.read_holding_registers( # test_result = self.relay_controller.modbus_client.read_holding_registers(
address=0x00, # address=0x00,
count=1, # count=1,
slave=self.inverter_controller.config['slave_id'] # slave=self.inverter_controller.config['slave_id']
) # )
if isinstance(test_result, Exception): # if isinstance(test_result, Exception):
print("变频器连接测试失败") # print("变频器连接测试失败")
return False # return False
# 检查下料斗变送器连接 # 检查下料斗变送器连接
test_weight = self.transmitter_controller.read_data(2) 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 core.state import FeedStatus
from feeding.process import FeedingProcess from feeding.process import FeedingProcess
from busisness.blls import ArtifactBll from busisness.blls import ArtifactBll
from config.settings import app_set_config
class FeedingController: class FeedingController:
def __init__(self, relay_controller, inverter_controller, def __init__(self, relay_controller, inverter_controller,
transmitter_controller, vision_detector, transmitter_controller, vision_detector,
camera_controller, rfid_controller,state, settings): camera_controller, rfid_controller,state):
self.relay_controller = relay_controller self.relay_controller = relay_controller
self.inverter_controller = inverter_controller self.inverter_controller = inverter_controller
self.transmitter_controller = transmitter_controller self.transmitter_controller = transmitter_controller
@ -16,14 +17,13 @@ class FeedingController:
self.camera_controller = camera_controller self.camera_controller = camera_controller
self.rfid_controller = rfid_controller self.rfid_controller = rfid_controller
self.state = state self.state = state
self.settings = settings
self.artifact_bll = ArtifactBll() self.artifact_bll = ArtifactBll()
# 初始化下料流程 # 初始化下料流程
self.process = FeedingProcess( self.process = FeedingProcess(
relay_controller, inverter_controller, relay_controller, inverter_controller,
transmitter_controller, vision_detector, transmitter_controller, vision_detector,
camera_controller, state, settings camera_controller, state
) )
@ -42,19 +42,21 @@ class FeedingController:
if current_weight is None: if current_weight is None:
self.state.upper_weight_error_count += 1 self.state.upper_weight_error_count += 1
print(f"上料斗重量读取失败,错误计数: {self.state.upper_weight_error_count}") 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("警告:上料斗传感器连续读取失败,请检查连接") print("警告:上料斗传感器连续读取失败,请检查连接")
return False return False
#需要搅拌楼通知下完料后移到上料斗上方 #需要搅拌楼通知下完料后移到上料斗上方
if self.state._upper_door_position != 'over_lower':
self.state._upper_door_position = 'over_lower'
self.state.upper_weight_error_count = 0 self.state.upper_weight_error_count = 0
# 判断是否需要要料:当前重量 < 目标重量 + 缓冲重量 # 判断是否需要要料:当前重量 < 目标重量 + 缓冲重量
if self.state.feed_status != FeedStatus.FUpperToLower: if self.state._feed_status != FeedStatus.FUpperToLower:
if current_weight < (self.settings.min_required_weight): if current_weight < (app_set_config.min_required_weight):
print("上料斗重量不足,通知搅拌楼要料") print("上料斗重量不足,通知搅拌楼要料")
self.request_material_from_mixing_building() # 请求搅拌楼下料 self.request_material_from_mixing_building() # 请求搅拌楼下料
return True return True
else:
if self.state._upper_door_position != 'over_lower':
self.state._upper_door_position = 'over_lower'
return False return False
def request_material_from_mixing_building(self): def request_material_from_mixing_building(self):
@ -62,7 +64,7 @@ class FeedingController:
请求搅拌楼下料 请求搅拌楼下料
""" """
print("发送要料请求至搅拌楼...") print("发送要料请求至搅拌楼...")
# self.settings. #
self.process.return_upper_door_to_default() self.process.return_upper_door_to_default()
@ -73,35 +75,35 @@ class FeedingController:
def check_arch_blocking(self): def check_arch_blocking(self):
"""检查是否需要破拱""" """检查是否需要破拱"""
current_time = time.time() current_time = time.time()
# 检查下料斗破拱(只有在下料过程中才检查) # 检查下料斗破拱(只有在下料过程中才检查)
if self.state._lower_feeding_stage in [1, 2, 3]: # 在所有下料阶段检查 # if self.state.lower_feeding_stage in [1, 2, 3]: # 在所有下料阶段检查
lower_weight = self.transmitter_controller.read_data(2) # lower_weight = self.transmitter_controller.read_data(2)
if lower_weight is not None: # if lower_weight is not None:
# 检查重量变化是否过慢小于0.1kg变化且时间超过10秒 # # 检查重量变化是否过慢小于0.1kg变化且时间超过10秒
if (abs(lower_weight - self.state.last_lower_weight) < 0.1) and \ # if (abs(lower_weight - self.state.last_lower_weight) < 0.1) and \
(current_time - self.state.last_weight_time) > 10: # (current_time - self.state.last_weight_time) > 10:
print("下料斗可能堵塞,启动破拱") # print("下料斗可能堵塞,启动破拱")
self.state._lower_is_arch_=True # self.state._lower_is_arch_=True
self.relay_controller.control(self.relay_controller.BREAK_ARCH_LOWER, 'open') # self.relay_controller.control(self.relay_controller.BREAK_ARCH_LOWER, 'open')
time.sleep(2) # time.sleep(2)
self.relay_controller.control(self.relay_controller.BREAK_ARCH_LOWER, 'close') # self.relay_controller.control(self.relay_controller.BREAK_ARCH_LOWER, 'close')
self.state._lower_is_arch_=False # 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 if self.state._feed_status == FeedStatus.FUpperToLower: # 在任何阶段都可能需要上料斗破拱
self.state._lower_feeding_stage in [0, 1, 2, 3, 4]): # 在任何阶段都可能需要上料斗破拱 print('上料斗振动线程启用中...')
upper_weight = self.transmitter_controller.read_data(1) upper_weight = self.transmitter_controller.read_data(1)
if upper_weight is not None: if upper_weight is not None:
# 检查重量变化是否过慢小于0.1kg变化且时间超过10秒 # 检查重量变化是否过慢小于0.1kg变化且时间超过10秒,觉得有点小。改成
if (abs(upper_weight - self.state.last_upper_weight) < 0.1) and \ if (abs(upper_weight - self.state.last_upper_weight) < 100) and \
(current_time - self.state.last_weight_time) > 10: (current_time - self.state.last_weight_time) > 5:
print("上料斗可能堵塞,启动破拱") print("上料斗可能堵塞,启动破拱")
self.state._upper_is_arch_=True self.state._upper_is_arch_=True
self.relay_controller.control(self.relay_controller.BREAK_ARCH_UPPER, 'open') 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.relay_controller.control(self.relay_controller.BREAK_ARCH_UPPER, 'close')
self.state._upper_is_arch_=False self.state._upper_is_arch_=False
@ -124,6 +126,7 @@ class FeedingController:
print("无法获取当前角度,跳过本次调整") print("无法获取当前角度,跳过本次调整")
return return
self.state.last_angle = current_angle self.state.last_angle = current_angle
self.state._lower_angle=current_angle
print(f"当前角度: {current_angle:.2f}°, 溢料状态: {overflow}, 溢料返回: {self.state.overflow_detected}") print(f"当前角度: {current_angle:.2f}°, 溢料状态: {overflow}, 溢料返回: {self.state.overflow_detected}")
return return
if self.state.overflow_detected!="浇筑满": if self.state.overflow_detected!="浇筑满":
@ -135,17 +138,17 @@ class FeedingController:
# 状态机控制逻辑 # 状态机控制逻辑
if self.state.angle_control_mode == "normal": if self.state.angle_control_mode == "normal":
# 正常模式大于self.settings.angle_threshold=60度 # 正常模式大于app_set_config.angle_threshold=60度
if overflow and current_angle > self.settings.angle_threshold: if overflow and current_angle > app_set_config.angle_threshold:
# 检测到堆料且角度过大,进入角度减小模式 # 检测到堆料且角度过大,进入角度减小模式
print("检测到堆料且角度过大,关闭出砼门开始减小角度") print("检测到堆料且角度过大,关闭出砼门开始减小角度")
self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'close') self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'close')
self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE, 'open') self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE, 'open')
self.state.angle_control_mode = "reducing" self.state.angle_control_mode = "reducing"
else: else:
# 保持正常开门 # 保持正常开门 30
self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'open') # self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'open')
if current_angle >self.settings.target_angle: 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_OPEN, 'close')
self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE, 'open') self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE, 'open')
@ -155,7 +158,7 @@ class FeedingController:
elif self.state.angle_control_mode == "reducing": 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: if overflow:
# 仍有堆料,进入维持模式 # 仍有堆料,进入维持模式
@ -196,9 +199,7 @@ class FeedingController:
self.state.angle_control_mode = "normal" self.state.angle_control_mode = "normal"
else: else:
self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'close') self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'close')
self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE, 'open') self.relay_controller.control_lower_close()
time.sleep(5)
self.relay_controller.control(self.relay_controller.DOOR_LOWER_CLOSE, 'close')
def pulse_control_door_for_maintaining(self): def pulse_control_door_for_maintaining(self):

View File

@ -5,11 +5,12 @@ from busisness.blls import ArtifactBll
from busisness.models import ArtifactInfoModel from busisness.models import ArtifactInfoModel
import time import time
from datetime import datetime from datetime import datetime
from config.settings import app_set_config
class FeedingProcess: class FeedingProcess:
def __init__(self, relay_controller, inverter_controller, def __init__(self, relay_controller, inverter_controller,
transmitter_controller, vision_detector, transmitter_controller, vision_detector,
camera_controller, state, settings): camera_controller, state):
self.relay_controller = relay_controller self.relay_controller = relay_controller
self.artifact_bll = ArtifactBll() self.artifact_bll = ArtifactBll()
self.mould_service = MouldService() self.mould_service = MouldService()
@ -18,50 +19,60 @@ class FeedingProcess:
self.vision_detector = vision_detector self.vision_detector = vision_detector
self.camera_controller = camera_controller self.camera_controller = camera_controller
self.state = state self.state = state
self.state.feed_status = FeedStatus.FNone self.state._feed_status = FeedStatus.FCheckM
#标志位用,是否是第一次运行 #标志位用,是否是第一次运行
self.is_first_flag=True self.is_first_flag=True
self.settings = settings
def start_feeding(self): def start_feeding(self):
loc_state=self.state 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: if loc_state._feed_status == FeedStatus.FNone:
loc_state.feed_status = FeedStatus.FCheckM # loc_state._feed_status = FeedStatus.FCheckM
return 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: # if app_set_config.debug_feeding:
loc_state.feed_status = FeedStatus.FApiCheck # loc_state._feed_status = FeedStatus.FApiCheck
if self.state.vehicle_aligned: if self.state.vehicle_aligned:
loc_state.feed_status = FeedStatus.FCheckGB loc_state._feed_status = FeedStatus.FApiCheck
print("检查模车") print("检查模车")
return return
elif loc_state.feed_status == FeedStatus.FApiCheck: elif loc_state._feed_status == FeedStatus.FApiCheck:
print("生产已开始") print("生产已开始")
time.sleep(2) # time.sleep(2)
loc_modules = self.mould_service.get_not_pour_artifacts() loc_modules = self.mould_service.get_not_pour_artifacts()
if loc_modules: if loc_modules:
# 取第一个未浇筑的管片 # 取第一个未浇筑的管片
#后续放入队列处理 #后续放入队列处理
loc_module = loc_modules[0] loc_module = loc_modules[0]
#API #API
loc_module.Source = 1 loc_module.Source = 1
loc_module.BeginTime=datetime.now() loc_module.BeginTime=datetime.now()
self.artifact_bll.insert_artifact_task(loc_module) 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.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: else:
#未读取到AIP接口数据. #未读取到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 return
elif loc_state.feed_status == FeedStatus.FRFID: elif loc_state._feed_status == FeedStatus.FRFID:
print("生产已检查RFID") print("生产已检查RFID")
#RFID格式模具编号,分块号,尺寸规格,方量 #RFID格式模具编号,分块号,尺寸规格,方量
rfid_info ='' rfid_info =''
@ -71,7 +82,7 @@ class FeedingProcess:
loc.BetonVolume=1.56 loc.BetonVolume=1.56
if self.state.current_artifact: if self.state.current_artifact:
#检测是否和RFID识别的管理一致 #检测是否和RFID识别的管理一致
self.state.feed_status = FeedStatus.FCheckGB loc_state._feed_status = FeedStatus.FCheckGB
else: else:
#以RFID为准 #以RFID为准
loc_module= ArtifactInfoModel() loc_module= ArtifactInfoModel()
@ -82,23 +93,23 @@ class FeedingProcess:
self.state.current_artifact = loc_module self.state.current_artifact = loc_module
#确认是否保存到数据库 #确认是否保存到数据库
self.state.feed_status = FeedStatus.FCheckGB loc_state._feed_status = FeedStatus.FCheckGB
return return
elif loc_state.feed_status == FeedStatus.FCheckGB: elif loc_state._feed_status == FeedStatus.FCheckGB:
print("检查盖板对齐,") print("检查盖板对齐,")
time.sleep(10) # time.sleep(5)
loc_state.feed_status = FeedStatus.FUpperToLower loc_state._feed_status = FeedStatus.FUpperToLower
#计算本次生产需要的总重量 #计算本次生产需要的总重量
print(f"本次生产需要的总重量:{self.state._mould_need_weight}") print(f"本次生产需要的总重量:{self.state._mould_need_weight}")
return return
elif loc_state.feed_status == FeedStatus.FUpperToLower: elif loc_state._feed_status == FeedStatus.FUpperToLower:
print("上料斗向下料斗转移") 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: if loc_state._mould_need_weight > loc_state.initial_upper_weight + loc_state.initial_lower_weight:
# 等待上料斗重量增加(多久不够报警可能出现F块不足的情况) # 等待上料斗重量增加(多久不够报警可能出现F块不足的情况)
print('重量不够,需要增加') print('重量不够,需要增加')
@ -114,40 +125,46 @@ class FeedingProcess:
# 最后一块F块前面多要0.25,0.3F块直接下料先多下0.3后续) # 最后一块F块前面多要0.25,0.3F块直接下料先多下0.3后续)
# loc_FWeight=0.3*loc_state.density # loc_FWeight=0.3*loc_state.density
# loc_feed_weight=loc_state.need_total_weight-loc_state.initial_lower_weight-loc_FWeight # 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) # time.sleep(10)
return return
elif loc_state.feed_status == FeedStatus.FFeed1: elif loc_state._feed_status == FeedStatus.FFeed1:
#下料 #下料
# self._start_feeding_stage() # self._start_feeding_stage()
self.feeding_stage_one(loc_state) self.feeding_stage_one(loc_state)
print("下料1") print("下料1")
return return
elif loc_state.feed_status == FeedStatus.FFeed2: elif loc_state._feed_status == FeedStatus.FFeed2:
#上料 #上料
# self._start_feeding_stage() # self._start_feeding_stage()
self.feeding_stage_two(loc_state) self.feeding_stage_two(loc_state)
print("下料2") print("下料2")
return return
elif loc_state.feed_status == FeedStatus.FFeed3: elif loc_state._feed_status == FeedStatus.FFeed3:
#下料 #下料
# self._start_feeding_stage() # self._start_feeding_stage()
self.feeding_stage_three(loc_state) self.feeding_stage_three(loc_state)
print("下料3") print("下料3")
return return
elif loc_state.feed_status == FeedStatus.FFinished: elif loc_state._feed_status == FeedStatus.FFinished:
"""完成当前批次下料""" """完成当前批次下料"""
print("当前批次下料完成,关闭出砼门") print("当前批次下料完成,关闭出砼门")
if loc_state.overflow_detected=="浇筑满": if loc_state.overflow_detected=="浇筑满":
self.inverter_controller.control('stop') 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) 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 return
def _start_feeding_stage(self): def _start_feeding_stage(self):
@ -156,34 +173,35 @@ class FeedingProcess:
print("开始分步下料过程") print("开始分步下料过程")
self.transfer_material_from_upper_to_lower() 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转移后剩下的上料斗重量""" """target_upper_weight转移后剩下的上料斗重量"""
# 如果低于单次,全部卸掉 # 如果低于单次,全部卸掉
_max_lower_weight=self.settings.max_lower_volume*self.state.density _max_lower_weight=app_set_config.max_lower_volume*self.state.density
if (initial_lower_weight+feed_weight>_max_lower_weight): # if (initial_lower_weight+feed_weight>_max_lower_weight):
feed_weight=_max_lower_weight-initial_lower_weight feed_weight=_max_lower_weight-initial_lower_weight
target_upper_weight=initial_upper_weight-feed_weight target_upper_weight=initial_upper_weight-feed_weight
target_upper_weight = max(target_upper_weight, 0) 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 import time
start_time = time.time() start_time = time.time()
# timeout = 30 # 30秒超时 # timeout = 30 # 30秒超时
while self.state.running: while loc_state.running:
current_upper_weight = self.transmitter_controller.read_data(1) current_upper_weight = self.transmitter_controller.read_data(1)
# 如果无法读取重量,继续尝试 # 如果无法读取重量,继续尝试
if current_upper_weight is None: if current_upper_weight is None:
print("无法读取上料斗重量,继续尝试...") print("无法读取上料斗重量,继续尝试...")
time.sleep(1) time.sleep(1)
continue continue
loc_state._upper_weight=current_upper_weight
print(f"上料斗当前重量: {current_upper_weight:.2f}kg") print(f"上料斗当前重量: {current_upper_weight:.2f}kg")
# 如果达到目标重量,则关闭上料斗出砼门 # 如果达到目标重量,则关闭上料斗出砼门
@ -198,35 +216,40 @@ class FeedingProcess:
print("重量变化过小,可能存在堵塞") print("重量变化过小,可能存在堵塞")
time.sleep(1) time.sleep(1)
# 关闭上料斗出砼门 # 关闭上料斗出砼门
self.relay_controller.control(self.relay_controller.DOOR_LOWER_1, 'close') self.relay_controller.control_upper_close()
#测试用
print("上料斗下料完成") print("上料斗下料完成")
def wait_for_vehicle_alignment(self): def wait_for_vehicle_alignment(self):
"""等待模具车对齐""" """等待模具车对齐"""
print("等待模具车对齐...") print("等待模具车对齐...")
self.state._lower_feeding_stage = 4 self.state.lower_feeding_stage = 4
import time 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: if self.state.vehicle_aligned:
print("模具车已对齐,开始下料") print("模具车已对齐,开始下料")
self.state._lower_feeding_stage = 1 self.state.lower_feeding_stage = 1
# self.feeding_stage_one() # self.feeding_stage_one()
break break
time.sleep(self.settings.alignment_check_interval) time.sleep(app_set_config.alignment_check_interval)
def feeding_stage_one(self,loc_state): def feeding_stage_one(self,loc_state):
"""第一阶段下料:下料斗向模具车下料(低速)""" """第一阶段下料:下料斗向模具车下料(低速)"""
print("开始第一阶段下料:下料斗低速下料") print("开始第一阶段下料:下料斗低速下料")
if self.is_first_flag: if self.is_first_flag:
self.inverter_controller.set_frequency(self.settings.frequencies[0]) # self.inverter_controller.set_frequency(app_set_config.frequencies[0])
self.inverter_controller.control('start') # 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') self.relay_controller.control(self.relay_controller.DOOR_LOWER_OPEN, 'open')
loc_cur_weight = self.transmitter_controller.read_data(2) loc_cur_weight = loc_state._lower_weight
if loc_cur_weight is None: if loc_cur_weight is None:
#报警处理 #报警处理
print("无法获取初始重量,取消下料") print("无法获取初始重量,取消下料")
@ -238,7 +261,7 @@ class FeedingProcess:
start_time = time.time() start_time = time.time()
current_weight = self.transmitter_controller.read_data(2) current_weight = loc_state._lower_weight
if current_weight is None: if current_weight is None:
#报警处理 #报警处理
print("无法获取当前重量,取消下料") print("无法获取当前重量,取消下料")
@ -246,25 +269,28 @@ class FeedingProcess:
return return
loc_state._mould_finish_weight=loc_state.initial_lower_weight-current_weight loc_state._mould_finish_weight=loc_state.initial_lower_weight-current_weight
target_weight = loc_state._mould_need_weight/3 target_weight = loc_state._mould_need_weight/3
# or (time.time() - start_time) > 30
if (current_weight is not None and loc_state._mould_finish_weight >= target_weight) 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._feed_status = FeedStatus.FFeed2
loc_state._lower_feeding_stage = 2 loc_state.lower_feeding_stage = 2
self.is_first_flag=True self.is_first_flag=True
return return
else: else:
time.sleep(1) time.sleep(1)
def feeding_stage_two(self): def feeding_stage_two(self,loc_state):
"""第二阶段下料:下料斗向模具车下料(中速)""" """第二阶段下料:下料斗向模具车下料(中速)"""
if self.is_first_flag: if self.is_first_flag:
print("开始第二阶段下料:下料斗中速下料") 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) # loc_cur_weight = self.transmitter_controller.read_data(2)
# if loc_cur_weight is None: # if loc_cur_weight is None:
# #报警处理 # #报警处理
@ -275,7 +301,7 @@ class FeedingProcess:
self.is_first_flag=False self.is_first_flag=False
start_time = time.time() start_time = time.time()
current_weight = self.transmitter_controller.read_data(2) current_weight = loc_state._lower_weight
if current_weight is None: if current_weight is None:
#报警处理 #报警处理
print("无法获取当前重量,取消下料") print("无法获取当前重量,取消下料")
@ -283,26 +309,29 @@ class FeedingProcess:
return return
loc_state._mould_finish_weight=loc_state.initial_lower_weight-current_weight loc_state._mould_finish_weight=loc_state.initial_lower_weight-current_weight
target_weight = (loc_state._mould_need_weight/3)*2 target_weight = (loc_state._mould_need_weight/3)*2
# or (time.time() - start_time) > 30
if (current_weight is not None and loc_state._mould_finish_weight >= target_weight) 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._feed_status = FeedStatus.FFeed3
loc_state._lower_feeding_stage = 3 loc_state.lower_feeding_stage = 3
self.is_first_flag=True self.is_first_flag=True
return return
else: else:
time.sleep(1) time.sleep(1)
def feeding_stage_three(self):
def feeding_stage_three(self,loc_state):
"""第三阶段下料:下料斗向模具车下料(高速)""" """第三阶段下料:下料斗向模具车下料(高速)"""
if self.is_first_flag: if self.is_first_flag:
print("开始第三阶段下料:下料斗高速下料") 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 self.is_first_flag=False
current_weight = self.transmitter_controller.read_data(2) current_weight = loc_state._lower_weight
if current_weight is None: if current_weight is None:
#报警处理 #报警处理
print("无法获取当前重量,取消下料") print("无法获取当前重量,取消下料")
@ -312,8 +341,8 @@ class FeedingProcess:
target_weight = loc_state._mould_need_weight target_weight = loc_state._mould_need_weight
if (current_weight is not None and loc_state._mould_finish_weight >= target_weight): if (current_weight is not None and loc_state._mould_finish_weight >= target_weight):
loc_state.feed_status = FeedStatus.FFinished loc_state._feed_status = FeedStatus.FFinished
loc_state._lower_feeding_stage = 5 loc_state.lower_feeding_stage = 5
self.is_first_flag=True self.is_first_flag=True
return return
else: else:
@ -348,13 +377,13 @@ class FeedingProcess:
# 继续等待当前模具车对齐(不需要重新等待对齐,因为是同一辆模具车) # 继续等待当前模具车对齐(不需要重新等待对齐,因为是同一辆模具车)
print("第二次上料完成,继续三阶段下料") print("第二次上料完成,继续三阶段下料")
self.state._lower_feeding_stage = 1 # 直接进入第一阶段下料 self.state.lower_feeding_stage = 1 # 直接进入第一阶段下料
self.feeding_stage_one() # 开始第二轮第一阶段下料 self.feeding_stage_one() # 开始第二轮第一阶段下料
def finish_feeding_process(self): def finish_feeding_process(self):
"""完成整个下料流程""" """完成整个下料流程"""
print("整个下料流程完成") print("整个下料流程完成")
self.state._lower_feeding_stage = 0 self.state.lower_feeding_stage = 0
self.state.lower_feeding_cycle = 0 self.state.lower_feeding_cycle = 0
self.state.upper_feeding_count = 0 self.state.upper_feeding_count = 0
# self.return_upper_door_to_default() # self.return_upper_door_to_default()

View File

@ -470,9 +470,10 @@ class command_hex:
""" """
# 寻找连续三个0x2C的情况 # 寻找连续三个0x2C的情况
for idx in range(len(response) - 2): # for idx in range(len(response) - 2):
if response[idx] == 0x2C and response[idx+1] == 0x2C and response[idx+2] == 0x2C: # if response[idx] == 0x2C and response[idx+1] == 0x2C and response[idx+2] == 0x2C:
response=response[:idx] # response=response[:idx]
# break
# 验证响应长度 # 验证响应长度
if len(response) <= 1 or len(response) != response[0] + 1: if len(response) <= 1 or len(response) != response[0] + 1:
raise ValueError("应答数据长度不正确") raise ValueError("应答数据长度不正确")
@ -499,7 +500,12 @@ class command_hex:
loc_string = '' loc_string = ''
if data_part: if data_part:
try: try:
loc_string = data_part.decode('ascii') 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: except UnicodeDecodeError:
print(f"无法将数据转换为ASCII字符串: {data_part}") print(f"无法将数据转换为ASCII字符串: {data_part}")
return loc_string return loc_string

View File

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

View File

@ -1,14 +1,16 @@
# hardware/relay.py # hardware/relay.py
import socket import socket
import binascii import binascii
import time
import threading
from pymodbus.client import ModbusTcpClient from pymodbus.client import ModbusTcpClient
from pymodbus.exceptions import ModbusException from pymodbus.exceptions import ModbusException
from config.settings import Settings from config.settings import app_set_config
class RelayController: class RelayController:
# 继电器映射 # 继电器映射
FAST_STOP = 'fast_stop' # DO1 - 急停 RING = 'ring' # DO1 - 响铃
UPPER_TO_JBL = 'upper_to_jbl' # DO2 - 上料斗到搅拌楼 UPPER_TO_JBL = 'upper_to_jbl' # DO2 - 上料斗到搅拌楼
UPPER_TO_ZD = 'upper_to_zd' # DO3 - 上料斗到振捣室 UPPER_TO_ZD = 'upper_to_zd' # DO3 - 上料斗到振捣室
# DOOR_UPPER = 'door_upper' # DO0 - 上料斗滑动 # DOOR_UPPER = 'door_upper' # DO0 - 上料斗滑动
@ -20,38 +22,37 @@ class RelayController:
BREAK_ARCH_LOWER = 'break_arch_lower' # DO4 - 下料斗震动 BREAK_ARCH_LOWER = 'break_arch_lower' # DO4 - 下料斗震动
DIRECT_LOWER_FRONT = 'direct_lower_front' # DO5 - 下料斗前 DIRECT_LOWER_FRONT = 'direct_lower_front' # DO5 - 下料斗前
DIRECT_LOWER_BEHIND = 'direct_lower_behind' # DO6 - 下料斗后 DIRECT_LOWER_BEHIND = 'direct_lower_behind' # DO6 - 下料斗后
DIRECT_LOWER_LEFT = 'direct_lower_left' # DO7 - 下料斗 DIRECT_LOWER_TOP = 'direct_lower_top' # DO7 - 下料斗
DIRECT_LOWER_RIGHT = 'direct_lower_right' # DO8 - 下料斗 DIRECT_LOWER_BELOW = 'direct_lower_below' # DO8 - 下料斗
def __init__(self, host='192.168.250.62', port=50000): def __init__(self, host='192.168.250.62', port=50000):
self.host = host self.host = host
self.port = port self.port = port
self.modbus_client = ModbusTcpClient(host, 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 # 继电器命令原始Socket
self.relay_commands = { 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_JBL: {'open': '00000000000601050001FF00', 'close': '000000000006010500010000'},
self.UPPER_TO_ZD: {'open': '00000000000601050002FF00', 'close': '000000000006010500020000'}, self.UPPER_TO_ZD: {'open': '00000000000601050002FF00', 'close': '000000000006010500020000'},
self.DOOR_LOWER_OPEN: {'open': '00000000000601050006FF00', 'close': '00000000000601050006FF00'}, 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_OPEN: {'open': '00000000000601050003FF00', 'close': '000000000006010500030000'},
self.DOOR_UPPER_CLOSE: {'open': '00000000000601050004FF00', 'close': '000000000006010500040000'}, self.DOOR_UPPER_CLOSE: {'open': '00000000000601050004FF00', 'close': '000000000006010500040000'},
self.BREAK_ARCH_UPPER: {'open': '0000000000060105000AFF00', 'close': '0000000000060105000A0000'}, self.BREAK_ARCH_UPPER: {'open': '0000000000060105000AFF00', 'close': '0000000000060105000A0000'},
self.BREAK_ARCH_LOWER: {'open': '00000000000601050005FF00', 'close': '000000000006010500050000'}, self.BREAK_ARCH_LOWER: {'open': '00000000000601050005FF00', 'close': '000000000006010500050000'},
self.DIRECT_LOWER_FRONT: {'open': '00000000000601050005FF00', 'close': '000000000006010500050000'}, self.DIRECT_LOWER_FRONT: {'open': '0000000000060105000DFF00', 'close': '0000000000060105000D0000'},
self.DIRECT_LOWER_BEHIND: {'open': '00000000000601050005FF00', 'close': '000000000006010500050000'}, self.DIRECT_LOWER_BEHIND: {'open': '0000000000060105000BFF00', 'close': '0000000000060105000B0000'},
self.DIRECT_LOWER_LEFT: {'open': '00000000000601050005FF00', 'close': '000000000006010500050000'}, self.DIRECT_LOWER_TOP: {'open': '0000000000060105000CFF00', 'close': '0000000000060105000C0000'},
self.DIRECT_LOWER_RIGHT: {'open': '00000000000601050005FF00', 'close': '000000000006010500050000'} self.DIRECT_LOWER_BELOW: {'open': '0000000000060105000EFF00', 'close': '0000000000060105000E0000'}
} }
self.settings = Settings()
# 读取状态命令 # 读取状态命令
self.read_status_command = '000000000006010100000008' self.read_status_command = '000000000006010100000008'
# 设备位映射 # 设备位映射
self.device_bit_map = { self.device_bit_map = {
self.FAST_STOP: 0, self.RING: 0,
self.UPPER_TO_JBL: 1, self.UPPER_TO_JBL: 1,
self.UPPER_TO_ZD: 2, self.UPPER_TO_ZD: 2,
self.BREAK_ARCH_UPPER: 3, self.BREAK_ARCH_UPPER: 3,
@ -60,7 +61,7 @@ class RelayController:
def send_command(self, command_hex): def send_command(self, command_hex):
"""发送原始Socket命令""" """发送原始Socket命令"""
if not self.settings.debug_feeding: if app_set_config.debug_mode:
return None return None
try: try:
@ -93,7 +94,40 @@ class RelayController:
def control(self, device, action): def control(self, device, action):
"""控制继电器""" """控制继电器"""
if device in self.relay_commands and action in self.relay_commands[device]: 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]) self.send_command(self.relay_commands[device][action])
else: else:
print(f"无效设备或动作: {device}, {action}") 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 # hardware/transmitter.py
from pymodbus.exceptions import ModbusException from pymodbus.exceptions import ModbusException
import socket
from config.ini_manager import ini_manager
from config.settings import app_set_config
class TransmitterController: class TransmitterController:
def __init__(self, relay_controller): def __init__(self, relay_controller):
self.relay_controller = relay_controller self.relay_controller = relay_controller
# 变送器配置 # 变送器配置
self.config = { self.config = {
1: { # 上料斗 1: { # 上料斗
@ -75,88 +78,57 @@ class TransmitterController:
Args: transmitter_id 为1 表示上料斗, 为2 表示下料斗 Args: transmitter_id 为1 表示上料斗, 为2 表示下料斗
return: 读取成功返回重量 weight: int, 失败返回 None return: 读取成功返回重量 weight: int, 失败返回 None
""" """
TIMEOUT = 2 # 超时时间为 2秒
BUFFER_SIZE= 1024
IP = None
PORT = None
weight = 0
if transmitter_id == 1: if transmitter_id == 1:
# 上料斗变送器的信息: # 上料斗变送器的信息:
IP = "192.168.250.63" IP = ini_manager.upper_transmitter_ip
PORT = 502 PORT = ini_manager.upper_transmitter_port
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
elif transmitter_id == 2: elif transmitter_id == 2:
# 料斗变送器的信息: # 料斗变送器的信息:
IP = "192.168.250.66" IP = ini_manager.lower_transmitter_ip
PORT = 8234 PORT = ini_manager.lower_transmitter_port
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 if not IP or not PORT:
return weight 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): def get_latest_valid_packet(self, raw_data):
""" """

74
main.py
View File

@ -1,17 +1,49 @@
# main.py # main.py
import time import time
from config.settings import Settings from config.settings import app_set_config
from core.system import FeedingControlSystem from core.system import FeedingControlSystem
from hardware import relay
from hardware.relay import RelayController
import threading
import time
def main(): 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) # time.sleep(2)
# system._alignment_check_loop() # system._alignment_check_loop()
# system.relay_controller.control(system.relay_controller.DOOR_LOWER_OPEN, 'open') # system.relay_controller.control(system.relay_controller.DOOR_LOWER_OPEN, 'open')
@ -27,29 +59,17 @@ def main():
# time.sleep(5) # time.sleep(5)
# system.relay_controller.control(system.relay_controller.DOOR_LOWER_CLOSE, 'close') # system.relay_controller.control(system.relay_controller.DOOR_LOWER_CLOSE, 'close')
# system._visual_control_loop() # 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__": if __name__ == "__main__":
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() objects = self.client.get_objects_node()
print(f"对象节点: {objects}") print(f"对象节点: {objects}")
# 浏览 IndustrialDevice 节点
upper_device = objects.get_child("2:upper") upper_device = objects.get_child("2:upper")
print(f"\n工业设备节点: {upper_device}") print(f"\n上料斗对象: {upper_device}")
# 获取传感器节点
lower_device = objects.get_child("2:lower") lower_device = objects.get_child("2:lower")
print(f"传感器节点: {lower_device}") print(f"下料斗对象: {lower_device}")
print("\n=== 当前对象属性===")
print(f"温度传感器: {upper_device}") self.read_object_properties(upper_device, lower_device)
print(f"压力传感器: {lower_device}")
# 获取变量值
print("\n=== 当前传感器数据 ===")
self.read_sensor_values(upper_device, lower_device)
except Exception as e: except Exception as e:
print(f"浏览节点时出错: {e}") print(f"浏览节点时出错: {e}")
def read_sensor_values(self, upper_device, lower_device): def read_object_properties(self, upper_device, lower_device):
"""读取传感器数值""" """读取重量数值"""
try: try:
# 读取温度 # 读取重量
temp_value = upper_device.get_child("2:upper_weight").get_value() upper_weight = upper_device.get_child("2:upper_weight").get_value()
temp_unit = upper_device.get_child("2:lower_weight").get_value() lower_weight = lower_device.get_child("2:lower_weight").get_value()
print(f"温度: {temp_value} {temp_unit}") print(f"上料斗重量: {upper_weight}")
print(f"下料斗重量: {lower_weight}")
except Exception as e: except Exception as e:
print(f"读取传感器数据时出错: {e}") print(f"读取数据时出错: {e}")
def monitor_data(self, duration=30): def monitor_data(self, duration=30):
"""监控数据变化""" """监控数据变化"""

View File

@ -10,9 +10,10 @@ import random
import threading import threading
from datetime import datetime from datetime import datetime
from core.system import SystemState from core.system import SystemState
from config.ini_manager import ini_manager
class SimpleOPCUAServer: 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服务器 初始化OPC UA服务器
@ -54,8 +55,8 @@ class SimpleOPCUAServer:
self.lower_weight = self.lower.add_variable(self.namespace, "lower_weight", 0.0) self.lower_weight = self.lower.add_variable(self.namespace, "lower_weight", 0.0)
# 设置变量为可写 # 设置变量为可写
self.upper_weight.set_writable() # self.upper_weight.set_writable()
self.lower_weight.set_writable() # self.lower_weight.set_writable()
def setup_state_listeners(self): def setup_state_listeners(self):
"""设置状态监听器 - 事件驱动更新""" """设置状态监听器 - 事件驱动更新"""
@ -83,9 +84,7 @@ class SimpleOPCUAServer:
try: try:
self.server.start() self.server.start()
self.running = True self.running = True
print(f"OPC UA服务器启动成功!")
print(f"服务器端点: opc.tcp://0.0.0.0:4840/freeopcua/server/") print(f"服务器端点: opc.tcp://0.0.0.0:4840/freeopcua/server/")
print(f"命名空间: {self.namespace}")
# 初始化当前值 # 初始化当前值
if self.state: if self.state:
@ -109,8 +108,6 @@ class SimpleOPCUAServer:
def stop(self): def stop(self):
"""停止服务器""" """停止服务器"""
self.running = False self.running = False
if hasattr(self, 'simulation_thread'):
self.simulation_thread.join(timeout=2)
self.server.stop() self.server.stop()
print("OPC UA服务器已停止") print("OPC UA服务器已停止")
@ -121,21 +118,6 @@ class SimpleOPCUAServer:
except: except:
pass 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(): def main():
"""主函数""" """主函数"""

View File

@ -2,7 +2,7 @@ from datetime import datetime, timedelta
from common.sqlite_handler import SQLiteHandler from common.sqlite_handler import SQLiteHandler
from typing import Optional, List from typing import Optional, List
from .api_http_client import api_http_client 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 from config.ini_manager import ini_manager
@ -80,59 +80,114 @@ class MouldService:
except Exception as e: except Exception as e:
print(f"请求未浇筑管片信息异常: {e}") print(f"请求未浇筑管片信息异常: {e}")
return None return None
if __name__ == "__main__": def get_pouring_led(self) -> Optional[LEDInfo]:
# 创建模具服务实例 """
mould_service = MouldService() 获取生产动态信息
db = SQLiteHandler.get_instance("db/three.db", max_readers=50, busy_timeout=4000)
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() # not_poured = mould_service.get_not_pour_artifacts()
if not_poured: # if not_poured:
for item in not_poured: # for item in not_poured:
artifact = db.fetch_one("SELECT * FROM ArtifactTask WHERE ArtifactID = ?", (item.ArtifactID,)) # artifact = db.fetch_one("SELECT * FROM ArtifactTask WHERE ArtifactID = ?", (item.ArtifactID,))
if not artifact: # if not artifact:
dict={ # dict={
"ArtifactID": item.ArtifactID, # "ArtifactID": item.ArtifactID,
"ArtifactActionID": item.ArtifactActionID, # "ArtifactActionID": item.ArtifactActionID,
"ArtifactIDVice1": item.ArtifactIDVice1, # "ArtifactIDVice1": item.ArtifactIDVice1,
"ProduceRingNumber": item.ProduceRingNumber, # "ProduceRingNumber": item.ProduceRingNumber,
"MouldCode": item.MouldCode, # "MouldCode": item.MouldCode,
"SkeletonID": item.SkeletonID, # "SkeletonID": item.SkeletonID,
"RingTypeCode": item.RingTypeCode, # "RingTypeCode": item.RingTypeCode,
"SizeSpecification": item.SizeSpecification, # "SizeSpecification": item.SizeSpecification,
"BuriedDepth": item.BuriedDepth, # "BuriedDepth": item.BuriedDepth,
"BlockNumber": item.BlockNumber, # "BlockNumber": item.BlockNumber,
"BetonVolume": item.BetonVolume, # "BetonVolume": item.BetonVolume,
"BetonTaskID": item.BetonTaskID, # "BetonTaskID": item.BetonTaskID,
"HoleRingMarking": item.HoleRingMarking, # "HoleRingMarking": item.HoleRingMarking,
"GroutingPipeMarking": item.GroutingPipeMarking, # "GroutingPipeMarking": item.GroutingPipeMarking,
"PolypropyleneFiberMarking": item.PolypropyleneFiberMarking, # "PolypropyleneFiberMarking": item.PolypropyleneFiberMarking,
"Status": 1, # "Status": 1,
"Source": 1 # "Source": 1
} # }
db.insert("ArtifactTask", dict) # db.insert("ArtifactTask", dict)
dict={ # dict={
"TaskID": item.BetonTaskID, # "TaskID": item.BetonTaskID,
"ProjectName": "上海市轨道交通19号线工程盾构区间管片生产2标", # "ProjectName": "上海市轨道交通19号线工程盾构区间管片生产2标",
"ProduceMixID": "20251030-02", # "ProduceMixID": "20251030-02",
"VinNo": "", # "VinNo": "",
"BetonVolume": item.BetonVolume, # "BetonVolume": item.BetonVolume,
"MouldCode": item.MouldCode, # "MouldCode": item.MouldCode,
"SkeletonID": item.SkeletonID, # "SkeletonID": item.SkeletonID,
"RingTypeCode": item.RingTypeCode, # "RingTypeCode": item.RingTypeCode,
"SizeSpecification": item.SizeSpecification, # "SizeSpecification": item.SizeSpecification,
"BuriedDepth": item.BuriedDepth, # "BuriedDepth": item.BuriedDepth,
"BlockNumber": item.BlockNumber, # "BlockNumber": item.BlockNumber,
"Mode": 1, # "Mode": 1,
"Status": 1, # "Status": 1,
"Source": 1, # "Source": 1,
"OptTime": str(datetime.now() - timedelta(minutes=5)), # "OptTime": str(datetime.now() - timedelta(minutes=5)),
"CreateTime": str(datetime.now()) # "CreateTime": str(datetime.now())
} # }
db.insert("PDRecord", dict) # db.insert("PDRecord", dict)
# for i in range(2, 5): # for i in range(2, 5):
# row = db.fetch_one("SELECT * FROM ArtifactTask WHERE ID = ?", (i,)) # row = db.fetch_one("SELECT * FROM ArtifactTask WHERE ID = ?", (i,))
# if row: # if row:

View File

@ -9,4 +9,9 @@ login_model = {"Program": 11, "SC": "1000000001", "loginName": "leduser", "passw
[app] [app]
log_path = logs/app.log log_path = logs/app.log
db_path = db/three.db 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 from unittest.mock import patch, MagicMock
import sys import sys
import os import os
from config.settings import app_set_config
# 添加项目根目录到Python路径 # 添加项目根目录到Python路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) 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.InverterController'), \
patch('feeding.process.TransmitterController'): patch('feeding.process.TransmitterController'):
system = FeedingProcess() system = FeedingProcess()
# 通过settings修改参数 system.settings.single_batch_weight = 1500 #修改参数 app_set_config.single_batch_weight = 1500
system.settings.min_required_weight = 300 app_set_config.min_required_weight = 300
system.settings.target_vehicle_weight = 3000 app_set_config.target_vehicle_weight = 3000
self.assertEqual(system.settings.target_vehicle_weight, 3000) self.assertEqual(app_set_config.target_vehicle_weight, 3000)
self.assertEqual(system.settings.min_required_weight, 300) self.assertEqual(app_set_config.min_required_weight, 300)
self.assertEqual(system.settings.single_batch_weight, 1500) self.assertEqual(app_set_config.single_batch_weight, 1500)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -19,7 +19,7 @@ def test_rfid_functions():
测试RFIDHardware的主要功能 测试RFIDHardware的主要功能
""" """
# 初始化RFID控制器 # 初始化RFID控制器
rfid = rfid_service(host='192.168.1.190', port=6000) rfid = rfid_service(host='192.168.250.67', port=6000)
# print("=== RFID硬件测试开始 ===") # print("=== RFID硬件测试开始 ===")
@ -41,6 +41,12 @@ def test_rfid_functions():
# rfid.set_working_mode(address=0x00, mode_params={ # rfid.set_working_mode(address=0x00, mode_params={
# 'word_num': 0x1E # '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. 测试读取读写器信息:") # print("\n3. 测试读取读写器信息:")
@ -54,7 +60,7 @@ def test_rfid_functions():
# 测试设置功率 (仅演示,实际使用时根据需要调整) # 测试设置功率 (仅演示,实际使用时根据需要调整)
# print("\n3. 测试设置功率 (演示,不实际执行):") # 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 '失败'}") # print(f"功率设置{'成功' if power_success else '失败'}")
# # 测试设置读卡间隔 (仅演示) # # 测试设置读卡间隔 (仅演示)
@ -87,7 +93,7 @@ def test_rfid_functions():
# rfid._process_collected_data() # rfid._process_collected_data()
rfid.start_receiver(callback=test_data_callback) rfid.start_receiver(callback=test_data_callback)
# print("接收线程已启动,等待接收数据...") # print("接收线程已启动,等待接收数据...")
# 等待5秒模拟接收过程 # 等待5秒模拟接收过程1111111111111
time.sleep(60*60) time.sleep(60*60)
finally: finally:
# 确保停止接收线程 # 确保停止接收线程

View File

@ -62,6 +62,30 @@ class DualCameraController:
# print('aaaaa') # print('aaaaa')
ret, frame = cap.read() ret, frame = cap.read()
if ret and frame is not None: 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() timestamp = time.time()
# 检查队列是否已满 # 检查队列是否已满
@ -216,11 +240,11 @@ class DualCameraController:
dt_t1, frame_latest = self.frame_queues['cam1'].queue[-1] dt_t1, frame_latest = self.frame_queues['cam1'].queue[-1]
# 获取cam2的最新帧选择时间戳更新的那个 # 获取cam2的最新帧选择时间戳更新的那个
if frame_latest is None: # if frame_latest is None:
if not self.frame_queues['cam2'].empty(): if not self.frame_queues['cam2'].empty():
dt_t2, frame2 = self.frame_queues['cam2'].queue[-1] dt_t2, frame2 = self.frame_queues['cam2'].queue[-1]
if dt_t1 is None or dt_t2 > dt_t1: if dt_t1 is None or dt_t2 > dt_t1:
frame_latest = frame2 frame_latest = frame2
# 返回最新帧的副本(如果找到) # 返回最新帧的副本(如果找到)
return frame_latest.copy() if frame_latest is not None else None 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.angle_detector import get_current_door_angle
from vision.overflow_detector import detect_overflow_from_image from vision.overflow_detector import detect_overflow_from_image
from vision.alignment_detector import detect_vehicle_alignment from vision.alignment_detector import detect_vehicle_alignment
from config.settings import app_set_config
class VisionDetector: class VisionDetector:
def __init__(self, settings): def __init__(self):
self.settings = settings pass
#model路径在对应的模型里面 #model路径在对应的模型里面
# self.alignment_model = os.path.join(current_dir, "align_model/yolov11_cls_640v6.rknn") # self.alignment_model = os.path.join(current_dir, "align_model/yolov11_cls_640v6.rknn")
@ -21,36 +22,36 @@ class VisionDetector:
# 加载夹角检测模型 # 加载夹角检测模型
try: try:
if not os.path.exists(self.settings.angle_model_path): if not os.path.exists(app_set_config.angle_model_path):
print(f"夹角检测模型不存在: {self.settings.angle_model_path}") print(f"夹角检测模型不存在: {app_set_config.angle_model_path}")
success = False success = False
else: else:
# 注意angle.pt模型通过predict_obb_best_angle函数使用不需要预加载 # 注意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: except Exception as e:
print(f"检查夹角检测模型失败: {e}") print(f"检查夹角检测模型失败: {e}")
success = False success = False
# 加载堆料检测模型 # 加载堆料检测模型
try: try:
if not os.path.exists(self.settings.overflow_model_path): if not os.path.exists(app_set_config.overflow_model_path):
print(f"堆料检测模型不存在: {self.settings.overflow_model_path}") print(f"堆料检测模型不存在: {app_set_config.overflow_model_path}")
success = False success = False
else: else:
self.overflow_model = YOLO(self.settings.overflow_model_path) self.overflow_model = YOLO(app_set_config.overflow_model_path)
print(f"成功加载堆料检测模型: {self.settings.overflow_model_path}") print(f"成功加载堆料检测模型: {app_set_config.overflow_model_path}")
except Exception as e: except Exception as e:
print(f"加载堆料检测模型失败: {e}") print(f"加载堆料检测模型失败: {e}")
success = False success = False
# 加载对齐检测模型 # 加载对齐检测模型
try: try:
if not os.path.exists(self.settings.alignment_model_path): if not os.path.exists(app_set_config.alignment_model_path):
print(f"对齐检测模型不存在: {self.settings.alignment_model_path}") print(f"对齐检测模型不存在: {app_set_config.alignment_model_path}")
success = False success = False
else: else:
self.alignment_model = YOLO(self.settings.alignment_model_path) self.alignment_model = YOLO(app_set_config.alignment_model_path)
print(f"成功加载对齐检测模型: {self.settings.alignment_model_path}") print(f"成功加载对齐检测模型: {app_set_config.alignment_model_path}")
except Exception as e: except Exception as e:
print(f"加载对齐检测模型失败: {e}") print(f"加载对齐检测模型失败: {e}")
success = False success = False
@ -64,7 +65,7 @@ class VisionDetector:
# image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
image=cv2.flip(image, 0) image=cv2.flip(image, 0)
return get_current_door_angle( return get_current_door_angle(
model=self.settings.angle_model_path, model=app_set_config.angle_model_path,
image=image 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.imwrite('test.jpg', image_array)
cv2.namedWindow("Alignment", cv2.WINDOW_NORMAL) # cv2.namedWindow("Alignment", cv2.WINDOW_NORMAL)
cv2.resizeWindow("Alignment", 640, 480) # cv2.resizeWindow("Alignment", 640, 480)
cv2.imshow("Alignment", image_array) # cv2.imshow("Alignment", image_array)
cv2.waitKey(1) # cv2.waitKey(1)
print('path:', self.settings.overflow_model_path) # print('path:', app_set_config.overflow_model_path)
print('roi:', self.settings.roi_file_path) # print('roi:', app_set_config.roi_file_path)
return detect_overflow_from_image( return detect_overflow_from_image(
self.settings.overflow_model_path, app_set_config.overflow_model_path,
self.settings.roi_file_path, app_set_config.roi_file_path,
image_array 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