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