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

@ -6,8 +6,8 @@
from dataclasses import fields
from typing import List, Optional, Dict, Any
from datetime import datetime
from busisness.models import ArtifactInfoModel, PDRecordModel
from busisness.dals import ArtifactDal, PDRecordDal
from busisness.models import ArtifactInfoModel, PDRecordModel,FreqRecordModel
from busisness.dals import ArtifactDal, PDRecordDal,FreqRecordDal
class ArtifactBll:
@ -109,9 +109,9 @@ class PDRecordBll:
"""获取PD官片任务数据"""
return self.dal.get_top_pd(5, "ID desc")
def get_last_pds(self,mould_code:str) -> List[PDRecordModel]:
def get_last_pds(self,mould_code:str,top=1) -> List[PDRecordModel]:
"""获取当前浇筑的后三片的派据(有可能其中一项为空,但需要保证第一条有记录)"""
return self.dal.get_last_pds(1,mould_code)
return self.dal.get_last_pds(top,mould_code)
def insert_PD_record(self,model:PDRecordModel) -> bool:
"""保存PD官片任务(存在更新,不存在插入)"""
@ -127,12 +127,16 @@ class PDRecordBll:
def start_pd(self, code: str,volumn:float) -> bool:
"""开始管片生产"""
return self.dal.update_by_modulecode(code, {'Status': 2,'OptTime':datetime.now(),'FBetonVolume':volumn})
return self.dal.update_by_modulecode(code, {'Status': 2,'GStatus':1,'OptTime':datetime.now(),'FBetonVolume':volumn})
def update_PD_record(self,artifact_action_id: int, update_fields: dict) -> bool:
"""更新PD官片任务状态"""
return self.dal.update_pd(artifact_action_id, update_fields)
def update_PD_record_byid(self,id: int, update_fields: dict) -> bool:
"""更新PD官片任务状态"""
return self.dal.update_pd_byid(id, update_fields)
def save_PD_record(self,model:PDRecordModel) -> int:
"""保存PD官片任务(存在ModuleCode更新不存在插入存在ArtifactID不处理)"""
@ -155,13 +159,27 @@ class PDRecordBll:
'SizeSpecification':model.SizeSpecification,
'BlockNumber':model.BlockNumber,
'BetonVolume':model.BetonVolume,
'GStatus':model.GStatus,
'PlannedVolume':model.PlannedVolume
}
return self.dal.update_by_modulecode(model.MouldCode, update_dict)
return 1
class FreqRecordBll:
def __init__(self):
"""初始化数据访问层,创建数据库连接"""
# 假设数据库文件在db目录下
self.dal = FreqRecordDal()
pass
def get_freqs_sync(self) -> List[FreqRecordModel]:
"""获取所有未同步的频率记录"""
return self.dal.get_all_sync()
def insert_freq_record(self,model:FreqRecordModel) -> int:
"""插入一条频率记录"""
return self.dal.insert_record(model)
if __name__ == "__main__":
# artifact_dal = ArtifactBll()

View File

@ -4,7 +4,7 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from dataclasses import fields
from typing import List, Optional, Dict, Any
from datetime import datetime, timedelta
from busisness.models import ArtifactInfoModel,PDRecordModel
from busisness.models import ArtifactInfoModel,PDRecordModel,FreqRecordModel
from common.sqlite_handler import SQLiteHandler
@ -20,6 +20,7 @@ class BaseDal:
"""初始化数据访问层,创建数据库连接"""
# 假设数据库文件在db目录下
self.db_dao = SQLiteHandler.get_instance("db/three.db", max_readers=50, busy_timeout=4000)
self.db_dao_record = SQLiteHandler.get_instance("db/three-record.db", max_readers=50, busy_timeout=4000)
class ArtifactDal(BaseDal):
def __init__(self):
@ -202,6 +203,7 @@ class PDRecordDal(BaseDal):
pdrecord = PDRecordModel()
pdrecord.ID=row["ID"]
pdrecord.ArtifactID=row["ArtifactID"]
pdrecord.ArtifactActionID=row["ArtifactActionID"]
pdrecord.TaskID=row["TaskID"]
pdrecord.ProjectName=row["ProjectName"]
pdrecord.ProduceMixID=row["ProduceMixID"]
@ -231,12 +233,12 @@ class PDRecordDal(BaseDal):
# 确保top为正整数
if not isinstance(top, int) or top <= 0:
raise ValueError("top参数必须是正整数")
_target_time = datetime.now() - timedelta(hours=4)
# 查询指定数量的记录按ID降序排列
# _target_time = datetime.now() - timedelta(hours=4)
_target_time ='2026-02-06 00:00:00'
# 查询当前浇筑模具编号的前一条记录
sql = f"""SELECT * FROM PDRecord WHERE CreateTime>? and ID>(
select ID FROM PDRecord WHERE MouldCode=? and CreateTime>?
)
order by ID asc LIMIT ?"""
select ID FROM PDRecord WHERE MouldCode=? and CreateTime>?
) order by ID asc LIMIT ?"""
results = self.db_dao.execute_read(sql, (_target_time,mould_code,_target_time,top))
pdrecords = []
@ -316,17 +318,67 @@ order by ID asc LIMIT ?"""
print(f"更新PD管片任务失败: {e}")
return False
def update_by_modulecode(self, module_code: str, update_data: dict) -> int:
def update_pd_byid(self, id: int, update_data: dict) -> bool:
"""更新PD官片任务记录"""
try:
# 构建WHERE条件
where_condition = f"ID={id}"
# 使用update方法更新数据
affected_rows = self.db_dao.update("PDRecord", update_data, where_condition)
return affected_rows > 0
except Exception as e:
print(f"更新PD管片任务失败: {e}")
return False
def update_by_modulecode(self, module_code: str, update_data: dict) -> bool:
"""更新构件派单记录"""
try:
# 构建WHERE条件
_target_time = datetime.now() - timedelta(hours=4)
_target_time ='2026-02-06 00:00:00'
where_condition = f"MouldCode='{module_code}' and CreateTime>='{_target_time}'"
# 使用update方法更新数据
affected_rows = self.db_dao.update("PDRecord", update_data, where_condition)
return affected_rows
return affected_rows > 0
except Exception as e:
print(f"更新PD官片任务失败: {e}")
return False
class FreqRecordDal(BaseDal):
def __init__(self):
super().__init__()
def get_all_sync(self) -> List[FreqRecordModel]:
"""获取未同步的记录"""
try:
# 查询所有记录
sql = "SELECT * FROM FreqRecord WHERE IsSync=0"
results = self.db_dao.execute_read(sql)
# 将查询结果转换为FreqRecord对象列表
artifacts = []
for row in results:
# 过滤字典,只保留模型中定义的字段
# filtered_data = filter_dict_for_model(dict(row), ArtifactInfoModel)
artifact = FreqRecordModel(**row)
artifacts.append(artifact)
return artifacts
except Exception as e:
print(f"获取所有频率记录失败: {e}")
return []
def insert_record(self, model: FreqRecordModel) -> int:
"""插入一条频率记录"""
try:
# 使用insert方法插入数据
model.OptTime=datetime.now()
model.__dict__.pop("ID", None)
row_id = self.db_dao_record.insert("FreqRecord", model.__dict__)
return row_id
except Exception as e:
print(f"插入频率记录失败: {e}")
return 0

View File

@ -276,9 +276,9 @@ class PDRecordModel:
BlockNumber: str=""
# 派单模式(1自动派单 2手动派单0未知
Mode: int=0
# 派单状态1计划中2已下发0未知)默认1
# 派单状态(1计划中2已下发0未知 3已超时 4未扫码))默认1
Status: int=1
#搅拌生产状态()
#搅拌生产状态(搅拌生产状态(1未进行生产,2正在生产3生产完毕,4生产中断5已插入搅拌系统6已取消)
GStatus: int=0
#数据来源(1 api 2离线)
Source: int=1
@ -289,6 +289,25 @@ class PDRecordModel:
#派单时间(下发)
OptTime: str=""
@dataclass
class FreqRecordModel:
"""频率记录"""
ID: int=0
#模具编号
ArtifactID: str=""
#模具ID
ArtifactActionID: int=0
#模具编号
MouldCode: str=""
#骨架编号
Freq: float=0.0
#是否已同步 0否 1是
IsSync: int=0
#记录时间
OptTime: str=""
@dataclass
class LEDInfo:
"""LED信息模型"""

43
common/helpers.py Normal file
View File

@ -0,0 +1,43 @@
"""工具函数模块"""
def get_f_block_positions(artifact_list):
"""获取artifact_list中F块的位置"""
positions = []
for i, artifact in enumerate(artifact_list):
if artifact.get("BlockNumber") == "F":
positions.append(i)
return positions
def cleanup_old_timestamps(artifact_timestamps, current_artifact_ids=None, max_age_hours=24):
"""
清理过期的时间戳记录
Args:
artifact_timestamps: 时间戳字典
current_artifact_ids: 当前活跃的artifact_id集合可选
max_age_hours: 时间戳最大保留时间小时默认24小时
"""
from datetime import datetime
current_time = datetime.now()
expired_ids = []
# 遍历所有存储的时间戳记录
for artifact_id, timestamp in artifact_timestamps.items():
# 如果提供了当前活跃ID列表且该ID不在当前活跃列表中则检查是否过期
if current_artifact_ids is None or artifact_id not in current_artifact_ids:
age = current_time - timestamp
# 如果超过最大保留时间,则标记为过期
if age.total_seconds() > max_age_hours * 3600:
expired_ids.append(artifact_id)
# 删除过期的时间戳记录
for artifact_id in expired_ids:
del artifact_timestamps[artifact_id]
if expired_ids:
print(f"清理了 {len(expired_ids)} 个过期的时间戳记录: {expired_ids}")
return len(expired_ids)

