0313界面对接

This commit is contained in:
2026-03-13 21:04:19 +08:00
parent 6e74eaf206
commit 8aeaffa885
33 changed files with 1541 additions and 419 deletions

View File

@ -3,6 +3,6 @@
包含系统核心控制逻辑和状态管理
"""
from .system import FeedingControlSystem
from .state import SystemState
from .system_state import SystemState
__all__ = ['FeedingControlSystem', 'SystemState']

396
core/pd_task_service.py Normal file
View File

@ -0,0 +1,396 @@
"""任务处理服务"""
import sys
import os
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
from datetime import datetime
from common.helpers import get_f_block_positions
import time
# from busisness.blls import PDRecordBll
class PD_TaskService:
def __init__(self):
# self.api_client = APIClient()
self.half_volume = [0, 0]
self.task_before = {"block_number":None, "beton_volume":None, "artifact_id":None}
self.artifact_timestamps = {}
# self.tcp_server = tcp_server
# self.pd_record_bll=PDRecordBll()
# from config.settings import TCP_CLIENT_HOST, TCP_CLIENT_PORT
# self.data_client = OPCClient(url=f'opc.tcp://{TCP_CLIENT_HOST}:{TCP_CLIENT_PORT}')
# self.data_client.start()
def process_not_pour_info(self,artifact_list):
"""处理未浇筑信息"""
#artifact_list[{'ArtifactActionID','BetonTaskID','BetonVolume','BetonTaskID'},{},{}]
# artifact_list = self.api_client.get_not_pour_info()
#mould_code 当前正在浇筑的模具编号
#artifact_list = self.pd_record_bll.get_last_pds(mould_code,3)
# 如果API调用失败返回空列表而不是抛出异常
if artifact_list is None:
return [], [], [], self.half_volume
if not artifact_list:
return [], [], [], self.half_volume
artifact_list = [person.__dict__ for person in artifact_list]
# 处理F块信息
f_blocks_info = self._process_f_blocks(artifact_list)
f_blocks = f_blocks_info["f_blocks"]
f_block_count = f_blocks_info["f_block_count"]
total_f_volume = f_blocks_info["total_f_volume"]
f_positions = f_blocks_info["f_positions"]
# 处理当前任务
#task{BetonGrade,MixID}
current_task = self._process_current_task(artifact_list[0])
# 如果获取任务信息失败,则返回空结果
if current_task is None or not current_task.get("artifact_id"):
return [], [], [], self.half_volume
# 根据F块情况处理任务
task_result = self._handle_tasks_by_f_blocks(
f_block_count, f_positions, current_task,
f_blocks, total_f_volume, artifact_list
)
# 更新上一个任务信息
self.task_before = {
"beton_task_id": current_task["beton_task_id"],
"beton_volume": round(current_task["beton_volume"],1),
"artifact_id": current_task["artifact_id"],
"block_number": current_task["block_number"]
}
return task_result
def _process_f_blocks(self, artifact_list):
"""处理F块相关信息"""
f_blocks = [artifact for artifact in artifact_list if artifact.get("BlockNumber") == "F"]
f_block_count = len(f_blocks)
total_f_volume = sum(artifact["BetonVolume"] for artifact in f_blocks)
f_positions = get_f_block_positions(artifact_list)
return {
"f_blocks": f_blocks,
"f_block_count": f_block_count,
"total_f_volume": total_f_volume,
"f_positions": f_positions
}
def _process_current_task(self, latest_artifact):
"""处理当前任务信息"""
#task_data = self.api_client.get_task_info(latest_artifact["BetonTaskID"])
# 如果API调用失败返回None
#if task_data is None:
# return None
#"beton_task_id": latest_artifact["BetonTaskID"],
return {
"beton_task_id": latest_artifact["TaskID"],
"beton_volume": latest_artifact["BetonVolume"],
"artifact_id": latest_artifact["ArtifactActionID"],
"block_number": latest_artifact.get("BlockNumber", ""),
"produce_mix_id": latest_artifact.get("ProduceMixID", ""),
"planned_volume": latest_artifact.get("PlannedVolume", 0),
"id": latest_artifact.get("ID", 0),
"task_data": latest_artifact
}
def _handle_tasks_by_f_blocks(self, f_block_count, f_positions, current_task,
f_blocks, total_f_volume, artifact_list):
"""根据F块数量和位置处理任务"""
# 多个F块情况
if f_block_count > 2:
return self._handle_multiple_f_blocks(current_task, total_f_volume, artifact_list)
# 两个F块情况
elif f_block_count == 2:
return self._handle_two_f_blocks(f_positions, current_task, total_f_volume, artifact_list)
# 一个F块情况
elif f_block_count == 1:
return self._handle_single_f_block(f_positions, current_task, f_blocks,
total_f_volume, artifact_list)
# 无F块情况
elif f_block_count == 0:
return self._handle_no_f_blocks(current_task, artifact_list)
else:
print("报警")
return [], [], [], self.half_volume
def _handle_multiple_f_blocks(self, current_task, total_f_volume, artifact_list):
"""处理多个F块的情况"""
if self.task_before.get("block_number") == "F":
print("报警:,超出正常补块逻辑")
else:
adjusted_volume = total_f_volume - self.half_volume[0]
tasks = [{
"beton_task_id": current_task["beton_task_id"],
"beton_volume": adjusted_volume,
"artifact_id": current_task["artifact_id"],
"block_number": current_task["block_number"],
"produce_mix_id": current_task.get("produce_mix_id", ""),
"planned_volume": current_task.get("planned_volume", 0),
"id": current_task.get("id", 0)
}]
send_list = [{
"beton_task_id": current_task["beton_task_id"],
"beton_volume": adjusted_volume,
"artifact_id": current_task["artifact_id"],
"block_number": current_task["block_number"],
"beton_grade": current_task["task_data"]["BetonGrade"],
"mix_id": current_task["task_data"]["ProduceMixID"],
"time": self.artifact_timestamps.get(current_task["artifact_id"], datetime.now())
}]
# 更新时间戳
#self._update_artifact_timestamps(send_list)
return tasks, artifact_list, send_list, self.half_volume
def _handle_two_f_blocks(self, f_positions, current_task, total_f_volume, artifact_list):
"""处理两个F块的情况"""
if f_positions == [0, 1] and self.task_before.get("block_number") == "F":
adjusted_volume = 0
block_number = "补方"
elif f_positions == [1, 2]:
adjusted_volume = artifact_list[0]["BetonVolume"]
block_number = current_task["block_number"]
elif f_positions == [0, 1] and self.task_before.get("block_number") != "F":
adjusted_volume = total_f_volume - self.half_volume[0]
block_number = current_task["block_number"]
else:
print("报警")
tasks = [{
"beton_task_id": current_task["beton_task_id"],
"beton_volume": adjusted_volume,
"artifact_id": current_task["artifact_id"],
"block_number": block_number,
"produce_mix_id": current_task.get("produce_mix_id", ""),
"planned_volume": current_task.get("planned_volume", 0),
"id": current_task.get("id", 0)
}]
send_list = [{
"beton_task_id": current_task["beton_task_id"],
"beton_volume": adjusted_volume,
"artifact_id": current_task["artifact_id"],
"block_number": block_number,
"beton_grade": current_task["task_data"]["BetonGrade"],
"mix_id": current_task["task_data"]["ProduceMixID"],
"time": self.artifact_timestamps.get(current_task["artifact_id"], datetime.now())
}]
# 更新时间戳
#self._update_artifact_timestamps(send_list)
return tasks, artifact_list, send_list, self.half_volume
def _handle_single_f_block(self, f_positions, current_task, f_blocks, total_f_volume, artifact_list):
"""处理单个F块的情况"""
if f_positions == [2]:
f_volume = f_blocks[0].get("BetonVolume") if f_blocks else 0
self.half_volume[0] = 0.1#round(total_f_volume / 2, 2)#暂时修改为0.1
self.half_volume[1] = 0.1#f_volume - self.half_volume[0]#暂时修改
adjusted_volume = round(current_task["beton_volume"],1) + self.half_volume[0]
elif f_positions == [1]:
adjusted_volume = round(current_task["beton_volume"],1) + self.half_volume[1]
elif f_positions == [0]:
adjusted_volume = 0
else:
adjusted_volume = round(current_task["beton_volume"],1)
block_number = "补方" if f_positions == [0] else current_task["block_number"]
tasks = [{
"beton_task_id": current_task["beton_task_id"],
"beton_volume": adjusted_volume,
"artifact_id": current_task["artifact_id"],
"block_number": block_number,
"produce_mix_id": current_task.get("produce_mix_id", ""),
"planned_volume": current_task.get("planned_volume", 0),
"id": current_task.get("id", 0)
}]
send_list = [{
"beton_task_id": current_task["beton_task_id"],
"beton_volume": adjusted_volume,
"artifact_id": current_task["artifact_id"],
"block_number": block_number,
"beton_grade": current_task["task_data"]["BetonGrade"],
"mix_id": current_task["task_data"]["ProduceMixID"],
"time": self.artifact_timestamps.get(current_task["artifact_id"], datetime.now())
}]
# 更新时间戳
#self._update_artifact_timestamps(send_list)
return tasks, artifact_list, send_list, self.half_volume
def _handle_no_f_blocks(self, current_task, artifact_list):
"""处理无F块的情况"""
tasks = [{
"beton_task_id": current_task["beton_task_id"],
"beton_volume": round(current_task["beton_volume"],1),
"artifact_id": current_task["artifact_id"],
"block_number": current_task["block_number"],
"produce_mix_id": current_task.get("produce_mix_id", ""),
"planned_volume": current_task.get("planned_volume", 0),
"id": current_task.get("id", 0),
}]
send_list = [{
"beton_task_id": current_task["beton_task_id"],
"beton_volume": round(current_task["beton_volume"],1),
"artifact_id": current_task["artifact_id"],
"block_number": current_task["block_number"],
"beton_grade": current_task["task_data"]["BetonGrade"],
"mix_id": current_task["task_data"]["ProduceMixID"],
"time": self.artifact_timestamps.get(current_task["artifact_id"], datetime.now())
}]
# 更新时间戳
#self._update_artifact_timestamps(send_list)
return tasks, artifact_list, send_list, self.half_volume
def _update_artifact_timestamps(self, send_list):
"""更新artifact时间戳"""
current_artifact_ids = {item["artifact_id"] for item in send_list}
for artifact_id in current_artifact_ids:
if artifact_id not in self.artifact_timestamps:
self.artifact_timestamps[artifact_id] = datetime.now()
def insert_into_produce_table(self, connection, task_info, beton_volume, erp_id, artifact_id, half_volume, status):
"""插入数据到Produce表"""
sql_db = SQLServerDB()
if status == 1:
# 准备插入数据
insert_data = {
"ErpID": erp_id,
"Code": task_info["TaskID"],
"DatTim": datetime.now(),
"Recipe": task_info["ProduceMixID"],
"MorRec": "",
"ProdMete": beton_volume,
"MorMete": 0.0, # 砂浆方量,根据实际需求填写
"TotVehs": 0, # 累计车次,根据实际需求填写
"TotMete": task_info["PlannedVolume"], # 累计方量
"Qualitor": "", # 质检员,根据实际需求填写
"Acceptor": "", # 现场验收,根据实际需求填写
"Attamper": "", # 调度员,根据实际需求填写
"Flag": "1", # 标识,根据实际需求填写
"Note": "" # 备注,根据实际需求填写
}
sql_db.insert_produce_data(insert_data)
print(f"数据已成功插入到Produce表中ERP ID: {erp_id}")
try:
# 参数包括: MISID(即erp_id), Flag, TaskID, ProduceMixID, ProjectName, BetonGrade, 调整后的方量
self.save_to_custom_table(
misid=erp_id,
flag="已插入", # 初始Flag值
task_id=task_info["TaskID"],
produce_mix_id=task_info["ProduceMixID"],
project_name=task_info["ProjectName"],
beton_grade=task_info["BetonGrade"],
adjusted_volume=round(beton_volume,1),
artifact_id=artifact_id,
half_volume=half_volume,
# 已经调整后的方量
)
print(f"任务 {erp_id} 的数据已保存到自定义数据表")
except Exception as e:
print(f"调用保存函数时出错: {e}")
if self.tcp_server:
try:
time.sleep(2)
task_data = {
"erp_id": erp_id, # 车号,相当于序号
"task_id": task_info["TaskID"], # 任务单号
"artifact_id": artifact_id,
"produce_mix_id": task_info["ProduceMixID"], # 配比号
"project_name": task_info["ProjectName"], # 任务名
"beton_grade": task_info["BetonGrade"], # 砼强度
"adjusted_volume": round(beton_volume,1), # 方量
"flag": "已插入", # 状态
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S") # 时间
}
self.tcp_server.send_data(task_data)
print(f"任务 {artifact_id} 的数据已发送给TCP客户端")
except Exception as e:
print(f"发送数据给TCP客户端时出错: {e}")
return erp_id
else:
try:
# 参数包括: MISID(即erp_id), Flag, TaskID, ProduceMixID, ProjectName, BetonGrade, 调整后的方量
self.save_to_custom_table(
misid=erp_id,
flag="已插入", # 初始Flag值
task_id=task_info["TaskID"],
produce_mix_id=task_info["ProduceMixID"],
project_name=task_info["ProjectName"],
beton_grade=task_info["BetonGrade"],
adjusted_volume=round(beton_volume,1),
artifact_id=artifact_id,
half_volume=half_volume,
)
print(f"任务 {erp_id} 的数据已保存到自定义数据表")
except Exception as e:
print(f"调用保存函数时出错: {e}")
return erp_id
def save_to_custom_table(self, misid, flag, task_id, produce_mix_id, project_name, beton_grade, adjusted_volume, artifact_id,half_volume):
try:
task_data = {
"erp_id": misid,
"task_id": task_id,
"artifact_id": artifact_id,
"produce_mix_id": produce_mix_id,
"project_name": project_name,
"beton_grade": beton_grade,
"adjusted_volume": adjusted_volume,
"flag": flag,
"half_volume":half_volume,
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
# self.data_client.send_data(task_data)
print(f"任务 {artifact_id} 的数据已发送到另一台电脑")
except Exception as e:
print(f"发送数据到另一台电脑时出错: {e}")
print(f"原计划保存到自定义数据表: MISID={misid}, Flag={flag}, TaskID={task_id}, 调整后方量={adjusted_volume}")
def update_custom_table_status(self, erp_id, status):
try:
status_data = {
"cmd": "update_status",
"erp_id": erp_id,
"status": status,
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
# self.data_client.send_data(status_data)
print(f"任务状态更新已发送到另一台电脑: ERP ID={erp_id}, 状态={status}")
except Exception as e:
print(f"发送状态更新到另一台电脑时出错: {e}")
print(f"原计划更新自定义数据表状态: ERP ID={erp_id}, 状态={status}")
if __name__ == "__main__":
pd_task_service = PD_TaskService()
ret=pd_task_service.process_not_pour_info('SHR2B3-5')
i=100

133
core/relay_feed.py Normal file
View File

@ -0,0 +1,133 @@
# hardware/relay.py
import socket
import binascii
import time
import threading
from pymodbus.exceptions import ModbusException
from config.settings import app_set_config
#下料过程控制
class RelayFeedController:
def __init__(self, relay,state):
self.relay = relay
self.state = state
# 添加线程锁,保护对下料斗控制的并发访问
self.door_control_lock = threading.Lock()
def control_upper_close_after(self):
"""控制上料斗关在几秒后"""
# 关闭上料斗出砼门
self.relay.control(self.DOOR_UPPER_OPEN, 'close')
# 异步5秒后关闭
threading.Thread(target=self._close_upper_after_s, daemon=True,name="close_upper_after_s").start()
def control_upper_close_sync(self,duration=5):
self.relay.control(self.DOOR_UPPER_OPEN, 'close')
self.relay.control(self.DOOR_UPPER_CLOSE, 'open')
time.sleep(duration)
self.relay.control(self.DOOR_UPPER_CLOSE, 'close')
def control_upper_open_sync(self,duration):
self.relay.control(self.DOOR_UPPER_CLOSE, 'close')
self.relay.control(self.DOOR_UPPER_OPEN, 'open')
time.sleep(duration)
self.relay.control(self.DOOR_UPPER_OPEN, 'close')
def control_ring_open(self):
"""控制下料斗关"""
# 关闭下料斗出砼门
self.relay.control(self.RING, 'open')
# 异步5秒后关闭
threading.Thread(target=self._close_ring, daemon=True,name="_close_ring").start()
def _close_upper_after_s(self):
"""
异步5秒后关闭上料斗20秒
"""
# time.sleep(5)
self.relay.control_arch_upper_open_sync(5)
self.relay.control(self.DOOR_UPPER_CLOSE, 'open')
time.sleep(1)
self.relay.control(self.DOOR_UPPER_CLOSE, 'close')
self.relay.control_arch_upper_open_sync(5)
# self.relay.control_arch_upper_open_sync(5)
self.relay.control_arch_upper_open_async(8)
self.relay.control(self.DOOR_UPPER_CLOSE, 'open')
time.sleep(20)
self.relay.control(self.DOOR_UPPER_CLOSE, 'close')
print("上料斗关闭完成")
def _close_lower_5s(self):
time.sleep(6)
self.relay.control(self.DOOR_LOWER_CLOSE, 'close')
def _close_ring(self):
time.sleep(3)
self.relay.control(self.RING, 'close')
def control_arch_lower_open(self):
"""控制下料斗关"""
# 关闭下料斗出砼门
self.relay.control(self.BREAK_ARCH_LOWER, 'open')
# 异步5秒后关闭
threading.Thread(target=self._close_break_arch_lower, daemon=True,name="_close_break_arch_lower").start()
def control_arch_lower_open_sync(self,duration):
"""控制下料斗振动"""
self.state._lower_is_arch = True
self.relay.control(self.BREAK_ARCH_LOWER, 'open')
# 异步5秒后关闭
time.sleep(duration)
self.relay.control(self.BREAK_ARCH_LOWER, 'close')
self.state._lower_is_arch = False
def control_arch_upper_open_sync(self,duration):
"""控制下料斗振动"""
self.state._upper_is_arch=True
self.relay.control(self.BREAK_ARCH_UPPER, 'open')
# 异步5秒后关闭
time.sleep(duration)
self.relay.control(self.BREAK_ARCH_UPPER, 'close')
self.state._upper_is_arch=False
def _close_break_arch_lower(self):
time.sleep(3)
self.relay.control(self.BREAK_ARCH_LOWER, 'close')
def control_arch_upper_open_async(self,delay_seconds: float = 15):
"""异步控制上料斗振动
Args:
delay_seconds: 延迟关闭时间默认15秒
"""
# 关闭下料斗出砼门
self.relay.control(self.BREAK_ARCH_UPPER, 'open')
# 异步5秒后关闭
threading.Thread(target=lambda d: self._close_break_arch_upper(delay_seconds),args=(delay_seconds,), daemon=True, name="_close_break_arch_upper").start()
def _close_break_arch_upper(self, delay_seconds: float = 15):
time.sleep(delay_seconds)
print(f"上料斗振动关闭完成,延迟{delay_seconds}")
self.relay.control(self.BREAK_ARCH_UPPER, 'close')
def control_arch_lower_open_async(self,delay_seconds: float = 15):
"""异步控制上料斗振动
Args:
delay_seconds: 延迟关闭时间默认15秒
"""
# 关闭下料斗出砼门
self.relay.control(self.BREAK_ARCH_LOWER, 'open')
# 异步5秒后关闭
threading.Thread(target=lambda d: self._close_break_arch_lower(delay_seconds),args=(delay_seconds,), daemon=True, name="_close_break_arch_lower").start()
def _close_break_arch_lower(self, delay_seconds: float = 15):
time.sleep(delay_seconds)
print(f"下料斗振动关闭完成,延迟{delay_seconds}")
self.relay.control(self.BREAK_ARCH_LOWER, 'close')

View File

@ -4,7 +4,8 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import threading
import time
import queue
from core.system_state import SystemState,FeedStatus
import json
from core.system_state import SystemState,FeedStatus,Upper_Door_Position
from hardware.relay import RelayController
from hardware.inverter import InverterController
from hardware.transmitter import TransmitterController
@ -12,14 +13,20 @@ from config.ini_manager import ini_manager
from hardware.upper_plc import OmronFinsPollingService
from vision.visual_callback_dq import VisualCallback
from opc.opcua_client_feed import OpcuaClientFeed
from busisness.blls import ArtifactBll,PDRecordBll
from busisness.models import ArtifactInfoModel,PDRecordModel
from busisness.blls import ArtifactBll,PDRecordBll,FreqRecordBll
from busisness.models import ArtifactInfoModel,PDRecordModel,FreqRecordModel
from core.pd_task_service import PD_TaskService
class FeedingControlSystem:
def __init__(self):
print('FeedingControlSystem初始化')
self.state = SystemState()
self.pd_task_service = PD_TaskService()
self.pd_record_bll=PDRecordBll()
self.freq_record_bll=FreqRecordBll()
# 初始化硬件控制器
self.relay_controller = RelayController(
@ -44,7 +51,7 @@ class FeedingControlSystem:
#小屏修改过屏幕
self.vf_auto_mode=True
# 初始化 OPC UA 客户端
self.opcua_client_feed = OpcuaClientFeed()
self.opcua_client_feed = OpcuaClientFeed(notify_callback=self.on_opcua_notify)
# 线程管理
self.feed_thread = None
@ -55,6 +62,9 @@ class FeedingControlSystem:
# 初始化 OPC 队列监听线程,用于处理队列中的数据
self.opc_queue_thread = None
#处理频率上传线程
self.freq_thread=None
def initialize(self)->bool:
"""初始化系统"""
print("初始化控制系统...")
@ -75,8 +85,12 @@ class FeedingControlSystem:
self.start_pd_thread()
# 启动OPC队列处理线程维护连接的断开重连等
self.opcua_client_feed.start()
#开启接收通知数据(queue线程)
self.opcua_client_feed.start_accept()
self.start_opc_queue_thread()
#启用频率上传线程
self.start_freq_thread()
print("控制系统初始化完成")
return True
@ -95,7 +109,7 @@ class FeedingControlSystem:
self.opc_queue_thread = threading.Thread(
target=self._process_opc_queue,
daemon=True,
name='opc_queue_processor'
name='send_queue_processor'
)
self.opc_queue_thread.start()
@ -123,6 +137,9 @@ class FeedingControlSystem:
_begin_time=None
_wait_times=300
_start_wait_seconds=None
_is_start=False
#处理振捣频率
while self.state.running:
try:
# if self.feeding_controller._is_finish_ratio>=0.6:
@ -136,12 +153,13 @@ class FeedingControlSystem:
if _start_wait_seconds is None:
#记录盖板对齐时间
_start_wait_seconds=time.time()
if self.feeding_controller._is_finish_ratio>=0.02:
if self.state._mould_finish_ratio>=0.05:
_elasped_time=time.time()-_start_wait_seconds
if _elasped_time<10:
time.sleep(10-_elasped_time)
self.inverter_controller.control('start',230)
print("----振捣已经启动-----")
_is_start=True
print("----浇筑已经启动-----")
_begin_time=time.time()
self.state._mould_frequency=230
self.state._mould_vibrate_status=True
@ -152,12 +170,21 @@ class FeedingControlSystem:
print("----振捣300秒-----")
_wait_time=300
else:
print("----下料重量小于46KG,暂时不振捣-----")
# else:
print("----下料重量小于46KG,暂时不振捣-----")
else:
if self.state._mould_finish_ratio>=0.3:
if self.state._mould_frequency!=220:
self.inverter_controller.set_frequency(220)
self.state._mould_frequency=220
elif self.state._mould_finish_ratio>=0.4:
if self.state._mould_frequency!=230:
self.inverter_controller.set_frequency(230)
self.state._mould_frequency=230
elif self.state.vf_status==3 and _begin_time is not None:
if time.time()-_begin_time>=_wait_time:
if self.vf_auto_mode:
self.inverter_controller.control('stop')
_is_start=False
self.state._mould_vibrate_status=False
_begin_time=None
_start_wait_seconds=None
@ -201,6 +228,7 @@ class FeedingControlSystem:
_ret=artifact_bll.save_artifact_task(_model_data)
if _ret > 0:
# 标记为已处理
self.state._sys_segment_refresh=1
processed_artifact_actions.append(item.MouldCode)
if len(processed_artifact_actions) > 4:
processed_artifact_actions.pop(0)
@ -255,6 +283,7 @@ class FeedingControlSystem:
continue
_ret=pdrecord_bll.save_PD_record(_pd_record_data)
if _ret > 0:
self.state._sys_segment_refresh=1
# 标记为已处理
processed_pd_records.append(item.MouldCode)
# 限制最多保存3条记录删除最旧的
@ -280,7 +309,7 @@ class FeedingControlSystem:
if item:
public_name, value = item
# 这里可以添加实际的OPC处理逻辑
print(f"Processing OPC update: {public_name} = {value}")
print(f"控制程序opc数据上传: {public_name} = {value}")
self.opcua_client_feed.write_value_by_name(public_name, value)
# 标记任务完成
self.state.opc_queue.task_done()
@ -356,22 +385,137 @@ class FeedingControlSystem:
_isFinish=False
_start_time=None
while self.state.running:
#增加生产阶段检测,
if self.state._feed_status==FeedStatus.FCheckGB:
if not _isFinish:
if _start_time is None:
_start_time=time.time()
_isSuccess=self.feeding_controller.send_pd_data()
if _isSuccess:
_isFinish=True
if time.time()-_start_time>60:
print('派单超时,人工介入')
_isFinish=True
elif self.state._feed_status==FeedStatus.FFinished:
_start_time=None
_isFinish=False
#设置的派单模式
if self.state.pd_set_mode==1:
#增加生产阶段检测,
if self.state._feed_status==FeedStatus.FCheckGB:
if not _isFinish:
if _start_time is None:
_start_time=time.time()
_isSuccess=self.send_pd_data()
if _isSuccess:
_isFinish=True
if time.time()-_start_time>60:
print('派单超时,人工介入')
_isFinish=True
elif self.state._feed_status==FeedStatus.FFinished:
_start_time=None
_isFinish=False
time.sleep(5)
def send_pd_data(self):
"""
发送PD数据到OPC队列
"""
# 构建PD数据
_cur_mould=self.state.current_mould
if _cur_mould is not None:
if _cur_mould.MouldCode:
_pdrecords = self.pd_record_bll.get_last_pds(_cur_mould.MouldCode,3)
if _pdrecords:
_pdrecord=_pdrecords[0]
if _pdrecord.TaskID:
if _pdrecord.Status==1:
tasks, artifact_list, send_list, half_volume = self.pd_task_service.process_not_pour_info(_pdrecords)
# if tasks and len(tasks)>0:
# task=tasks[0]
# # task['MouldCode']=_pdrecord.MouldCode
# #更新派单表
# _ret_flag=self.pd_record_bll.start_pd(_pdrecord.MouldCode,task['beton_volume'])
# if _ret_flag:
# _opc_pd={
# "ID":task['id'],
# "ArtifactActionID":task['artifact_id'],
# "TaskID":task['beton_task_id'],
# "ProduceMixID":task['produce_mix_id'],
# "BetonVolume":task['beton_volume'],
# "PlannedVolume":task['planned_volume']
# }
# print(f"{self.state.current_mould.MouldCode}派单:{_pdrecord.MouldCode},数据:{_opc_pd}")
# self.state.opc_queue.put_nowait(('pd_data', json.dumps(_opc_pd)))
if _pdrecord.BlockNumber=='F':
print(f'{_pdrecord.MouldCode} F块不发送派单数据')
self.pd_record_bll.start_pd(_pdrecord.MouldCode,0)
else:
_fact_volumn=self.get_fact_volumn(_pdrecord.MouldCode,_pdrecord.BlockNumber)
if _fact_volumn>0:
#更新派单表
_ret_flag=self.pd_record_bll.start_pd(_pdrecord.MouldCode,_fact_volumn)
if _ret_flag:
_opc_pd={
"ID":_pdrecord.ID,
"ArtifactActionID":_pdrecord.ArtifactActionID,
"TaskID":_pdrecord.TaskID,
"ProduceMixID":_pdrecord.ProduceMixID,
"BetonVolume":_fact_volumn,
"PlannedVolume":_pdrecord.PlannedVolume
}
print(f"{self.state.current_mould.MouldCode}派单:{_pdrecord.MouldCode}-{_pdrecord.BlockNumber},数据:{_opc_pd}")
self.state.opc_queue.put_nowait(('pd_data', json.dumps(_opc_pd)))
return True
else:
print(f'{_pdrecord.MouldCode} 未派单,当前状态为:{_pdrecord.Status}')
return False
else:
print(f'{_pdrecord.MouldCode} 未获取到数据-(等待扫码)')
return False
else:
print(f'接口数据为空')
return False
else:
return None
def get_fact_volumn(self,mould_code:str,block_number:str='') -> float:
"""获取实际派单发量"""
# _now_volume=0
_pd_volume=0
_init_lower_weight=100#self.transmitter_controller.read_data(2)
if _init_lower_weight is None:
print(f'获取重量异常,上料斗传感器错误')
print(f'获取重量异常,上料斗传感器错误')
return None
print(f'get_fact_volumn当前重量{_init_lower_weight}')
_per_volume=2480
_left_volume=_init_lower_weight/_per_volume
if not block_number and '-' in mould_code:
block_number = mould_code.split('-')[0][-2:]
if block_number=='B1':
_left_volume=_left_volume-0.54
if _left_volume>0:
_pd_volume=1.9-_left_volume
_pd_volume=math.ceil(_pd_volume*10)/10
else:
_pd_volume=1.9
elif block_number in ['B2','B3']:
_pd_volume=1.9
elif block_number=='L1':
#浇B3
_pd_volume=2
self.prev_pd_volume=2
#调整
elif block_number=='L2':
#2.4方大约L2和F的量
_pd_volume=2.4
# if _weight>1300:
#留0.15 math.floor(_now_volume*10)/10 保留一位小数,丢掉其他的
if self.prev_pd_volume>0:
_pd_volume=_pd_volume-(_left_volume+self.prev_pd_volume-1.86)
else:
#不知道上一块叫的多少
_pd_volume=_pd_volume-(_left_volume+2-1.86)
_pd_volume=math.ceil(_pd_volume*10)/10+0.1
if _pd_volume>2.1:
_pd_volume=2.1
elif _pd_volume<0.8:
_pd_volume=0.8
return _pd_volume
def start_led(self):
"""启动LED流程"""
self.led_thread = threading.Thread(
@ -388,8 +532,8 @@ class FeedingControlSystem:
while self.state.running:
led_info = app_web_service.get_pouring_led()
if led_info:
if self.state.current_artifact.MouldCode==led_info.MouldCode:
led_info.RingTypeCode=self.state.current_artifact.RingTypeCode
if self.state.mould_code==led_info.MouldCode:
led_info.RingTypeCode=led_info.RingTypeCode
led_info.UpperWeight=self.state._upper_weight
led_info.LowerWeight=self.state._lower_weight
led_info.VibrationFrequency=self.state._mould_frequency
@ -398,16 +542,68 @@ class FeedingControlSystem:
time.sleep(app_set_config.led_interval)
def on_opcua_notify(self,var_name,val):
"""OPC UA通知回调"""
if var_name=='pd_set_mode':
self.state.pd_set_mode=val
elif var_name=='pd_set_volume' and val:
_notify=json.loads(val)
_ret=self.pd_record_bll.update_PD_record_byid(_notify['ID'],{'FBetonVolume':_notify['Volume']})
if _ret:
#刷新派单表
self.state.opc_queue.put_nowait(('sys_pd_refresh',1))
# self.state._db_pd_set_volume=val
elif var_name=='pd_notify' and val:
# self.state._db_pd_notify=val
_notify=json.loads(val)
if _notify['ID']:
if _notify['Flag']==3:
#生产完毕
_ret=self.pd_record_bll.update_PD_record_byid(_notify['ID'],{'GStatus':3,'Status':5})
# if _ret:
#发送命令
# if self.state._upper_door_position==Upper_Door_Position.JBL:
# self.relay_controller.control_upper_to_zd()
else:
self.pd_record_bll.update_PD_record_byid(_notify['ID'],{'GStatus':_notify['Flag']})
print(f'on_opcua_notify收到,var_name:{var_name},val:{val}')
def start_freq_thread(self):
"""启动频率上传线程"""
self.freq_thread = threading.Thread(
target=self._start_freq,
name="Freq",
daemon=True
)
self.freq_thread.start()
def _start_freq(self):
"""启动频率上传线程"""
while self.state.running:
# 上传频率
#未上传到IOT平台保存到数据库
_cur_mould=self.state.current_mould
if _cur_mould:
_code=_cur_mould.ArtifactID
_id=_cur_mould.ArtifactActionID
_mcode=_cur_mould.MouldCode
# freq=self.inverter_controller.read_frequency()
freq=self.state._mould_frequency
if freq>0:
self.freq_record_bll.insert_freq_record(FreqRecordModel(ArtifactID=_code,ArtifactActionID=_id,MouldCode=_mcode,Freq=freq))
else:
print('振捣频率未保存(因为当前浇筑的模具号为空)')
time.sleep(1)
@property
def _is_finish(self):
"""检查系统是否运行"""
return self.feeding_controller._is_finish
"""检查系统是否完成"""
return self.state._feed_status==FeedStatus.FFinished
@property
def _is_finish_ratio(self):
"""检查系统是否运行"""
return self.feeding_controller._is_finish_ratio
"""完成比例"""
return self.state._mould_finish_ratio
@property
def vibrate_status(self):
@ -439,6 +635,8 @@ class FeedingControlSystem:
self.arch_thread.join()
if self.plc_service:
self.plc_service.stop_polling()
self.opcua_client_feed.stop_run()
self.feeding_controller.shutdown()
# 释放摄像头资源
# self.camera_controller.release()
@ -448,6 +646,7 @@ class FeedingControlSystem:
if __name__ == "__main__":
system = FeedingControlSystem()
system.initialize()
system.opcua_client_feed.subscribe_node(system.state.feed_status)
time.sleep(2)
system.state._upper_weight=1000

View File

@ -2,7 +2,9 @@ import threading
from enum import IntEnum
import queue
import json
import time
from dataclasses import asdict
from typing_extensions import Optional
from busisness.blls import ArtifactBll,PDRecordBll
class SystemState:
@ -18,47 +20,61 @@ class SystemState:
# 系统运行状态
self.running = True
# 上料斗控制相关
self._upper_door_position = Upper_Door_Position.Default # default(在搅拌楼下接料), over_lower(在下料斗上方), returning(返回中)
# 是否破拱
self._upper_is_arch_=False
self._upper_door_closed=True
# 上料斗是否振动True:是False:否)
self._upper_is_arch=False
#上料斗门关闭状态0:关闭,1:半开2全开)
self._upper_door_closed=0
#上料斗重量
self._upper_weight=0
#上料斗剩余方量
self._upper_volume=0.0
# 上料斗控制相关
self._upper_door_position = Upper_Door_Position.JBL
#低料斗是否振动True:是False:否)
self._lower_is_arch=False
self._lower_weight=0
#完成下料的总重量
self._mould_finish_weight=0
#时间戳记录上一次发送到OPC的时间不用每次都传
self.opc_timestamp=time.time()
#需要下料的总重量
# self._mould_need_weight=0
self._mould_finish_ratio=0
#当前振捣频率
self._mould_frequency=230
#模具是否捣动中True:是False:否)
self._mould_vibrate_status=False #True振捣中False未振捣
#当前模具状态
self._feed_status=FeedStatus.FNone
#下料比例变频
self.vf_frequencys=[{'radio':0,'fre':230},{'radio':0.3,'fre':230},{'radio':0.6,'fre':230}]
#使用
self._mould_frequency=230
self._mould_vibrate_status=False #True振动中False未振动
#记录模具开始振动的时间
self.mould_vibrate_time=0
#下料斗状态
self._lower_is_arch_=False
self._lower_weight=0
#当前浇筑模具ArtifactInfo对象需要属性ArtifactID,ArtifactActionID,MouldCode RFID情况下绑定
self.current_mould=None
#模具车状态
self._mould_weight=0
#需要下料的总重量
self._mould_need_weight=0
#完成下料的总重量
self._mould_finish_weight=0
# 视觉系统状态
self.overflow_detected = "0" # 堆料检测
#当前生产的管片
self.current_artifact=None
#当前生产状态
self._feed_status=FeedStatus.FNone
self.bll_artifact=ArtifactBll()
self.bll_pdrecord=PDRecordBll()
#记录正在生产code模具编号,status:2正生产3完成生成,weight:完成重量
self._db_status={'code':'','status':1,'weight':0}
#记录生产的模具编号,更新到数据库的管片生成任务状态,status:2正生产3完成生成,weight:完成重量
self._db_mould_status={'mould_code':'','status':1,'weight':0}
#派单数据发送到OPC
self._pd_data=''
# self._pd_data=''
#设置派单模式(1自动派单 2手动派单0未知)
self.pd_set_mode=0
#管片刷新通知UI
self._sys_segment_refresh=0
#派单刷新通知UI
self._sys_pd_refresh=0
#变频器相关
#是否启动变频器0未1普通块启动2F块启动 3结束
@ -74,18 +90,19 @@ class SystemState:
if public_name.startswith('db'):
self.save_db(public_name,value) #有影响的话改成异步
else:
if public_name=="pd_data":
#更新派单表
if hasattr(value,'MouldCode'):
self.bll_pdrecord.start_pd(value.MouldCode,value.FBetonVolume)
_opc_pd={
"ArtifactID":value.ArtifactID,
"TaskID":value.TaskID,
"ProduceMixID":value.ProduceMixID,
"BetonVolume":value.FBetonVolume,
"PlannedVolume":value.PlannedVolume
}
self.opc_queue.put_nowait((public_name, json.dumps(_opc_pd)))
if public_name=='mould_finish_weight':
if time.time()-self.opc_timestamp>=1:
self.opc_queue.put_nowait((public_name, value))
self.opc_timestamp=time.time()
elif public_name=='mould_finish_ratio':
if time.time()-self.opc_timestamp>=1:
self.opc_queue.put_nowait((public_name, value))
self.opc_timestamp=time.time()
elif value>=1:
#保证比例能到100%
self.opc_queue.put_nowait((public_name, value))
self.opc_queue.put_nowait(('mould_finish_weight', self._mould_finish_weight))
self.opc_timestamp=time.time()
else:
self.opc_queue.put_nowait((public_name, value))
except queue.Full:
@ -100,8 +117,8 @@ class SystemState:
def save_db(self,public_name,val):
if not val:
return
if public_name=="db_status":
_code=val['code']
if public_name=="db_mould_status":
_code=val['mould_code']
if _code:
_status=val['status']
if _status==3:
@ -133,18 +150,18 @@ class FeedStatus(IntEnum):
FFinished = 11
class Upper_Door_Position(IntEnum):
# default(在搅拌楼下接料), over_lower(在下料斗上方), returning(返回中)
Default = 0
Over_Lower = 1
Returning = 2
# JBL(在搅拌楼), ZDS(在振捣室), Returning(返回中)
JBL = 1
ZDS = 2
Returning = 3
class Upper_PLC_Status(IntEnum):
# 即将振捣室
PLC_ZDS_Ready = 4
#到达振捣室
PLC_ZDS_Finish = 5
#即将搅拌楼
PLC_JBL_Ready = 64
#到达搅拌楼
PLC_JBL_Finish = 66
# class Upper_PLC_Status(IntEnum):
# # 即将振捣室
# PLC_ZDS_Ready = 4
# #到达振捣室
# PLC_ZDS_Finish = 5
# #即将搅拌楼
# PLC_JBL_Ready = 64
# #到达搅拌楼
# PLC_JBL_Finish = 66