Compare commits
2 Commits
a3b6287808
...
ecba4d726a
| Author | SHA1 | Date | |
|---|---|---|---|
| ecba4d726a | |||
| 8aeaffa885 |
@ -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()
|
||||
|
||||
@ -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 ?"""
|
||||
) 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
|
||||
|
||||
|
||||
|
||||
@ -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
43
common/helpers.py
Normal 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)
|
||||
@ -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
158
common/util_time.py
Normal 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())
|
||||
@ -13,16 +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
|
||||
; ; upper_hopper_position = 2:upper,2:upper_hopper_position
|
||||
; upper_clamp_status = 2:upper,2:upper_clamp_status
|
||||
; production_progress=2:production_progress
|
||||
; segment_tasks=2:segment_tasks
|
||||
; dispatch_tasks=2:dispatch_tasks
|
||||
segment_tasks=2:sys,2:segment_refresh
|
||||
dispatch_tasks=2:sys,2:pd_refresh
|
||||
mould_vibrate_status=2:mould,2:mould_vibrate_status
|
||||
vibration_frequency=2:mould,2:mould_frequency
|
||||
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
|
||||
pd_mode=2:pd,2:set_mode
|
||||
pd_plan_volume=2:pd,2:set_volume
|
||||
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
|
||||
pd_notify = 2:pd,2:pd_notify
|
||||
pd_set_mode = 2:pd,2:set_mode
|
||||
pd_set_volume = 2:pd,2:set_volume
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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
396
core/pd_task_service.py
Normal 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
133
core/relay_feed.py
Normal 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')
|
||||
|
||||
231
core/system.py
231
core/system.py
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
BIN
db/messages.db
BIN
db/messages.db
Binary file not shown.
BIN
db/three-record.db
Normal file
BIN
db/three-record.db
Normal file
Binary file not shown.
BIN
db/three.db
BIN
db/three.db
Binary file not shown.
BIN
db/three.db-shm
BIN
db/three.db-shm
Binary file not shown.
BIN
db/three.db-wal
BIN
db/three.db-wal
Binary file not shown.
BIN
doc/table表设计.doc
BIN
doc/table表设计.doc
Binary file not shown.
BIN
doc/~$控制程序对接.docx
Normal file
BIN
doc/~$控制程序对接.docx
Normal file
Binary file not shown.
BIN
doc/控制程序对接.docx
BIN
doc/控制程序对接.docx
Binary file not shown.
@ -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()
|
||||
|
||||
@ -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):
|
||||
"""关闭所有继电器"""
|
||||
|
||||
@ -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:派单表ID,Volumn:修改后方量}
|
||||
# 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)
|
||||
|
||||
|
||||
@ -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
2
pytest.ini
Normal file
@ -0,0 +1,2 @@
|
||||
[pytest]
|
||||
addopts = -s -v
|
||||
@ -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():
|
||||
|
||||
@ -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
100
tests/test_example.py
Normal 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)
|
||||
|
||||
|
||||
@ -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 = []
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 中定义
|
||||
|
||||
@ -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('当前没有未浇筑的管片')
|
||||
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user