View File

@ -108,14 +108,16 @@ class SQLiteHandler:
"""SQLite数据库操作通用类单例模式"""
_lock = threading.Lock() # 单例锁
_instance = None
_instances = {}
@classmethod
def get_instance(cls, *args, **kwargs):
if cls._instance is None:
def get_instance(cls,db_path, *args, **kwargs):
# 使用文件路径作为键
key = db_path
if key not in cls._instances:
with cls._lock:
if cls._instance is None: # 双重检查
cls._instance = cls(*args, **kwargs)
return cls._instance
if key not in cls._instances: # 双重检查
cls._instances[key] = cls(db_path, *args, **kwargs)
return cls._instances[key]
def __init__(self, db_path: str = "three.db", max_readers: int = 10, busy_timeout: int = 5000):
"""
@ -184,6 +186,7 @@ class SQLiteHandler:
def _create_connection(self) -> sqlite3.Connection:
"""创建新的数据库连接"""
conn = sqlite3.connect(self.db_path, **self._connection_params)
conn.set_trace_callback(lambda x: print(x))
conn.row_factory = sqlite3.Row
return conn

158
common/util_time.py Normal file
View File

@ -0,0 +1,158 @@
import time
class MyTimer:
@staticmethod
def gMyGetTickCount():
ts = time.time()
return int(ts * 1000) # Convert to milliseconds
# CTon class equivalent in Python
class CTon:
def __init__(self):
#已经经过的时间
self.m_unET = 0
#上一次的输入状态(布尔值)
self.m_bLastI = False
#当前的输入状态(布尔值)
self.m_bIn = False
#暂停状态(布尔值)
self.m_bPause = False
#完成状态(布尔值)
self.m_bOver = True
#预设时间(延时时间,毫秒)
self.m_unPT = 0
#开始时间戳(毫秒)
self.m_unStartTime = 0
#暂停时已经经过的时间(毫秒)
self.m_unPauseET = 0
def GetET(self):
if self.m_bIn:
nET = self.m_unPT + (self.m_unStartTime - MyTimer.gMyGetTickCount())
return max(nET, 0)
else:
return 0
def SetReset(self):
self.m_bIn = False
self.m_bLastI = False
self.m_bPause = False
def SetPause(self, value):
if self.m_bIn:
self.m_bPause = value
if self.m_bPause:
self.m_unPauseET = MyTimer.gMyGetTickCount() - self.m_unStartTime
def SetOver(self, value):
self.m_bOver = value
def GetStartTime(self):
return self.m_unStartTime
"""
CTon.Q() 方法实现了一个 通电延时定时器 ,其工作原理如下:
1. 启动 :当输入信号 value_i 变为 True 时,记录当前时间作为开始时间
2. 计时 :从开始时间开始,累计经过的时间
3. 完成 :当经过的时间达到预设时间 value_pt 时,输出变为 True
4. 重置 :当输入信号变为 False 时,定时器重置,输出变为 False
5. 暂停/恢复 :支持暂停功能,暂停后恢复时从暂停点继续计时
"""
def Q(self, value_i, value_pt):
self.m_bIn = value_i
self.m_unPT = value_pt
un_tick = MyTimer.gMyGetTickCount()
if self.m_bOver and self.m_bIn:
self.m_unStartTime = un_tick - self.m_unPT
self.m_bOver = False
if self.m_bPause and self.m_bIn:
self.m_unStartTime = un_tick - self.m_unPauseET
if self.m_bIn != self.m_bLastI:
self.m_bLastI = self.m_bIn
if self.m_bIn:
self.m_unStartTime = un_tick
self.m_bPause = False
return self.m_bIn and (un_tick >= (self.m_unStartTime + self.m_unPT))
# CClockPulse class equivalent in Python
class CClockPulse:
def __init__(self):
self.m_bFirstOut = True
self.m_bTonAOut = False
self.m_bTonBOut = False
self.m_cTonA = CTon()
self.m_cTonB = CTon()
def Q(self, value_i, run_time, stop_time):
if self.m_bFirstOut:
self.m_bTonAOut = self.m_cTonA.Q(not self.m_bTonBOut and value_i, run_time)
self.m_bTonBOut = self.m_cTonB.Q(self.m_bTonAOut and value_i, stop_time)
return not self.m_bTonAOut and value_i
else:
self.m_bTonAOut = self.m_cTonA.Q(not self.m_bTonBOut and value_i, stop_time)
self.m_bTonBOut = self.m_cTonB.Q(self.m_bTonAOut and value_i, run_time)
return self.m_bTonAOut and value_i
# CDelayOut class equivalent in Python
class CDelayOut:
def __init__(self):
self.m_cOutTon = CTon()
self.m_cmWaitTon = CTon()
def Reset(self):
self.m_cOutTon.SetReset()
self.m_cmWaitTon.SetReset()
def Q(self, value_i, wait_time, out_time):
if self.m_cmWaitTon.Q(value_i, wait_time):
if self.m_cOutTon.Q(True, out_time):
self.m_cOutTon.SetReset()
self.m_cmWaitTon.SetReset()
value_i = False
return False
return True
return False
# CRisOrFall class equivalent in Python
class CRisOrFall:
def __init__(self):
self.m_bTemp = False
def Q(self, value_i, ris_or_fall):
result = False
if value_i != self.m_bTemp:
if ris_or_fall and value_i: # Rising edge
result = True
if not ris_or_fall and not value_i: # Falling edge
result = True
self.m_bTemp = value_i
return result
# CTof class equivalent in Python
class CTof:
def __init__(self):
self.m_cDelayTon = CTon()
self.m_bValue = False
self.m_cRis = CRisOrFall()
def SetReset(self):
self.m_bValue = False
self.m_cDelayTon.SetReset()
def Q(self, value_i, delay_time):
if self.m_cRis.Q(value_i, False):
self.m_cDelayTon.SetReset()
self.m_bValue = True
if self.m_cDelayTon.Q(self.m_bValue, delay_time):
self.m_bValue = False
self.m_cDelayTon.SetReset()
return value_i or self.m_bValue
# Utility function
def gGetNowTime():
return int(time.time())

View File

@ -13,10 +13,25 @@ sub_interval = 500
[OPC_NODE_LIST]
upper_weight = 2:upper,2:upper_weight
upper_is_arch = 2:upper,2:upper_is_arch
upper_door_closed = 2:upper,2:upper_door_closed
upper_volume = 2:upper,2:upper_volume
upper_door_position = 2:upper,2:upper_door_position
lower_weight = 2:lower,2:lower_weight
lower_is_arch = 2:lower,2:lower_is_arch
lower_angle = 2:lower,2:lower_angle
mould_finish_weight = 2:mould,2:mould_finish_weight
mould_need_weight = 2:mould,2:mould_need_weight
mould_frequency = 2:mould,2:mould_frequency
mould_vibrate_status = 2:mould,2:mould_vibrate_status
feed_status = 2:mould,2:feed_status
sys_set_mode = 2:sys,2:set_mode
sys_segment_refresh = 2:sys,2:segment_refresh
sys_pd_refresh = 2:sys,2:pd_refresh
pd_data = 2:pd,2:pd_data
upper_hopper_position = 2:upper,2:upper_hopper_position
upper_clamp_status = 2:upper,2:upper_clamp_status
vibration_frequency=2:vibration_frequency
production_progress=2:production_progress
segment_tasks=2:segment_tasks
pd_notify = 2:pd,2:pd_notify
pd_set_mode = 2:pd,2:set_mode
pd_set_volume = 2:pd,2:set_volume

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
@ -153,11 +171,20 @@ class FeedingControlSystem:
_wait_time=300
else:
print("----下料重量小于46KG,暂时不振捣-----")
# else:
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,12 +385,14 @@ class FeedingControlSystem:
_isFinish=False
_start_time=None
while self.state.running:
#设置的派单模式
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.feeding_controller.send_pd_data()
_isSuccess=self.send_pd_data()
if _isSuccess:
_isFinish=True
if time.time()-_start_time>60:
@ -372,6 +403,119 @@ class FeedingControlSystem:
_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

Binary file not shown.

BIN
db/three-record.db Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -54,9 +54,9 @@ class InverterController:
frequency_value = self.inverter.read_register(0x7310)
_ret=frequency_value / 100
else:
print(f"读取频率{frequency}失败")
print(f"读取频率失败")
except Exception as e:
print(f"读取频率{frequency}异常:{e}")
print(f"读取频率异常:{e}")
finally:
if self.inverter:
self.inverter.serial.close()

View File

@ -145,18 +145,6 @@ class RelayController:
time.sleep(duration)
self.control(self.DOOR_UPPER_OPEN, 'close')
def control_upper_close_sync(self,duration):
thread_name = threading.current_thread().name
print(f"[{thread_name}] 尝试执行上料斗同步关闭,实际操作下料斗")
with self.door_control_lock:
print(f"[{thread_name}] 获得下料斗控制锁,执行同步关闭操作")
self.control(self.DOOR_UPPER_OPEN, 'close')
self.control(self.DOOR_UPPER_CLOSE, 'open')
time.sleep(duration)
self.control(self.DOOR_UPPER_CLOSE, 'close')
print(f"[{thread_name}] 同步关闭操作完成,释放控制锁")
def control_upper_open(self):
#关闭信号才能生效
self.control(self.DOOR_UPPER_CLOSE, 'close')
@ -315,6 +303,15 @@ class RelayController:
self.control(self.BREAK_ARCH_LOWER, 'close')
def control_upper_to_jbl(self):
"""控制上料斗到搅拌楼"""
# self.control(self.UPPER_TO_ZD, 'close')
# self.control(self.UPPER_TO_JBL, 'open')
def control_upper_to_zd(self):
"""控制上料斗到料斗"""
# self.control(self.UPPER_TO_JBL, 'close')
# self.control(self.UPPER_TO_ZD, 'open')
def close_all(self):
"""关闭所有继电器"""

View File

@ -3,34 +3,75 @@ from opcua import Client, ua
import time
from datetime import datetime
import configparser
from threading import Thread
# class OpcuaUiSignal:
# value_changed = Signal(str, str, object)
# opc_disconnected = Signal(str) # OPC服务断开信号参数断开原因
# opc_reconnected = Signal() # OPC重连成功信号
# opc_log = Signal(str) # OPC运行日志信号参数日志信息
from threading import Thread,RLock
import logging
import queue
logging.getLogger("opcua").setLevel(logging.WARNING)
logging.getLogger("opcua.client.ua_client").setLevel(logging.WARNING)
logging.getLogger("opcua.uaprotocol").setLevel(logging.WARNING)
#控制程序opcua 需要需要在opc_config中配置_f结尾的key才会收到通知
# Opcua回调处理器
class SubscriptionHandler:
def __init__(self):
def __init__(self,parent):
self.node_id_to_name = {}
# self.opc_signal = opc_signal
#设置派单模式(1自动派单 2手动派单0未知)
# self.pd_set_mode=0
# #设置方量{ID:派单表IDVolumn:修改后方量}
# self.pd_set_volumn=''
# #派单通知数据{ErpID:ID:派单表ID,Flag:状态ErrorMsg:失败异常信息 }
# self.pd_notify=''
# 添加队列和锁
self.notification_queue = queue.Queue()
self.processing_thread = Thread(target=self._process_notifications, daemon=True,name="opcua_recv_thread")
self.lock = RLock() # 可重入锁
self.parent=parent
self.notify_callback=self.parent.notify_callback
def start_accept(self):
self.processing_thread.start()
def datachange_notification(self, node, val, data):
try:
node_id = node.nodeid.to_string()
var_name = self.node_id_to_name.get(node_id)
# self.opc_signal.value_changed.emit(node_id, var_name, val)
print(f"node_id: {node_id}, var_name: {var_name}, val: {val}")
self.notification_queue.put_nowait((var_name, val))
# setattr(self, var_name, val)
# if self.notify_callback:
# self.notify_callback(var_name,val)
except Exception as e:
err_msg = f"opcua解析值变化事件失败: {e}"
# self.opc_signal.opc_log.emit(err_msg)
print(err_msg)
def _process_notifications(self):
"""后台线程处理通知(串行执行)"""
while self.parent.is_running:
try:
var_name, val = self.notification_queue.get(timeout=1)
# 使用锁保护共享资源
with self.lock:
self.notify_callback(var_name,val)
self.notification_queue.task_done()
except queue.Empty:
continue
except Exception as e:
print(f"处理通知失败: {e}")
class OpcuaClientFeed(Thread):
def __init__(self, parent=None):
def __init__(self,notify_callback=None, parent=None):
super().__init__(parent)
self.server_url = ""
self.client = None
self.name="opcua_feed_client"
self.target_var_paths = []
#订阅配置文件中的参数
self.subscription_name=["pd_notify","pd_set_mode","pd_set_volume"]
# self.server_url = ""
self.read_opc_config() # 读取配置文件
self.client = Client(self.server_url) # 初始化opc客户端
self.notify_callback=notify_callback
self.connected = False
self.subscription = None
@ -38,18 +79,10 @@ class OpcuaClientFeed(Thread):
self.is_running = True # 线程运行标志位
self.node_id_mapping = {} # node_id 和 可读变量名的映射表
self.is_reconnect_tip_sent = False # 重连失败提示是否已发送
self.handler = SubscriptionHandler(self)
# self.opc_signal = OpcuaUiSignal()
# self.handler = SubscriptionHandler(self.opc_signal)
self.handler = SubscriptionHandler()
self.target_var_paths = []
# 参数
self.heartbeat_interval = None # 心跳检测间隔
self.reconnect_interval = None # 首次/掉线重连间隔
self.sub_interval = None # 订阅间隔 (单位:ms)
def start_accept(self):
self.handler.start_accept()
def stop_run(self):
"""停止线程+断开连接"""
@ -65,7 +98,6 @@ class OpcuaClientFeed(Thread):
self.connected = True
msg = f"成功连接到OPCUA服务器: {self.server_url}"
print(msg)
# self.opc_signal.opc_log.emit(msg)
self.is_reconnect_tip_sent = False
return True
except Exception as e:
@ -73,7 +105,7 @@ class OpcuaClientFeed(Thread):
err_msg = f"连接OPCUA服务器失败: {e}"
print(err_msg)
if not self.is_reconnect_tip_sent:
# self.opc_signal.opc_log.emit(err_msg)
print(err_msg)
# 标记为已发送后续不重复在UI上显示
self.is_reconnect_tip_sent = True
return False
@ -111,41 +143,37 @@ class OpcuaClientFeed(Thread):
if not self.connected:
return False
try:
# self.opc_signal.opc_log.emit("开始构建nodeid映射表...")
objects_node = self.client.get_objects_node()
self.handler.node_id_to_name = self.node_id_mapping
for var_name, path_list in self.target_var_paths:
target_node = objects_node.get_child(path_list)
node_id = target_node.nodeid.to_string()
self.node_id_mapping[node_id] = var_name
# self.opc_signal.opc_log.emit("nodeid映射表构建成功")
print("nodeid映射表构建成功")
return True
except Exception as e:
err_msg = f"构建{var_name}映射表失败: {e}"
print(err_msg)
# self.opc_signal.opc_log.emit(err_msg)
return False
def create_multi_subscription(self, interval=None):
"""订阅多个变量(基于映射表的nodeid)"""
if not self.connected:
return
if not self.node_id_mapping and not self.build_node_id_mapping():
if not self.node_id_mapping:
return
try:
interval = int(interval) if interval else self.sub_interval
self.subscription = self.client.create_subscription(interval, self.handler)
# self.opc_signal.opc_log.emit(f"opcua订阅创建成功(间隔:{interval}ms)")
for node_id, var_name in self.node_id_mapping.items():
if var_name in self.subscription_name:
var_node = self.client.get_node(node_id)
monitored_item = self.subscription.subscribe_data_change(var_node)
self.monitored_items.append(monitored_item)
# self.opc_signal.opc_log.emit(f"已订阅变量: {var_name} (nodeid: {node_id})")
print(f"已订阅变量: {var_name} (nodeid: {node_id})")
except Exception as e:
err_msg = f"创建批量订阅失败: {e}"
print(err_msg)
# self.opc_signal.opc_log.emit(err_msg)
def read_opc_config(self, cfg_path = "config/opc_config.ini"):
"""读取OPC配置文件, 初始化所有参数和节点列表"""
@ -163,17 +191,13 @@ class OpcuaClientFeed(Thread):
for readable_name, node_path_str in node_section.items():
node_path_list = node_path_str.split(",")
self.target_var_paths.append( (readable_name, node_path_list) )
# print("target_var_paths", self.target_var_paths)
print("target_var_paths", self.target_var_paths)
except Exception as e:
print(f"读取配置文件失败: {e},使用默认配置启动!")
self.server_url = "opc.tcp://localhost:4840/zjsh_feed/server/"
self.heartbeat_interval = 4
self.reconnect_interval = 2
self.sub_interval = 500
self.target_var_paths = [
("upper_weight", ["2:upper", "2:upper_weight"]),
("lower_weight", ["2:lower", "2:lower_weight"])
]
# 参数合法性检验
self.heartbeat_interval = self.heartbeat_interval if isinstance(self.heartbeat_interval, int) and self.heartbeat_interval >=1 else 4
self.reconnect_interval = self.reconnect_interval if isinstance(self.reconnect_interval, int) and self.reconnect_interval >=1 else 2
@ -185,26 +209,31 @@ class OpcuaClientFeed(Thread):
:param var_readable_name: 变量可读名称(如"upper_weight")
:param value: 要写入的值
"""
max_wait = 30 # 最大等待30秒
wait_count = 0
while (not self.node_id_mapping and wait_count < max_wait):
time.sleep(0.5)
wait_count += 1
if wait_count % 2 == 0: # 每2秒打印一次
print(f"等待OPC连接中...({wait_count/2}秒)")
if not self.connected:
# self.opc_signal.opc_log.emit(f"{var_readable_name}写入失败: OPC服务未连接")
print(f"{var_readable_name}写入失败: OPC服务未连接")
return
target_node_id = None
for node_id, name in self.node_id_mapping.items():
if name == var_readable_name:
target_node_id = node_id
break
if not target_node_id:
# self.opc_signal.opc_log.emit(f"写入失败:未找到变量名 {var_readable_name} 对应的nodeid")
return
try:
target_node = self.client.get_node(target_node_id)
# variant = ua.Variant(float(value), ua.VariantType.Double)
target_node.set_value(value)
# self.opc_signal.opc_log.emit(f"写入成功:{var_readable_name} = {value}")
except Exception as e:
err_msg = f"opcua写入值失败: {e}"
print(err_msg)
# self.opc_signal.opc_log.emit(err_msg)
# ===== 心跳检测函数 =====
def _heartbeat_check(self):
@ -215,45 +244,38 @@ class OpcuaClientFeed(Thread):
except Exception as e:
err_msg = f"心跳检测失败, OPCUA服务已断开 {e}"
print(err_msg)
# self.opc_signal.opc_log.emit(err_msg)
return False
# ===== 掉线重连函数 =====
def _auto_reconnect(self):
"""掉线后自动重连+重建映射+恢复订阅"""
# self.opc_signal.opc_disconnected.emit("OPC服务掉线, 开始自动重连...")
print("OPC服务掉线, 开始自动重连...")
try:
self.disconnect()
except Exception as e:
print(f"_auto_reconnect: 断开旧连接时出现异常: {e}")
while self.is_running:
# self.opc_signal.opc_log.emit(f"重试连接OPC服务器: {self.server_url}")
print(f"重试连接OPC服务器: {self.server_url}")
if self.connect():
self.build_node_id_mapping()
self.create_multi_subscription()
# self.opc_signal.opc_reconnected.emit()
# self.opc_signal.opc_log.emit("OPCUA服务器重连成功, 所有订阅已恢复正常")
print("OPCUA服务器重连成功, 所有订阅已恢复正常")
break
time.sleep(self.reconnect_interval)
def _init_connect_with_retry(self):
"""连接opc服务器"""
# self.opc_signal.opc_log.emit("OPC客户端初始化, 开始连接服务器...")
print("OPC客户端初始化, 开始连接服务器...")
while self.is_running:
if self.connect():
self.build_node_id_mapping()
self.create_multi_subscription()
break
# self.opc_signal.opc_log.emit(f"连接OPCUA服务器失败, {self.reconnect_interval}秒后重试...")
print(f"连接OPCUA服务器失败, {self.reconnect_interval}秒后重试...")
time.sleep(self.reconnect_interval)
def run(self) -> None:
"""opcua客户端线程主函数"""
self.read_opc_config() # 读取配置文件
self.client = Client(self.server_url) # 初始化opc客户端
# 连接opc服务器
self._init_connect_with_retry()
@ -269,6 +291,9 @@ class OpcuaClientFeed(Thread):
if __name__ == "__main__":
opcua_client = OpcuaClientFeed()
opcua_client.run()
opcua_client.write_value_by_name("upper_weight", 100.0)
opcua_client.start()
opcua_client.write_value_by_name("pd_set_mode", 1)
while True:
time.sleep(1)

View File

@ -61,23 +61,35 @@ class SimpleOPCUAServer:
"""创建OPC UA变量"""
# 创建变量时显式指定数据类型和初始值
#上料斗
# 上料斗
self.upper_weight = self.upper.add_variable(self.namespace, "upper_weight", ua.Variant(0.0, ua.VariantType.Float))
self.upper_is_arch = self.upper.add_variable(self.namespace, "upper_is_arch", ua.Variant(False, ua.VariantType.Boolean))
self.upper_door_closed = self.upper.add_variable(self.namespace, "upper_door_closed", ua.Variant(False, ua.VariantType.Boolean))
self.upper_door_closed = self.upper.add_variable(self.namespace, "upper_door_closed", ua.Variant(False, ua.VariantType.Int16))
self.upper_volume = self.upper.add_variable(self.namespace, "upper_volume", ua.Variant(0.0, ua.VariantType.Float))
self.upper_door_position = self.upper.add_variable(self.namespace, "upper_door_position", ua.Variant(0, ua.VariantType.Int16))
#下料斗
# 下料斗
self.lower_weight = self.lower.add_variable(self.namespace, "lower_weight", ua.Variant(0.0, ua.VariantType.Float))
self.lower_is_arch = self.lower.add_variable(self.namespace, "lower_is_arch", ua.Variant(False, ua.VariantType.Boolean))
self.lower_angle = self.lower.add_variable(self.namespace, "lower_angle", ua.Variant(0.0, ua.VariantType.Float))
#模具车
# 模具车
self.mould_finish_weight = self.mould.add_variable(self.namespace, "mould_finish_weight", ua.Variant(0.0, ua.VariantType.Float))
self.mould_need_weight = self.mould.add_variable(self.namespace, "mould_need_weight", ua.Variant(0.0, ua.VariantType.Float))
self.mould_finish_ratio = self.mould.add_variable(self.namespace, "mould_finish_ratio", ua.Variant(0.0, ua.VariantType.Float))
self.mould_frequency = self.mould.add_variable(self.namespace, "mould_frequency", ua.Variant(230, ua.VariantType.Int32))
self.mould_vibrate_status = self.mould.add_variable(self.namespace, "mould_vibrate_status", ua.Variant(False, ua.VariantType.Boolean))
self.feed_status = self.mould.add_variable(self.namespace, "feed_status", ua.Variant(0, ua.VariantType.Int16))
self.pd_data=self.pd.add_variable(self.namespace, "pd_data", ua.Variant("", ua.VariantType.String))
self.pd_notify=self.pd.add_variable(self.namespace, "pd_notify", ua.Variant("", ua.VariantType.String))
self.pd_set_mode=self.pd.add_variable(self.namespace, "set_mode", ua.Variant(0, ua.VariantType.Int16))
self.pd_set_volume=self.pd.add_variable(self.namespace, "set_volume", ua.Variant("", ua.VariantType.String))
# 系统对象
self.sys_set_mode=self.sys.add_variable(self.namespace, "set_mode", ua.Variant(0, ua.VariantType.Int16))
self.sys_segment_refresh=self.sys.add_variable(self.namespace, "segment_refresh", ua.Variant(0, ua.VariantType.Int16))
self.sys_pd_refresh=self.sys.add_variable(self.namespace, "pd_refresh", ua.Variant(0, ua.VariantType.Int16))
# 在创建变量后立即设置可写权限(不需要等待服务器启动)
self.upper_weight.set_writable(True)
@ -93,63 +105,18 @@ class SimpleOPCUAServer:
self.mould_vibrate_status.set_writable(True)
self.feed_status.set_writable(True)
self.pd_data.set_writable(True)
print("[变量创建] 变量创建完成AccessLevel权限已设置")
# 验证并打印当前的AccessLevel属性
# try:
# al = self.upper_weight.get_attribute(ua.AttributeIds.AccessLevel)
# ual = self.upper_weight.get_attribute(ua.AttributeIds.UserAccessLevel)
# print(f"[变量创建] upper_weight AccessLevel: {al.Value.Value}, UserAccessLevel: {ual.Value.Value}")
# al2 = self.lower_weight.get_attribute(ua.AttributeIds.AccessLevel)
# ual2 = self.lower_weight.get_attribute(ua.AttributeIds.UserAccessLevel)
# print(f"[变量创建] lower_weight AccessLevel: {al2.Value.Value}, UserAccessLevel: {ual2.Value.Value}")
# except Exception as e:
# print(f"[变量创建] 获取权限属性失败: {e}")
def setup_variable_permissions(self):
"""设置变量权限 - 在服务器启动后调用"""
try:
# 重新设置变量为可写,确保权限生效
self.upper_weight.set_writable(True)
self.lower_weight.set_writable(True)
print("[权限设置] 变量权限已重新设置")
# 验证权限
try:
al = self.upper_weight.get_attribute(ua.AttributeIds.AccessLevel)
ual = self.upper_weight.get_attribute(ua.AttributeIds.UserAccessLevel)
print(f"[权限设置] upper_weight AccessLevel: {al.Value.Value}, UserAccessLevel: {ual.Value.Value}")
except Exception as e:
print(f"[权限设置] 验证失败: {e}")
except Exception as e:
print(f"[权限设置] 设置权限失败: {e}")
print("[权限设置] 尝试强制设置...")
self.pd_notify.set_writable(True)
self.pd_set_mode.set_writable(True)
self.pd_set_volume.set_writable(True)
self.sys_set_mode.set_writable(True)
self.mould_finish_ratio.set_writable(True)
self.sys_segment_refresh.set_writable(True)
self.sys_pd_refresh.set_writable(True)
self.lower_angle.set_writable(True)
# def setup_state_listeners(self):
# """设置状态监听器 - 事件驱动更新"""
# if hasattr(self.state, 'state_updated'):
# self.state.state_updated.connect(self.on_state_changed)
# print("状态监听器已设置 - 事件驱动模式")
print("[变量创建] 变量创建完成")
# def on_state_changed(self, property_name, value):
# """状态变化时的回调函数"""
# try:
# # 根据属性名更新对应的OPC UA变量
# if property_name == "upper_weight":
# self.upper_weight.set_value(value)
# elif property_name == "lower_weight":
# self.lower_weight.set_value(value)
# # 可以在这里添加更多状态映射
# print(f"状态更新: {property_name} = {value}")
# except Exception as e:
# print(f"状态更新错误: {e}")
def start(self):
"""启动服务器"""

2
pytest.ini Normal file
View File

@ -0,0 +1,2 @@
[pytest]
addopts = -s -v

View File

@ -14370,139 +14370,139 @@ qt_resource_struct = b"\
\x00\x00\x00\x10\x00\x02\x00\x00\x00C\x00\x00\x00\x03\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x05\xb8\x00\x00\x00\x00\x00\x01\x00\x02\xdd\x16\
\x00\x00\x01\x9a\x80)\xd9\x84\
\x00\x00\x01\x9c\x94G\xc6\x9e\
\x00\x00\x04\x02\x00\x00\x00\x00\x00\x01\x00\x01\xfb\xa6\
\x00\x00\x01\x9a\x80)\xd3W\
\x00\x00\x01\x9c\x94G\xc6\x9f\
\x00\x00\x05(\x00\x00\x00\x00\x00\x01\x00\x02\xbc.\
\x00\x00\x01\x9a\x80)\xd8a\
\x00\x00\x01\x9c\x94G\xc6\xa6\
\x00\x00\x01\x04\x00\x00\x00\x00\x00\x01\x00\x00+8\
\x00\x00\x01\x9a\x80)\xdc9\
\x00\x00\x01\x9c\x94G\xc6\x9f\
\x00\x00\x00T\x00\x00\x00\x00\x00\x01\x00\x00\x11r\
\x00\x00\x01\x9a\x80)\xd8\xb7\
\x00\x00\x01\x9c\x94G\xc6\xa0\
\x00\x00\x05\xd0\x00\x00\x00\x00\x00\x01\x00\x02\xddk\
\x00\x00\x01\x9a\x80)\xda\x98\
\x00\x00\x01\x9c\x94G\xc6\xa1\
\x00\x00\x02\xa6\x00\x00\x00\x00\x00\x01\x00\x01\x08\xf0\
\x00\x00\x01\x9a\x80)\xd9b\
\x00\x00\x01\x9c\x94G\xc6\xa1\
\x00\x00\x00:\x00\x00\x00\x00\x00\x01\x00\x00\x01\x05\
\x00\x00\x01\x9a\x80)\xd6\x9c\
\x00\x00\x01\x9c\x94G\xc6\xa0\
\x00\x00\x02\xd8\x00\x00\x00\x00\x00\x01\x00\x01\x1d/\
\x00\x00\x01\x9a\x80)\xdc\x8a\
\x00\x00\x01\x9c\x94G\xc6\xa0\
\x00\x00\x01\xda\x00\x00\x00\x00\x00\x01\x00\x00\x84\x83\
\x00\x00\x01\x9a\x80)\xdd\x04\
\x00\x00\x01\x9c\x94G\xc6\xa6\
\x00\x00\x03\x8e\x00\x00\x00\x00\x00\x01\x00\x01N/\
\x00\x00\x01\x9a\x80)\xd8\xfc\
\x00\x00\x01\x9c\x94G\xc6\x9f\
\x00\x00\x04\x84\x00\x00\x00\x00\x00\x01\x00\x02\x92\xa4\
\x00\x00\x01\x9a\x80)\xd9\xff\
\x00\x00\x01\x9c\x94G\xc6\x9f\
\x00\x00\x01$\x00\x00\x00\x00\x00\x01\x00\x00-\x82\
\x00\x00\x01\x9a\x80)\xdaB\
\x00\x00\x01\x9c\x94G\xc6\x9a\
\x00\x00\x02x\x00\x00\x00\x00\x00\x01\x00\x00\xf5\xe3\
\x00\x00\x01\x9a\x80)\xda]\
\x00\x00\x01\x9c\x94G\xc6\xa8\
\x00\x00\x01L\x00\x00\x00\x00\x00\x01\x00\x00c\xfb\
\x00\x00\x01\x9a\x80)\xdb\xf3\
\x00\x00\x01\x9c\x94G\xc6\xa3\
\x00\x00\x02\x92\x00\x00\x00\x00\x00\x01\x00\x01\x02\xaa\
\x00\x00\x01\x9a\x80)\xd4\x9b\
\x00\x00\x01\x9c\x94G\xc6\xa3\
\x00\x00\x03\x02\x00\x00\x00\x00\x00\x01\x00\x01%\xbb\
\x00\x00\x01\x9a\x80)\xda\xf5\
\x00\x00\x01\x9c\x94G\xc6\xaa\
\x00\x00\x05p\x00\x00\x00\x00\x00\x01\x00\x02\xc7\x9f\
\x00\x00\x01\x9a\x80)\xd3\xfd\
\x00\x00\x01\x9c\x94G\xc6\xa4\
\x00\x00\x03\xae\x00\x00\x00\x00\x00\x01\x00\x01Q\xcb\
\x00\x00\x01\x9a\x80)\xd7\x04\
\x00\x00\x01\x9c\x94G\xc6\xab\
\x00\x00\x00\x22\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x01\x9a\x80)\xd8A\
\x00\x00\x01\x9c\x94G\xc6\xa4\
\x00\x00\x04\xc0\x00\x00\x00\x00\x00\x01\x00\x02\x96\x82\
\x00\x00\x01\x9a\x80)\xddw\
\x00\x00\x01\x9c\x94G\xc6\xa1\
\x00\x00\x00\x88\x00\x00\x00\x00\x00\x01\x00\x00 \x01\
\x00\x00\x01\x9a\x80)\xd67\
\x00\x00\x01\x9c\x94G\xc6\xa6\
\x00\x00\x06\x9a\x00\x00\x00\x00\x00\x01\x00\x03Yo\
\x00\x00\x01\x9a\x80)\xdbu\
\x00\x00\x01\x9c\x94G\xc6\xa4\
\x00\x00\x04\xa4\x00\x00\x00\x00\x00\x01\x00\x02\x94\xf7\
\x00\x00\x01\x9a\x80)\xd9\x1e\
\x00\x00\x01\x9c\x94G\xc6\xa0\
\x00\x00\x03^\x00\x00\x00\x00\x00\x01\x00\x010\x95\
\x00\x00\x01\x9a\x80)\xda|\
\x00\x00\x01\x9c\x94G\xc6\xa7\
\x00\x00\x00\xe8\x00\x00\x00\x00\x00\x01\x00\x00((\
\x00\x00\x01\x9a\x80)\xd4Y\
\x00\x00\x01\x9c\x94G\xc6\xa1\
\x00\x00\x06l\x00\x00\x00\x00\x00\x01\x00\x036\xa7\
\x00\x00\x01\x9a\x80)\xdcp\
\x00\x00\x01\x9c\x94G\xc6\xa6\
\x00\x00\x02\x18\x00\x00\x00\x00\x00\x01\x00\x00\xe1c\
\x00\x00\x01\x9a\x80)\xdd;\
\x00\x00\x01\x9c\x94G\xc6\x9b\
\x00\x00\x02`\x00\x00\x00\x00\x00\x01\x00\x00\xf4\xe4\
\x00\x00\x01\x9a\x80)\xd4\xba\
\x00\x00\x01\x9c\x94G\xc6\xa4\
\x00\x00\x03\xc4\x00\x00\x00\x00\x00\x01\x00\x01\xa8}\
\x00\x00\x01\x9a\x80)\xd5\x9f\
\x00\x00\x01\x9c\x94G\xc6\xa5\
\x00\x00\x03>\x00\x00\x00\x00\x00\x01\x00\x01/\xe2\
\x00\x00\x01\x9a\x80)\xd9\xa3\
\x00\x00\x01\x9c\x94G\xc6\x9b\
\x00\x00\x01\xf8\x00\x00\x00\x00\x00\x01\x00\x00\xe0\xd7\
\x00\x00\x01\x9a\x80)\xdd\x8f\
\x00\x00\x01\x9c\x94G\xc6\x9d\
\x00\x00\x01\x80\x00\x00\x00\x00\x00\x01\x00\x00z\x9d\
\x00\x00\x01\x9a\x80)\xdb\xb6\
\x00\x00\x01\x9c\x94G\xc6\x9d\
\x00\x00\x05\xea\x00\x00\x00\x00\x00\x01\x00\x02\xeb\xec\
\x00\x00\x01\x9a\x80)\xd3\x96\
\x00\x00\x01\x9c\x94G\xc6\x9d\
\x00\x00\x06T\x00\x00\x00\x00\x00\x01\x00\x032L\
\x00\x00\x01\x9a\x80)\xd5|\
\x00\x00\x01\x9c\x94G\xc6\xaa\
\x00\x00\x06\x86\x00\x00\x00\x00\x00\x01\x00\x03?\x19\
\x00\x00\x01\x9a\x80)\xd4\xfa\
\x00\x00\x01\x9c\x94G\xc6\xa2\
\x00\x00\x04h\x00\x00\x00\x00\x00\x01\x00\x02\x8a\xb0\
\x00\x00\x01\x9a\x80)\xd7t\
\x00\x00\x01\x9c\x94G\xc6\xa8\
\x00\x00\x05>\x00\x00\x00\x00\x00\x01\x00\x02\xbc\xd1\
\x00\x00\x01\x9a\x80)\xda#\
\x00\x00\x01\x9c\x94G\xc6\xa8\
\x00\x00\x05\x0c\x00\x00\x00\x00\x00\x01\x00\x02\xb9\x8e\
\x00\x00\x01\x9a\x80)\xd4;\
\x00\x00\x01\x9c\x94G\xc6\xa0\
\x00\x00\x00\xcc\x00\x00\x00\x00\x00\x01\x00\x00#\xb3\
\x00\x00\x01\x9a\x80)\xdc\x17\
\x00\x00\x01\x9c\x94G\xc6\xa0\
\x00\x00\x00\x9e\x00\x00\x00\x00\x00\x01\x00\x00!\x14\
\x00\x00\x01\x9a\x80)\xd6z\
\x00\x00\x01\x9c\x94G\xc6\x9d\
\x00\x00\x06\xb2\x00\x00\x00\x00\x00\x01\x00\x03_2\
\x00\x00\x01\x9a\x80)\xda\xda\
\x00\x00\x01\x9c\x94G\xc6\xa3\
\x00\x00\x01`\x00\x00\x00\x00\x00\x01\x00\x00rC\
\x00\x00\x01\x9a\x80)\xd9?\
\x00\x00\x01\x9c\x94G\xc6\xa8\
\x00\x00\x064\x00\x00\x00\x00\x00\x01\x00\x03)\xfc\
\x00\x00\x01\x9a\x80)\xdb\x19\
\x00\x00\x01\x9c\x94G\xc6\xa8\
\x00\x00\x05\xa6\x00\x00\x00\x00\x00\x01\x00\x02\xd7\xf5\
\x00\x00\x01\x9a\x80)\xddX\
\x00\x00\x01\x9c\x94G\xc6\xa5\
\x00\x00\x01\xa0\x00\x00\x00\x00\x00\x01\x00\x00{\x07\
\x00\x00\x01\x9a\x80)\xd5\xc2\
\x00\x00\x01\x9c\x94G\xc6\xa7\
\x00\x00\x03\x1e\x00\x00\x00\x00\x00\x01\x00\x01&\xcb\
\x00\x00\x01\x9a\x80)\xdb\xd3\
\x00\x00\x01\x9c\x94G\xc6\xa7\
\x00\x00\x04\xdc\x00\x00\x00\x00\x00\x01\x00\x02\x98\x0d\
\x00\x00\x01\x9a\x80)\xdcT\
\x00\x00\x01\x9c\x94G\xc6\xa5\
\x00\x00\x06\x0a\x00\x00\x00\x00\x00\x01\x00\x02\xecg\
\x00\x00\x01\x9a\x80)\xd8\x9b\
\x00\x00\x01\x9c\x94G\xc6\xa1\
\x00\x00\x01\xc0\x00\x00\x00\x00\x00\x01\x00\x00\x84.\
\x00\x00\x01\x9a\x80)\xdbU\
\x00\x00\x01\x9c\x94G\xc6\x9b\
\x00\x00\x04\xf2\x00\x00\x00\x00\x00\x01\x00\x02\x9c\x95\
\x00\x00\x01\x9a\x80)\xd7O\
\x00\x00\x01\x9c\x94G\xc6\x9e\
\x00\x00\x04L\x00\x00\x00\x00\x00\x01\x00\x02h\xd9\
\x00\x00\x01\x9a\x80)\xd7*\
\x00\x00\x01\x9c\x94G\xc6\x9b\
\x00\x00\x03\xd6\x00\x00\x00\x00\x00\x01\x00\x01\xaa\x0a\
\x00\x00\x01\x9a\x80)\xd4\x1c\
\x00\x00\x01\x9c\x94G\xc6\xa8\
\x00\x00\x00n\x00\x00\x00\x00\x00\x01\x00\x00\x1d\x5c\
\x00\x00\x01\x9a\x80)\xd3\xdc\
\x00\x00\x01\x9c\x94G\xc6\xaa\
\x00\x00\x05\x88\x00\x00\x00\x00\x00\x01\x00\x02\xc8\xac\
\x00\x00\x01\x9a\x80)\xd6\xdf\
\x00\x00\x01\x9c\x94G\xc6\xa6\
\x00\x00\x02\xea\x00\x00\x00\x00\x00\x01\x00\x01\x22\x08\
\x00\x00\x01\x9a\x80)\xd8\x03\
\x00\x00\x01\x9c\x94G\xc6\x9f\
\x00\x00\x06 \x00\x00\x00\x00\x00\x01\x00\x03&\x04\
\x00\x00\x01\x9a\x80)\xd6\x06\
\x00\x00\x01\x9c\x94G\xc6\x9e\
\x00\x00\x020\x00\x00\x00\x00\x00\x01\x00\x00\xea\x99\
\x00\x00\x01\x9a\x80)\xd5\x1a\
\x00\x00\x01\x9c\x94G\xc6\xa4\
\x00\x00\x05Z\x00\x00\x00\x00\x00\x01\x00\x02\xc5\x8e\
\x00\x00\x01\x9a\x80)\xd6\xbe\
\x00\x00\x01\x9c\x94G\xc6\xa7\
\x00\x00\x04\x22\x00\x00\x00\x00\x00\x01\x00\x01\xffL\
\x00\x00\x01\x9a\x80)\xda\xbe\
\x00\x00\x01\x9c\x94G\xc6\xa2\
\x00\x00\x01:\x00\x00\x00\x00\x00\x01\x00\x00.\xdc\
\x00\x00\x01\x9a\x80)\xdd\x22\
\x00\x00\x01\x9c\x94G\xc6\xa2\
\x00\x00\x03\xf0\x00\x00\x00\x00\x00\x01\x00\x01\xabj\
\x00\x00\x01\x9a\x80)\xd5\xe3\
\x00\x00\x01\x9c\x94G\xc6\xa2\
\x00\x00\x03v\x00\x00\x00\x00\x00\x01\x00\x01I\x0f\
\x00\x00\x01\x9a\x80)\xd9\xc9\
\x00\x00\x01\x9c\x94G\xc6\xa3\
\x00\x00\x02H\x00\x00\x00\x00\x00\x01\x00\x00\xf4\x8f\
\x00\x00\x01\x9a\x80)\xd8\xda\
\x00\x00\x01\x9c\x94G\xc6\x9e\
\x00\x00\x02\xc0\x00\x00\x00\x00\x00\x01\x00\x01\x1c\xda\
\x00\x00\x01\x9a\x80)\xd4x\
\x00\x00\x01\x9c\x94G\xc6\x9d\
\x00\x00\x044\x00\x00\x00\x00\x00\x01\x00\x02h\x84\
\x00\x00\x01\x9a\x80)\xd7\x99\
\x00\x00\x01\x9c\x94G\xc6\x9e\
\x00\x00\x00\xb4\x00\x00\x00\x00\x00\x01\x00\x00#^\
\x00\x00\x01\x9a\x80)\xd57\
\x00\x00\x01\x9c\x94G\xc6\x9e\
"
def qInitResources():

View File

@ -18,8 +18,8 @@ from busisness.models import ArtifactInfoModel,PDRecordModel
class FeedingControlSystem:
def __init__(self):
print('FeedingControlSystem初始化')
self.pd_record_bll=PDRecordBll()
print('派单测试初始化')
# self.pd_record_bll=PDRecordBll()
def send_pd_data(self):
@ -56,7 +56,7 @@ class FeedingControlSystem:
print(f'接口数据异常')
return False
def get_fact_volumn(self,mould_code:str,block_number:str='',_weight:float=0) -> float:
def get_fact_volumn(self,block_number:str='') -> float:
"""获取实际派单发量"""
_now_volumn=0
_pd_volumn=0

100
tests/test_example.py Normal file
View File

@ -0,0 +1,100 @@
#!/usr/bin/env python3
"""
pytest 测试示例
"""
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import pytest
import time
from core.system_state import SystemState
from core.system import FeedingControlSystem
from busisness.models import ArtifactInfo,FreqRecordModel
from busisness.blls import FreqRecordBll
system=FeedingControlSystem()
system.state.current_mould=ArtifactInfo(MouldCode="SHR2B3-5")
def test_pd_record_send():
"""测试派单记录发送"""
system.send_pd_data()
def test_api_process():
system._process_api_db()
def test_freq_record_send():
"""测试频率记录发送"""
bll=FreqRecordBll()
record_id=bll.insert_freq_record(FreqRecordModel(ArtifactID='12',ArtifactActionID=1,MouldCode="SHR2B3-5",Freq=100.0))
assert record_id > 0
def add(a, b):
"""简单的加法函数"""
return a + b
def test_add():
"""测试加法函数"""
assert add(1, 2) == 4
assert add(0, 0) == 0
assert add(-1, 1) == 0
assert add(100, 200) == 300
def multiply(a, b):
"""简单的乘法函数"""
return a * b
def test_multiply():
"""测试乘法函数"""
assert multiply(2, 3) == 6
assert multiply(0, 5) == 0
assert multiply(-2, 3) == -6
assert multiply(10, 10) == 100
class Calculator:
"""简单的计算器类"""
def add(self, a, b):
return a + b
def subtract(self, a, b):
return a - b
def test_calculator():
"""测试计算器类"""
calc = Calculator()
assert calc.add(1, 2) == 3
assert calc.subtract(5, 2) == 3
assert calc.subtract(2, 5) == -3
@pytest.mark.parametrize("a, b, expected", [
(1, 2, 3),
(0, 0, 0),
(-1, 1, 0),
(100, 200, 300)
])
def test_add_parametrized(a, b, expected):
"""参数化测试加法函数"""
assert add(a, b) == expected
if __name__ == "__main__":
# 运行所有测试
system.opcua_client_feed.start()
system.start_opc_queue_thread()
test_pd_record_send()
# pytest.main([f'{__file__}::test_api_process'])
# pytest.main([f'{__file__}::test_freq_record_send'])
while True:
time.sleep(1)

View File

@ -1,8 +1,8 @@
# 读取原始数据文件
import datetime
input_file = r"C:\Users\fujin\Desktop\fsdownload\weight.txt" # 原始数据文件路径
output_file = r"C:\Users\fujin\Desktop\fsdownload\filtered_B_records.txt" # 输出结果文件路径 (使用原始字符串避免Unicode转义错误)
input_file = r"C:\Users\xj-robot\Desktop\weight.txt" # 原始数据文件路径
output_file = r"C:\Users\xj-robot\Desktop\filtered_B_records.txt" # 输出结果文件路径 (使用原始字符串避免Unicode转义错误)
# 存储包含"B"标记的记录
filtered_records = []

View File

@ -1,7 +1,6 @@
import os
import cv2
import numpy as np
from rknnlite.api import RKNNLite
from collections import deque
@ -63,6 +62,7 @@ _global_rknn = None
def init_rknn_model(model_path):
from rknnlite.api import RKNNLite
global _global_rknn
if _global_rknn is not None:
return _global_rknn

View File

@ -1,6 +1,6 @@
import os
import cv2
from rknnlite.api import RKNNLite
#from rknnlite.api import RKNNLite
import time
# classify_single_image, StableClassJudge, CLASS_NAMES 已在 muju_cls_rknn 中定义

View File

@ -3,6 +3,7 @@ from pickle import FALSE
from re import S
from cv2.gapi import ov
from config.settings import app_set_config
from core.relay_feed import RelayFeedController
from hardware.relay import RelayController
from hardware.transmitter import TransmitterController
import time
@ -16,7 +17,7 @@ from vision.camera_picture import save_camera_picture
from busisness.blls import ArtifactBll,PDRecordBll
from busisness.models import ArtifactInfoModel,PDRecordModel
from service.mould_service import app_web_service
from core.system_state import SystemState,FeedStatus
from core.system_state import SystemState,FeedStatus,Upper_Door_Position
from dataclasses import asdict
import json
import math
@ -44,6 +45,8 @@ class VisualCallback:
self.relay_controller = relay_controller
self.transmitter_controller = transmitter_controller
self.relay_feed=RelayFeedController(relay_controller,state)
self.pd_record_bll=PDRecordBll()
self.state=state
@ -67,6 +70,8 @@ class VisualCallback:
self._current_controlling_thread = None
#是否启动后的第一个模具
self._is_first_module=True
#上一次派单方量
self.prev_pd_volume=0.0
self.init_val()
# self._setup_logging_2()
@ -88,6 +93,8 @@ class VisualCallback:
self._time_mould_end=''
#记录当前模具信息model
self._cur_mould_model=None
self.plc_valid_data=[5,37]
self.plc_valid_data_jbl=[66,98]
# self.db_queue=queue.Queue()
# 获取视觉数据线程angle_visual_callback推送的数据并进行处理注意处理视觉进行夹角控制
@ -308,7 +315,7 @@ class VisualCallback:
(current_time - self._last_arch_time) >= 2:
self._last_arch_time = current_time
print('---------------------第一阶段振动5秒小于200KG-----------------')
self.relay_controller.control_arch_lower_open_sync(5)
self.relay_feed.control_arch_lower_open_sync(5)
self._last_arch_one_weight = _arch_weight
continue
self._last_arch_one_weight = _arch_weight
@ -322,8 +329,8 @@ class VisualCallback:
if (_weight_changed < 100) and \
(current_time - self._last_arch_time) >= 2:
self._last_arch_time = current_time
print('---------------------第二阶段振动3秒-----------------')
self.relay_controller.control_arch_upper_open_sync(3)
#print('---------------------第二阶段振动3秒-----------------')
# self.relay_feed.control_arch_upper_open_sync(3)
self._last_arch_two_weight = _arch_weight
continue
self._last_arch_two_weight = _arch_weight
@ -339,7 +346,7 @@ class VisualCallback:
(current_time - self._last_arch_time) >= 2:
self._last_arch_time = current_time
print('---------------------第三阶段振动5秒(小于100KG-----------------')
self.relay_controller.control_arch_lower_open_sync(5)
self.relay_feed.control_arch_lower_open_sync(5)
self._last_arch_three_weight = _arch_weight
continue
self._last_arch_three_weight = _arch_weight
@ -355,7 +362,7 @@ class VisualCallback:
self._last_arch_time = current_time
print('---------------------第四阶段振动5秒-----------------')
#重量不准,暂时不振动
# self.relay_controller.control_arch_upper_open_sync(5)
# self.relay_feed.control_arch_upper_open_sync(5)
self._last_arch_four_weight = _arch_weight
continue
self._last_arch_four_weight = _arch_weight
@ -372,7 +379,7 @@ class VisualCallback:
(current_time - self._last_arch_time) >= 2:
self._last_arch_time = current_time
print(f'---------------------第五阶段振动3秒(小于{_min_arch_weight}kg)-----------------')
self.relay_controller.control_arch_lower_open_sync(3)
self.relay_feed.control_arch_lower_open_sync(3)
self._last_arch_five_weight = _arch_weight
continue
self._last_arch_five_weight = _arch_weight
@ -486,6 +493,13 @@ class VisualCallback:
print('------------启动程序后,进入第一块-------------')
self._is_first_module=False
self._mould_before_aligned=True
if self._cur_mould_model:
self.state._db_mould_status={
'mould_code':self._cur_mould_model.MouldCode,
'status':2,
'weight':0,
}
_current_weight=self.transmitter_controller.read_data(2)
if _current_weight:
self._init_lower_weight=_current_weight
@ -575,6 +589,14 @@ class VisualCallback:
print(f'--------进入关闭(浇筑满)-----------')
self.safe_control_lower_close(3)
print(f'--------浇筑完成-----------')
if self._cur_mould_model:
self.state._db_mould_status={
'mould_code':self._cur_mould_model.MouldCode,
'status':3,
'weight':self.state._mould_finish_weight,
}
# try:
# self.db_queue.put_nowait({
# "f":self._is_small_f,
@ -633,7 +655,6 @@ class VisualCallback:
"""第一阶段下料:下料斗向模具车下料(低速)"""
print("--------------------开始下料F块--------------------")
self._time_mould_begin=datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# loc_relay=self.relay_controller
loc_mitter=self.transmitter_controller
max_weight_none=5
cur_weight_none=0
@ -664,6 +685,8 @@ class VisualCallback:
cur_weight_none=0
first_finish_weight=initial_lower_weight-current_weight
self._is_finish_ratio=(first_finish_weight)/need_total_weight
self.state._mould_finish_weight=first_finish_weight
self.state._mould_finish_ratio=self._is_finish_ratio
print(f'------------已下料比例: {self._is_finish_ratio}-------------')
# if self._is_finish_ratio>=1:
#关5秒
@ -683,7 +706,7 @@ class VisualCallback:
"""第一阶段下料:下料斗向模具车下料(低速)"""
print("--------------------开始下料(普通块)--------------------")
self._time_mould_begin=datetime.now().strftime("%Y-%m-%d %H:%M:%S")
loc_relay=self.relay_controller
loc_relay=self.relay_feed
loc_mitter=self.transmitter_controller
max_weight_none=5
cur_weight_none=0
@ -710,12 +733,15 @@ class VisualCallback:
return
continue
cur_weight_none=0
self._is_finish_ratio=(initial_lower_weight-current_weight)/need_total_weight
self.state._mould_finish_weight=initial_lower_weight-current_weight
self._is_finish_ratio= self.state._mould_finish_weight/need_total_weight
self.state._mould_finish_ratio=self._is_finish_ratio
print(f'------------已下料比例: {self._is_finish_ratio}-------------')
if current_weight<250 and current_weight>0:
# if current_weight>100:
#100,上面粘贴的,振动一下
# self.relay_controller.control_arch_lower_open_async(5)
# loc_relay.control_arch_lower_open_async(5)
self.close_lower_door_visual()
break
time.sleep(1)
@ -729,12 +755,12 @@ class VisualCallback:
print(f'------------已下料(第一次): {first_finish_weight}kg-------------')
self._is_feed_stage=0
while self.plc_data not in [5,37]:
while self.plc_data not in self.plc_valid_data:
#print('------------上料斗未就位----------------')
# print('------------上料斗未就位----------------')
time.sleep(1)
if self.plc_data==5 or self.plc_data==37:
if self.plc_data in self.plc_valid_data:
print(f'------------上料斗就位(上料斗往下料斗阶段)-------------')
#打开上料斗出砼门开5就开三分之一下
@ -742,7 +768,7 @@ class VisualCallback:
self._is_feed_stage=2
loc_time_count=1
upper_open_time=time.time()
self.state._upper_door_closed=1
while not self._is_finish:
current_upper_weight = loc_mitter.read_data(1)
if current_upper_weight is None:
@ -751,6 +777,7 @@ class VisualCallback:
#如果重量连续5次为None认为上料斗未就位跳出循环
print('------------第一次上到下,上料斗重量异常----------------')
print('------------第一次上到下,上料斗重量异常----------------')
self.state._upper_door_closed=0
loc_relay.control_upper_close_sync(5+loc_time_count)
return
continue
@ -760,6 +787,7 @@ class VisualCallback:
_two_lower_weight=0
if (current_upper_weight<3200 and current_upper_weight>0) or _two_lower_weight>3200:
#关5秒,loc_time_count多关一秒
self.state._upper_door_closed=0
loc_relay.control_upper_close_sync(5+loc_time_count)
break
else:
@ -772,7 +800,7 @@ class VisualCallback:
time.sleep(0.5)
else:
loc_relay.control_upper_close_sync(6+loc_time_count)
self.state._upper_door_closed=0
self.is_start_visual=True
initial_lower_weight=loc_mitter.read_data(2)
if initial_lower_weight is None:
@ -789,11 +817,13 @@ class VisualCallback:
return
continue
cur_weight_none=0
self._is_finish_ratio=(first_finish_weight+initial_lower_weight-current_weight)/need_total_weight
self.state._mould_finish_weight=first_finish_weight+initial_lower_weight-current_weight
self._is_finish_ratio=self.state._mould_finish_weight/need_total_weight
self.state._mould_finish_ratio=self._is_finish_ratio
if current_weight<250:
# if current_weight>100:
#100,上面粘贴的,振动一下
# self.relay_controller.control_arch_lower_open_async(5)
# loc_relay.control_arch_lower_open_async(5)
self.close_lower_door_visual()
break
# print(f'------------已下料: {first_finish_weight+second_finish_weight}kg-------------')
@ -807,7 +837,8 @@ class VisualCallback:
print(f'------------已下料(第二次): {first_finish_weight}kg-------------')
self._is_feed_stage=0
if self.plc_data==5 or self.plc_data==37:
if self.plc_data in self.plc_valid_data:
self.state._upper_door_closed=2
#第二次上料斗向下料斗转移
loc_relay.control_upper_open_sync(12)
loc_time_count=1
@ -818,6 +849,7 @@ class VisualCallback:
#initial_upper_weight=loc_mitter.read_data(1)
#start_time=None
self._is_feed_stage=4
while not self._is_finish:
# print(f'------------上料斗向下料斗转移22222-------------')
current_upper_weight = loc_mitter.read_data(1)
@ -827,8 +859,9 @@ class VisualCallback:
#如果重量连续5次为None认为上料斗未就位跳出循环
print('------------第二次上到下,上料斗重量异常----------------')
print('------------第二次上到下,上料斗重量异常----------------')
self.state._upper_door_closed=0
loc_relay.control_upper_close_sync(15)
break
return
continue
cur_weight_none=0
if (current_upper_weight<600 and current_upper_weight>0) or upper_open_time_2 is not None:
@ -838,6 +871,7 @@ class VisualCallback:
loc_relay.control_arch_upper_open_async(5)
# loc_relay.control_arch_upper_open()
loc_relay.control_upper_open_sync(5)
self.state._upper_door_closed=2
# start_time=None
#5秒后关闭
loc_relay.control_upper_close_after()#control_upper_close_sync(8+loc_time_count)
@ -853,6 +887,8 @@ class VisualCallback:
time.sleep(0.5)
else:
loc_relay.control_upper_close_sync(15)
self.state._upper_door_closed=0
# time.sleep(0.4)
#第三次下料斗转移到模具车
@ -877,7 +913,9 @@ class VisualCallback:
continue
cur_weight_none=0
second_finish_weight=initial_lower_weight-current_weight
self._is_finish_ratio=(second_finish_weight+first_finish_weight)/need_total_weight
self.state._mould_finish_weight=second_finish_weight+first_finish_weight
self._is_finish_ratio=self.state._mould_finish_weight/need_total_weight
self.state._mould_finish_ratio=self._is_finish_ratio
print(f'------------已下料比例: {self._is_finish_ratio}-------------')
# if self._is_finish_ratio>=1:
#关5秒
@ -1340,6 +1378,15 @@ class VisualCallback:
#4即将振捣室5振捣室 64即将搅拌楼 66到达搅拌楼
# print(f"[数据回调] 数值: 0x{data:02X} | 十进制: {data:3d} | 二进制: {binary}")
self.plc_data=data
if self.plc_data in self.plc_valid_data:
if self.state._upper_door_position!=Upper_Door_Position.ZDS:
self.state._upper_door_position=Upper_Door_Position.ZDS
elif self.plc_data in self.plc_valid_data_jbl:
if self.state._upper_door_position!=Upper_Door_Position.JBL:
self.state._upper_door_position=Upper_Door_Position.JBL
else:
if self.state._upper_door_position!=Upper_Door_Position.Returning:
self.state._upper_door_position=Upper_Door_Position.Returning
# @classmethod
# def instance_exists(cls):
@ -1360,81 +1407,11 @@ class VisualCallback:
# self.safe_control_lower_close()
# 等待线程结束
if self.callback_thread.is_alive():
if self.callback_thread and self.callback_thread.is_alive():
self.callback_thread.join(timeout=1.0)
if self.diff_thread:
if self.diff_thread and self.diff_thread.is_alive():
self.diff_thread.join(timeout=1.0)
# self.relay_controller._close_lower_5s
def send_pd_data(self):
"""
发送PD数据到OPC队列
"""
# 构建PD数据
_cur_mould=self._cur_mould_model
if _cur_mould is not None:
if _cur_mould.MouldCode:
_pdrecords = self.pd_record_bll.get_last_pds(_cur_mould.MouldCode)
if _pdrecords:
_pdrecord=_pdrecords[0]
if _pdrecord.TaskID:
if _pdrecord.BlockNumber=='F':
print(f'{_pdrecord.MouldCode} F块不发送派单数据')
print(f'{_pdrecord.MouldCode} F块不发送派单数据')
print(f'{_pdrecord.MouldCode} F块不发送派单数据')
return True
_fact_volumn=self.get_fact_volumn(_pdrecord.MouldCode,_pdrecord.BlockNumber)
if _fact_volumn>0:
_pdrecord.FBetonVolume=_fact_volumn
print(f'{_pdrecord.MouldCode}-{_pdrecord.BlockNumber} 实际派单方量:{_fact_volumn},{_fact_volumn},{_fact_volumn}')
print(f'{_pdrecord.MouldCode}-{_pdrecord.BlockNumber} 实际派单方量:{_fact_volumn},{_fact_volumn},{_fact_volumn}')
print(f'{_pdrecord.MouldCode}-{_pdrecord.BlockNumber} 实际派单方量:{_fact_volumn},{_fact_volumn},{_fact_volumn}')
self.state._pd_data=_pdrecord
return True
else:
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_volumn=0
_pd_volumn=0
print(f'get_fact_volumn当前重量{self._init_lower_weight}')
_now_volumn=self._init_lower_weight/2500
if not block_number and '-' in mould_code:
block_number = mould_code.split('-')[0][-2:]
if block_number in ['B1','B2','B3']:
_pd_volumn=1.9
elif block_number=='L1':
_pd_volumn=2.0
if _now_volumn>0.5:
#保证至少0.5方
_pd_volumn=1.9-_now_volumn+0.5
_pd_volumn=math.ceil(_pd_volumn*10)/10
if _pd_volumn<0.8:
_pd_volumn=0.8
#调整
elif block_number=='L2':
#2.4方大约L2和F的量
_pd_volumn=2.4
# if _weight>1300:
#留0.15 math.floor(_now_volumn*10)/10 保留一位小数,丢掉其他的
_pd_volumn=_pd_volumn-math.floor(_now_volumn*10)/10+0.1
_pd_volumn=math.ceil(_pd_volumn*10)/10
if _pd_volumn>2.1:
_pd_volumn=2.1
elif _pd_volumn<0.8:
_pd_volumn=0.8
return _pd_volumn
def get_current_mould(self):
"""获取当前要浇筑的管片"""
@ -1443,6 +1420,7 @@ class VisualCallback:
_cur_poured_model=_not_poured[-1]
if _cur_poured_model.MouldCode:
self._cur_mould_model=_cur_poured_model
self.state.current_mould=_cur_poured_model
print(f'当前要浇筑的管片 {json.dumps(asdict(_cur_poured_model), ensure_ascii=False)}')
else:
print('当前没有未浇筑的管片')

View File

@ -1417,7 +1417,7 @@ class VisualCallback:
print(f'{_pdrecord.MouldCode}-{_pdrecord.BlockNumber} 实际派单方量:{_fact_volumn},{_fact_volumn},{_fact_volumn}')
print(f'{_pdrecord.MouldCode}-{_pdrecord.BlockNumber} 实际派单方量:{_fact_volumn},{_fact_volumn},{_fact_volumn}')
print(f'{_pdrecord.MouldCode}-{_pdrecord.BlockNumber} 实际派单方量:{_fact_volumn},{_fact_volumn},{_fact_volumn}')
self.state._pd_data=_pdrecord
# self.state._pd_data=_pdrecord
return True
else:
return False