Compare commits
2 Commits
5e8f291738
...
d52bd7a83e
| Author | SHA1 | Date | |
|---|---|---|---|
| d52bd7a83e | |||
| 00dcd6b6cc |
@ -5,7 +5,7 @@
|
||||
# sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||
from dataclasses import fields
|
||||
from typing import List, Optional, Dict, Any
|
||||
from datetime import datetime
|
||||
from datetime import datetime,timedelta
|
||||
from busisness.models import ArtifactInfoModel, PDRecordModel,FreqRecordModel
|
||||
from busisness.dals import ArtifactDal, PDRecordDal,FreqRecordDal
|
||||
|
||||
@ -52,24 +52,29 @@ class ArtifactBll:
|
||||
if _artifactid is None:
|
||||
return self.dal.insert_artifact(model)
|
||||
else:
|
||||
if not _artifactid and model.ArtifactID:
|
||||
update_dict={
|
||||
'ArtifactID':model.ArtifactID,
|
||||
'ArtifactActionID':model.ArtifactActionID,
|
||||
'ArtifactIDVice1':model.ArtifactIDVice1,
|
||||
'ProduceRingNumber':model.ProduceRingNumber,
|
||||
'SkeletonID':model.SkeletonID,
|
||||
'RingTypeCode':model.RingTypeCode,
|
||||
'SizeSpecification':model.SizeSpecification,
|
||||
'BuriedDepth':model.BuriedDepth,
|
||||
'BlockNumber':model.BlockNumber,
|
||||
'HoleRingMarking':model.HoleRingMarking,
|
||||
'GroutingPipeMarking':model.GroutingPipeMarking,
|
||||
'PolypropyleneFiberMarking':model.PolypropyleneFiberMarking,
|
||||
'BetonVolume':model.BetonVolume,
|
||||
'BetonTaskID':model.BetonTaskID,
|
||||
}
|
||||
return self.dal.update_by_modulecode(model.MouldCode, update_dict)
|
||||
if model.ArtifactID:
|
||||
if not _artifactid:
|
||||
#不存在ArtifactID,更新
|
||||
update_dict={
|
||||
'ArtifactID':model.ArtifactID,
|
||||
'ArtifactActionID':model.ArtifactActionID,
|
||||
'ArtifactIDVice1':model.ArtifactIDVice1,
|
||||
'ProduceRingNumber':model.ProduceRingNumber,
|
||||
'SkeletonID':model.SkeletonID,
|
||||
'RingTypeCode':model.RingTypeCode,
|
||||
'SizeSpecification':model.SizeSpecification,
|
||||
'BuriedDepth':model.BuriedDepth,
|
||||
'BlockNumber':model.BlockNumber,
|
||||
'HoleRingMarking':model.HoleRingMarking,
|
||||
'GroutingPipeMarking':model.GroutingPipeMarking,
|
||||
'PolypropyleneFiberMarking':model.PolypropyleneFiberMarking,
|
||||
'BetonVolume':model.BetonVolume,
|
||||
'BetonTaskID':model.BetonTaskID,
|
||||
}
|
||||
return self.dal.update_by_modulecode(model.MouldCode, update_dict)
|
||||
else:
|
||||
#已经存在了ArtifactID,不处理
|
||||
return 1
|
||||
|
||||
return 1
|
||||
|
||||
@ -113,6 +118,16 @@ class PDRecordBll:
|
||||
"""获取当前浇筑的后三片的派据(有可能其中一项为空,但需要保证第一条有记录)"""
|
||||
return self.dal.get_last_pds(top,mould_code)
|
||||
|
||||
def get_near_pd_scan(self,size:str) -> PDRecordModel:
|
||||
"""获取最近的派单信息"""
|
||||
_target_time = datetime.now() - timedelta(hours=4)
|
||||
where=f"SizeSpecification='{size}' and CreateTime>='{_target_time}'"
|
||||
_pdrecords=self.dal.get_top_pd(1,"ID desc",where)
|
||||
if _pdrecords:
|
||||
return _pdrecords[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
def insert_PD_record(self,model:PDRecordModel) -> bool:
|
||||
"""保存PD官片任务(存在更新,不存在插入)"""
|
||||
if self.dal.exists_artifactid(model.ArtifactActionID):
|
||||
@ -125,9 +140,12 @@ class PDRecordBll:
|
||||
"""完成管片生产"""
|
||||
return self.dal.update_by_modulecode(code, {'Status': status,'EndTime':datetime.now()})
|
||||
|
||||
def start_pd(self, code: str,volumn:float) -> bool:
|
||||
"""开始管片生产"""
|
||||
return self.dal.update_by_modulecode(code, {'Status': 2,'GStatus':1,'OptTime':datetime.now(),'FBetonVolume':volumn})
|
||||
def start_pd(self, status:int,code: str,volumn:float) -> bool:
|
||||
"""开始管片生产,-1不更方量"""
|
||||
if volumn==-1:
|
||||
return self.dal.update_by_modulecode(code, {'Status': status,'GStatus':1,'OptTime':datetime.now()})
|
||||
else:
|
||||
return self.dal.update_by_modulecode(code, {'Status': status,'GStatus':1,'OptTime':datetime.now(),'FBetonVolume':volumn})
|
||||
|
||||
def update_PD_record(self,artifact_action_id: int, update_fields: dict) -> bool:
|
||||
"""更新PD官片任务状态"""
|
||||
@ -137,32 +155,44 @@ class PDRecordBll:
|
||||
"""更新PD官片任务状态"""
|
||||
return self.dal.update_pd_byid(id, update_fields)
|
||||
|
||||
def update_pd_notify(self,id: int, update_fields: dict) -> bool:
|
||||
"""更新PD官片任务状态"""
|
||||
return self.dal.update_pd_notify(id, update_fields)
|
||||
|
||||
def save_PD_record(self,model:PDRecordModel) -> int:
|
||||
|
||||
"""保存PD官片任务(存在ModuleCode更新,不存在插入,存在ArtifactID不处理)"""
|
||||
_artifactid=self.dal.get_artifactid(model.MouldCode)
|
||||
if _artifactid is None:
|
||||
if model.TaskID:
|
||||
model.ScanTime=datetime.now()
|
||||
return self.dal.insert_PD_record(model)
|
||||
else:
|
||||
if not _artifactid and model.ArtifactID:
|
||||
update_dict={
|
||||
'ArtifactID':model.ArtifactID,
|
||||
'ArtifactActionID':model.ArtifactActionID,
|
||||
'TaskID':model.TaskID,
|
||||
'ProjectName':model.ProjectName,
|
||||
'ProduceMixID':model.ProduceMixID,
|
||||
'BetonVolume':model.BetonVolume,
|
||||
'BetonGrade':model.BetonGrade,
|
||||
'BuriedDepth':model.BuriedDepth,
|
||||
'SkeletonID':model.SkeletonID,
|
||||
'RingTypeCode':model.RingTypeCode,
|
||||
'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)
|
||||
if model.ArtifactID:
|
||||
if not _artifactid:
|
||||
#不存在ArtifactID,更新PDRecord表
|
||||
update_dict={
|
||||
'ArtifactID':model.ArtifactID,
|
||||
'ArtifactActionID':model.ArtifactActionID,
|
||||
'TaskID':model.TaskID,
|
||||
'ProjectName':model.ProjectName,
|
||||
'ProduceMixID':model.ProduceMixID,
|
||||
'BetonVolume':model.BetonVolume,
|
||||
'BetonGrade':model.BetonGrade,
|
||||
'BuriedDepth':model.BuriedDepth,
|
||||
'SkeletonID':model.SkeletonID,
|
||||
'RingTypeCode':model.RingTypeCode,
|
||||
'SizeSpecification':model.SizeSpecification,
|
||||
'BlockNumber':model.BlockNumber,
|
||||
'BetonVolume':model.BetonVolume,
|
||||
'GStatus':model.GStatus,
|
||||
'PlannedVolume':model.PlannedVolume,
|
||||
'ScanTime':datetime.now(),
|
||||
}
|
||||
return self.dal.update_by_modulecode(model.MouldCode, update_dict)
|
||||
else:
|
||||
#已经存在了ArtifactID,不处理
|
||||
return 1
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
@ -233,8 +233,8 @@ class PDRecordDal(BaseDal):
|
||||
# 确保top为正整数
|
||||
if not isinstance(top, int) or top <= 0:
|
||||
raise ValueError("top参数必须是正整数")
|
||||
# _target_time = datetime.now() - timedelta(hours=4)
|
||||
_target_time ='2026-02-06 00:00:00'
|
||||
_target_time = datetime.now() - timedelta(hours=4)
|
||||
# _target_time ='2026-03-31 00:00:00'
|
||||
# 查询当前浇筑模具编号的前一条记录
|
||||
sql = f"""SELECT * FROM PDRecord WHERE CreateTime>? and ID>(
|
||||
select ID FROM PDRecord WHERE MouldCode=? and CreateTime>?
|
||||
@ -330,12 +330,23 @@ class PDRecordDal(BaseDal):
|
||||
print(f"更新PD管片任务失败: {e}")
|
||||
return False
|
||||
|
||||
def update_pd_notify(self, id: int, update_data: dict) -> bool:
|
||||
"""更新PD官片任务记录"""
|
||||
try:
|
||||
# 构建WHERE条件
|
||||
where_condition = f"ID={id} and Status<>5"
|
||||
# 使用update方法更新数据
|
||||
affected_rows = self.db_dao.update("PDRecord", update_data, where_condition)
|
||||
return affected_rows > 0
|
||||
except Exception as e:
|
||||
raise
|
||||
|
||||
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'
|
||||
# _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)
|
||||
|
||||
@ -254,8 +254,12 @@ class PDRecordModel:
|
||||
ProjectName: str=""
|
||||
#生产配合比编号
|
||||
ProduceMixID: str=""
|
||||
#派单方量
|
||||
#设计方量
|
||||
BetonVolume: float=0.0
|
||||
#计划派单方量
|
||||
BetonVolume2: float=0.0
|
||||
#修改后的方量
|
||||
BetonVolumeUpd: float=0.0
|
||||
#实际派单方量
|
||||
FBetonVolume: float=0.0
|
||||
#实际派单方量
|
||||
@ -288,6 +292,11 @@ class PDRecordModel:
|
||||
EndTime: str=""
|
||||
#派单时间(下发)
|
||||
OptTime: str=""
|
||||
#扫描时间
|
||||
ScanTime: str=""
|
||||
#ErpID
|
||||
ErpID: str=""
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -348,4 +357,4 @@ class LEDInfo:
|
||||
# 白班拆模强度文本描述
|
||||
DayStrengthValue: str
|
||||
# 夜班拆模强度文本描述
|
||||
NihtStrengthValue: str
|
||||
NihtStrengthValue: str
|
||||
|
||||
@ -116,6 +116,11 @@ class IniManager:
|
||||
def api_base_url(self):
|
||||
"""获取API基础URL"""
|
||||
return self._read_config_value('api', 'base_url', self._DEFAULT_API_BASE_URL, str)
|
||||
|
||||
@property
|
||||
def jbl_api_url(self):
|
||||
"""获取搅拌楼API基础URL"""
|
||||
return self._read_config_value('api', 'jbl_api_url', 'http://10.6.242.111:5002', str)
|
||||
|
||||
@property
|
||||
def api_login_url(self):
|
||||
|
||||
@ -31,6 +31,7 @@ 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_notify_finish = 2:pd,2:pd_notify_finish
|
||||
pd_set_mode = 2:pd,2:set_mode
|
||||
pd_set_volume = 2:pd,2:set_volume
|
||||
|
||||
|
||||
25
core/core_utils.py
Normal file
25
core/core_utils.py
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
|
||||
class CoreUtils:
|
||||
|
||||
@staticmethod
|
||||
def get_number_by_mould_code(mould_code:str=''):
|
||||
"""获取分块号"""
|
||||
"""根据模块码获取块号"""
|
||||
if '-' in mould_code:
|
||||
block_number = mould_code.split('-')[0][-2:]
|
||||
else:
|
||||
block_number=''
|
||||
return block_number
|
||||
|
||||
@staticmethod
|
||||
def get_size_by_mould_code(mould_code:str=''):
|
||||
"""获取模具尺寸"""
|
||||
if mould_code.startswith('SHR'):
|
||||
return '6600*1500'
|
||||
elif mould_code.startswith('SHZ'):
|
||||
return '6600*1200'
|
||||
else:
|
||||
return '0'
|
||||
|
||||
|
||||
157
core/pd_volume.py
Normal file
157
core/pd_volume.py
Normal file
@ -0,0 +1,157 @@
|
||||
from hardware.transmitter import TransmitterController
|
||||
from core.core_utils import CoreUtils
|
||||
import math
|
||||
|
||||
|
||||
class PdVolume:
|
||||
|
||||
@staticmethod
|
||||
def get_fact_volume_12(transmitter_controller:TransmitterController,mould_code:str='') -> float:
|
||||
"""获取实际派单发量"""
|
||||
# _now_volume=0
|
||||
block_number = mould_code.split('-')[0][-2:]
|
||||
if not block_number:
|
||||
print('分块号不能为空')
|
||||
return 0
|
||||
_pd_volume=0
|
||||
_init_lower_weight=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 block_number=='B1':
|
||||
_left_volume=_left_volume-0.5
|
||||
if _left_volume>0:
|
||||
_pd_volume=1.6-_left_volume
|
||||
_pd_volume=math.ceil(_pd_volume*10)/10
|
||||
else:
|
||||
_pd_volume=1.6
|
||||
elif block_number in ['B3','L1']:
|
||||
_pd_volume=1.5
|
||||
elif block_number=='B2':
|
||||
#浇B3
|
||||
_pd_volume=1.6
|
||||
#调整
|
||||
elif block_number=='L2':
|
||||
|
||||
if _init_lower_weight>950 and _init_lower_weight<=1350:
|
||||
_pd_volume=1.5
|
||||
elif _init_lower_weight>1350 and _init_lower_weight<=1600:
|
||||
_pd_volume=1.4
|
||||
elif _init_lower_weight<=950 and _init_lower_weight>800:
|
||||
_pd_volume=1.6
|
||||
else:
|
||||
|
||||
#1.9方,大约L2和F的量
|
||||
_pd_volume=1.9
|
||||
# if _weight>1300:
|
||||
#留0.15 math.floor(_now_volume*10)/10 保留一位小数,丢掉其他的
|
||||
# if prev_pd_volume>0:
|
||||
# _pd_volume=_pd_volume-(_left_volume+prev_pd_volume-1.86)
|
||||
# else:
|
||||
#不知道上一块叫的多少
|
||||
_pd_volume=_pd_volume-(_left_volume+1.6-1.46)
|
||||
_pd_volume=math.ceil(_pd_volume*10)/10+0.1
|
||||
# _pd_volume=math.ceil(_pd_volume*10)/10
|
||||
if _pd_volume>2.1:
|
||||
_pd_volume=2.1
|
||||
elif _pd_volume<0.8:
|
||||
_pd_volume=0.8
|
||||
|
||||
|
||||
|
||||
return _pd_volume
|
||||
|
||||
@staticmethod
|
||||
def get_fact_volume(transmitter_controller:TransmitterController,mould_code:str='') -> float:
|
||||
"""获取实际派单发量"""
|
||||
# _now_volume=0
|
||||
block_number = mould_code.split('-')[0][-2:]
|
||||
if not block_number:
|
||||
print('分块号不能为空')
|
||||
return 0
|
||||
_pd_volume=0
|
||||
_init_lower_weight=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 block_number=='B1':
|
||||
_left_volume=_left_volume-0.6
|
||||
if _left_volume>0:
|
||||
_pd_volume=2-_left_volume
|
||||
_pd_volume=math.ceil(_pd_volume*10)/10
|
||||
else:
|
||||
_pd_volume=2
|
||||
elif block_number in ['B3','L1']:
|
||||
_pd_volume=1.9
|
||||
elif block_number=='B2':
|
||||
#浇B3
|
||||
_pd_volume=2
|
||||
#调整
|
||||
elif block_number=='L2':
|
||||
if _init_lower_weight>1300 and _init_lower_weight<=1700:
|
||||
_pd_volume=1.9
|
||||
elif _init_lower_weight>1700 and _init_lower_weight<=1950:
|
||||
_pd_volume=1.8
|
||||
elif _init_lower_weight<=1300 and _init_lower_weight>1150:
|
||||
_pd_volume=2
|
||||
else:
|
||||
|
||||
#2.4方,大约L2和F的量
|
||||
_pd_volume=2.4
|
||||
# if _weight>1300:
|
||||
#留0.15 math.floor(_now_volume*10)/10 保留一位小数,丢掉其他的
|
||||
# if prev_pd_volume>0:
|
||||
# _pd_volume=_pd_volume-(_left_volume+prev_pd_volume-1.86)
|
||||
# else:
|
||||
#不知道上一块叫的多少
|
||||
_pd_volume=_pd_volume-(_left_volume+1.9-1.86)
|
||||
_pd_volume=math.ceil(_pd_volume*10)/10+0.1
|
||||
# _pd_volume=math.ceil(_pd_volume*10)/10
|
||||
if _pd_volume>2.2:
|
||||
_pd_volume=2.2
|
||||
elif _pd_volume<0.8:
|
||||
_pd_volume=0.8
|
||||
|
||||
return _pd_volume
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_volume_expect(mould_code:str=''):
|
||||
"""获取模具预期方量"""
|
||||
_block_number=CoreUtils.get_number_by_mould_code(mould_code)
|
||||
_size=CoreUtils.get_size_by_mould_code(mould_code)
|
||||
_volume_expect=0.0
|
||||
if _size=='6600*1500':
|
||||
if _block_number=='B1':
|
||||
_volume_expect=2
|
||||
elif _block_number=='B2':
|
||||
_volume_expect=2
|
||||
elif _block_number=='B3':
|
||||
_volume_expect=1.9
|
||||
elif _block_number=='L1':
|
||||
_volume_expect=1.9
|
||||
elif _block_number=='L2':
|
||||
_volume_expect=0
|
||||
elif _size=='6600*1200':
|
||||
if _block_number=='B1':
|
||||
_volume_expect=1.6
|
||||
elif _block_number=='B2':
|
||||
_volume_expect=1.6
|
||||
elif _block_number=='B3':
|
||||
_volume_expect=1.5
|
||||
elif _block_number=='L1':
|
||||
_volume_expect=1.5
|
||||
elif _block_number=='L2':
|
||||
_volume_expect=0
|
||||
|
||||
return _volume_expect
|
||||
@ -5,10 +5,11 @@ import time
|
||||
import threading
|
||||
from pymodbus.exceptions import ModbusException
|
||||
from config.settings import app_set_config
|
||||
from hardware.relay import RelayController
|
||||
|
||||
#下料过程控制
|
||||
class RelayFeedController:
|
||||
def __init__(self, relay,state):
|
||||
def __init__(self, relay:RelayController,state):
|
||||
self.relay = relay
|
||||
self.state = state
|
||||
# 添加线程锁,保护对下料斗控制的并发访问
|
||||
@ -17,28 +18,28 @@ class RelayFeedController:
|
||||
def control_upper_close_after(self):
|
||||
"""控制上料斗关在几秒后"""
|
||||
# 关闭上料斗出砼门
|
||||
self.relay.control(self.DOOR_UPPER_OPEN, 'close')
|
||||
self.relay.control(self.relay.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')
|
||||
self.relay.control(self.relay.DOOR_UPPER_OPEN, 'close')
|
||||
self.relay.control(self.relay.DOOR_UPPER_CLOSE, 'open')
|
||||
time.sleep(duration)
|
||||
self.relay.control(self.DOOR_UPPER_CLOSE, 'close')
|
||||
self.relay.control(self.relay.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')
|
||||
self.relay.control(self.relay.DOOR_UPPER_CLOSE, 'close')
|
||||
self.relay.control(self.relay.DOOR_UPPER_OPEN, 'open')
|
||||
time.sleep(duration)
|
||||
self.relay.control(self.DOOR_UPPER_OPEN, 'close')
|
||||
self.relay.control(self.relay.DOOR_UPPER_OPEN, 'close')
|
||||
|
||||
|
||||
def control_ring_open(self):
|
||||
"""控制下料斗关"""
|
||||
# 关闭下料斗出砼门
|
||||
self.relay.control(self.RING, 'open')
|
||||
self.relay.control(self.relay.RING, 'open')
|
||||
# 异步5秒后关闭
|
||||
threading.Thread(target=self._close_ring, daemon=True,name="_close_ring").start()
|
||||
|
||||
@ -50,53 +51,60 @@ class RelayFeedController:
|
||||
|
||||
# time.sleep(5)
|
||||
self.relay.control_arch_upper_open_sync(5)
|
||||
self.relay.control(self.DOOR_UPPER_CLOSE, 'open')
|
||||
self.relay.control(self.relay.DOOR_UPPER_CLOSE, 'open')
|
||||
time.sleep(1)
|
||||
self.relay.control(self.DOOR_UPPER_CLOSE, 'close')
|
||||
self.relay.control(self.relay.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')
|
||||
self.relay.control(self.relay.DOOR_UPPER_CLOSE, 'open')
|
||||
time.sleep(20)
|
||||
self.relay.control(self.DOOR_UPPER_CLOSE, 'close')
|
||||
self.relay.control(self.relay.DOOR_UPPER_CLOSE, 'close')
|
||||
if self.state.pd_set_mode==1:
|
||||
print('上料斗到搅拌楼')
|
||||
self.relay.control_upper_to_jbl()
|
||||
else:
|
||||
print('非自动派单,手动上料斗到搅拌楼')
|
||||
print('非自动派单,手动上料斗到搅拌楼')
|
||||
|
||||
print("上料斗关闭完成")
|
||||
|
||||
def _close_lower_5s(self):
|
||||
time.sleep(6)
|
||||
self.relay.control(self.DOOR_LOWER_CLOSE, 'close')
|
||||
self.relay.control(self.relay.DOOR_LOWER_CLOSE, 'close')
|
||||
|
||||
def _close_ring(self):
|
||||
time.sleep(3)
|
||||
self.relay.control(self.RING, 'close')
|
||||
self.relay.control(self.relay.RING, 'close')
|
||||
|
||||
def control_arch_lower_open(self):
|
||||
"""控制下料斗关"""
|
||||
# 关闭下料斗出砼门
|
||||
self.relay.control(self.BREAK_ARCH_LOWER, 'open')
|
||||
self.relay.control(self.relay.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')
|
||||
self.relay.control(self.relay.BREAK_ARCH_LOWER, 'open')
|
||||
# 异步5秒后关闭
|
||||
time.sleep(duration)
|
||||
self.relay.control(self.BREAK_ARCH_LOWER, 'close')
|
||||
self.relay.control(self.relay.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')
|
||||
self.relay.control(self.relay.BREAK_ARCH_UPPER, 'open')
|
||||
# 异步5秒后关闭
|
||||
time.sleep(duration)
|
||||
self.relay.control(self.BREAK_ARCH_UPPER, 'close')
|
||||
self.relay.control(self.relay.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')
|
||||
self.relay.control(self.relay.BREAK_ARCH_LOWER, 'close')
|
||||
|
||||
|
||||
def control_arch_upper_open_async(self,delay_seconds: float = 15):
|
||||
@ -106,14 +114,14 @@ class RelayFeedController:
|
||||
delay_seconds: 延迟关闭时间(秒),默认15秒
|
||||
"""
|
||||
# 关闭下料斗出砼门
|
||||
self.relay.control(self.BREAK_ARCH_UPPER, 'open')
|
||||
self.relay.control(self.relay.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')
|
||||
self.relay.control(self.relay.BREAK_ARCH_UPPER, 'close')
|
||||
|
||||
def control_arch_lower_open_async(self,delay_seconds: float = 15):
|
||||
"""异步控制上料斗振动
|
||||
@ -122,12 +130,12 @@ class RelayFeedController:
|
||||
delay_seconds: 延迟关闭时间(秒),默认15秒
|
||||
"""
|
||||
# 关闭下料斗出砼门
|
||||
self.relay.control(self.BREAK_ARCH_LOWER, 'open')
|
||||
self.relay.control(self.relay.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')
|
||||
self.relay.control(self.relay.BREAK_ARCH_LOWER, 'close')
|
||||
|
||||
|
||||
282
core/system.py
282
core/system.py
@ -5,7 +5,8 @@ import threading
|
||||
import time
|
||||
import queue
|
||||
import json
|
||||
from core.system_state import SystemState,FeedStatus,Upper_Door_Position
|
||||
import math
|
||||
from core.system_state import SystemState,FeedStatus,Upper_Door_Position,PD_StatusEnum
|
||||
from hardware.relay import RelayController
|
||||
from hardware.inverter import InverterController
|
||||
from hardware.transmitter import TransmitterController
|
||||
@ -16,12 +17,16 @@ from opc.opcua_client_feed import OpcuaClientFeed
|
||||
from busisness.blls import ArtifactBll,PDRecordBll,FreqRecordBll
|
||||
from busisness.models import ArtifactInfoModel,PDRecordModel,FreqRecordModel
|
||||
from core.pd_task_service import PD_TaskService
|
||||
|
||||
from vision.charge_3cls.charge_utils import run_stable_charge_loop
|
||||
from core.pd_volume import PdVolume
|
||||
from core.core_utils import CoreUtils
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
class FeedingControlSystem:
|
||||
def __init__(self):
|
||||
print('FeedingControlSystem初始化')
|
||||
self.state = SystemState()
|
||||
self.prev_pd_volume=0.0
|
||||
self.pd_task_service = PD_TaskService()
|
||||
self.pd_record_bll=PDRecordBll()
|
||||
self.freq_record_bll=FreqRecordBll()
|
||||
@ -154,9 +159,14 @@ class FeedingControlSystem:
|
||||
#记录盖板对齐时间
|
||||
_start_wait_seconds=time.time()
|
||||
if self.state._mould_finish_ratio>=0.05:
|
||||
_elasped_time=time.time()-_start_wait_seconds
|
||||
if _elasped_time<10:
|
||||
time.sleep(10-_elasped_time)
|
||||
_is_charge= run_stable_charge_loop()
|
||||
if _is_charge is not None:
|
||||
print(f'------------已插好振捣棒: {_is_charge}-------------')
|
||||
time.sleep(2)
|
||||
|
||||
# _elasped_time=time.time()-_start_wait_seconds
|
||||
# if _elasped_time<10:
|
||||
# time.sleep(10-_elasped_time)
|
||||
self.inverter_controller.control('start',230)
|
||||
_is_start=True
|
||||
print("----浇筑已经启动-----")
|
||||
@ -172,10 +182,10 @@ class FeedingControlSystem:
|
||||
else:
|
||||
print("----下料重量小于46KG,暂时不振捣-----")
|
||||
else:
|
||||
if self.state._mould_finish_ratio>=0.3:
|
||||
if self.state._mould_frequency!=220:
|
||||
self.inverter_controller.set_frequency(220)
|
||||
self.state._mould_frequency=220
|
||||
if self.state._mould_finish_ratio>=0.3 and 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._mould_finish_ratio>=0.4:
|
||||
if self.state._mould_frequency!=230:
|
||||
self.inverter_controller.set_frequency(230)
|
||||
@ -208,6 +218,7 @@ class FeedingControlSystem:
|
||||
try:
|
||||
not_poured = app_web_service.get_not_pour_artifacts()
|
||||
if not_poured:
|
||||
_is_refresh=False
|
||||
for item in reversed(not_poured):
|
||||
|
||||
if item.MouldCode is None or item.MouldCode == '':
|
||||
@ -228,13 +239,14 @@ class FeedingControlSystem:
|
||||
_ret=artifact_bll.save_artifact_task(_model_data)
|
||||
if _ret > 0:
|
||||
# 标记为已处理
|
||||
self.state._sys_segment_refresh=1
|
||||
_is_refresh=True
|
||||
# self.state._sys_segment_refresh=1
|
||||
processed_artifact_actions.append(item.MouldCode)
|
||||
if len(processed_artifact_actions) > 4:
|
||||
if len(processed_artifact_actions) > 10:
|
||||
processed_artifact_actions.pop(0)
|
||||
if item.ArtifactID:
|
||||
processed_artifact_ids.append(item.ArtifactID)
|
||||
if len(processed_artifact_ids) > 4:
|
||||
if len(processed_artifact_ids) > 10:
|
||||
processed_artifact_ids.pop(0)
|
||||
# 限制最多保存3条记录,删除最旧的
|
||||
|
||||
@ -266,6 +278,7 @@ class FeedingControlSystem:
|
||||
ProduceMixID=_model_task.ProduceMixID,
|
||||
BetonGrade=_model_task.BetonGrade,
|
||||
BetonVolume=item.BetonVolume,
|
||||
BetonVolume2=PdVolume.get_volume_expect(item.MouldCode),
|
||||
MouldCode=item.MouldCode,
|
||||
SkeletonID=item.SkeletonID,
|
||||
RingTypeCode=item.RingTypeCode,
|
||||
@ -276,25 +289,30 @@ class FeedingControlSystem:
|
||||
)
|
||||
else:
|
||||
_pd_record_data = PDRecordModel(
|
||||
MouldCode=item.MouldCode
|
||||
MouldCode=item.MouldCode,
|
||||
BetonVolume2=PdVolume.get_volume_expect(item.MouldCode),
|
||||
)
|
||||
|
||||
if _pd_record_data is None:
|
||||
continue
|
||||
_ret=pdrecord_bll.save_PD_record(_pd_record_data)
|
||||
if _ret > 0:
|
||||
self.state._sys_segment_refresh=1
|
||||
# self.state._sys_segment_refresh=1
|
||||
_is_refresh=True
|
||||
# 标记为已处理
|
||||
processed_pd_records.append(item.MouldCode)
|
||||
# 限制最多保存3条记录,删除最旧的
|
||||
if len(processed_pd_records) > 4:
|
||||
if len(processed_pd_records) > 10:
|
||||
processed_pd_records.pop(0)
|
||||
if item.ArtifactID:
|
||||
processed_pd_ids.append(item.ArtifactID)
|
||||
if len(processed_pd_ids) > 4:
|
||||
if len(processed_pd_ids) > 10:
|
||||
processed_pd_ids.pop(0)
|
||||
|
||||
#print(f"派单:已处理MouldCode {item.MouldCode} ArtifactID {item.ArtifactID}")
|
||||
|
||||
if _is_refresh:
|
||||
self.state._sys_segment_refresh=1
|
||||
except Exception as e:
|
||||
print(f"处理MouldCode {item.MouldCode} 时发生错误: {e}")
|
||||
|
||||
@ -382,41 +400,73 @@ class FeedingControlSystem:
|
||||
def _process_pd_jbl(self):
|
||||
# pass
|
||||
#根据当前浇筑块进行最近一块的派单
|
||||
_isFinish=False
|
||||
_start_time=None
|
||||
# _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()
|
||||
#增加生产阶段检测,
|
||||
|
||||
try:
|
||||
if self.state.pd_status==PD_StatusEnum.PD_Ready:
|
||||
#L1对齐排L2和F的。L2对齐派F块,L2完成派B1块
|
||||
# if _start_time is None:
|
||||
# _start_time=time.time()
|
||||
_isSuccess=self.send_pd_data()
|
||||
if _isSuccess:
|
||||
_isFinish=True
|
||||
if time.time()-_start_time>60:
|
||||
print('派单超时,人工介入')
|
||||
_isFinish=True
|
||||
elif self.state._feed_status==FeedStatus.FFinished:
|
||||
self.state.pd_status=PD_StatusEnum.PD_Send_Finish
|
||||
# _start_time=None
|
||||
# elif time.time()-_start_time>60:
|
||||
# print('派单超时,人工介入')
|
||||
# self.pd_record_bll.start_pd(PD_StatusEnum.PD_TimeOut.value,_pdrecord.MouldCode,0)
|
||||
# self.state.pd_status=PD_StatusEnum.PD_TimeOut
|
||||
# _start_time=None
|
||||
elif self.state.pd_status==PD_StatusEnum.PD_TimeOut:
|
||||
self.pd_record_bll.start_pd(PD_StatusEnum.PD_TimeOut.value,self.state.pd_mould_code,-1)
|
||||
self.state.pd_status=PD_StatusEnum.PD_Not
|
||||
except Exception as e:
|
||||
print(f"派单处理错误: {e}")
|
||||
self.state.pd_status=PD_StatusEnum.PD_Error
|
||||
_start_time=None
|
||||
_isFinish=False
|
||||
else:
|
||||
print('已开启手动派单,或未设置派单模式')
|
||||
print('已开启手动派单,或未设置派单模式')
|
||||
time.sleep(5)
|
||||
|
||||
def send_pd_data(self):
|
||||
"""
|
||||
发送PD数据到OPC队列
|
||||
:param _pd_status: PD状态
|
||||
"""
|
||||
# 构建PD数据
|
||||
# print("send_pd_data:1")
|
||||
_cur_mould=self.state.current_mould
|
||||
if _cur_mould is not None:
|
||||
# print("send_pd_data:2")
|
||||
if _cur_mould.MouldCode:
|
||||
_pdrecords = self.pd_record_bll.get_last_pds(_cur_mould.MouldCode,3)
|
||||
if _pdrecords:
|
||||
_pdrecord=_pdrecords[0]
|
||||
_pdrecords = self.pd_record_bll.get_last_pds(_cur_mould.MouldCode,2)
|
||||
if _pdrecords and len(_pdrecords)>1:
|
||||
_pdrecord=_pdrecords[1]
|
||||
# print(f"send_pd_data:3,{_pdrecord.MouldCode}")
|
||||
_block_number=CoreUtils.get_number_by_mould_code(_pdrecord.MouldCode)
|
||||
_size=CoreUtils.get_size_by_mould_code(_pdrecord.MouldCode)
|
||||
self.state.pd_mould_code=_pdrecord.MouldCode
|
||||
_pd_flag=1
|
||||
if not _pdrecord.TaskID:
|
||||
#查找最近的未浇筑块
|
||||
print(f"未扫描,使用最近的未浇筑块,{_size}")
|
||||
_nearrecord=self.pd_record_bll.get_near_pd_scan(_size)
|
||||
if _nearrecord is not None:
|
||||
_pd_flag=2
|
||||
_pdrecord.ArtifactActionID=0
|
||||
_pdrecord.TaskID=_nearrecord.TaskID
|
||||
_pdrecord.ProduceMixID=_nearrecord.ProduceMixID
|
||||
_pdrecord.PlannedVolume=_nearrecord.PlannedVolume
|
||||
print(f"未扫描,使用最近的未浇筑块,{_size},派单:{_pdrecord.TaskID},配比号:{_pdrecord.ProduceMixID}")
|
||||
# print(f"send_pd_data:5,{_pdrecord.TaskID}")
|
||||
if _pdrecord.TaskID:
|
||||
if _pdrecord.Status==1:
|
||||
tasks, artifact_list, send_list, half_volume = self.pd_task_service.process_not_pour_info(_pdrecords)
|
||||
# print(f"send_pd_data:6,{_pdrecord.TaskID}")
|
||||
# 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
|
||||
@ -433,22 +483,39 @@ class FeedingControlSystem:
|
||||
# }
|
||||
# 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':
|
||||
_fact_volumn=0
|
||||
if _pdrecord.BetonVolumeUpd>0.8:
|
||||
#当前已经修改过方量
|
||||
_fact_volumn=_pdrecord.BetonVolumeUpd
|
||||
print(f"当前已经修改过方量,方量:{_fact_volumn}")
|
||||
if _block_number=='F' and _fact_volumn<0.8:
|
||||
print(f'{_pdrecord.MouldCode} F块,不发送派单数据')
|
||||
self.pd_record_bll.start_pd(_pdrecord.MouldCode,0)
|
||||
self.pd_record_bll.start_pd(PD_StatusEnum.PD_Send_Finish.value,_pdrecord.MouldCode,0)
|
||||
else:
|
||||
_fact_volumn=self.get_fact_volumn(_pdrecord.MouldCode,_pdrecord.BlockNumber)
|
||||
if _fact_volumn>0:
|
||||
if _fact_volumn<0.8:
|
||||
# _fact_volumn=0
|
||||
if _size=='6600*1500':
|
||||
_fact_volumn=PdVolume.get_fact_volume(self.transmitter_controller,_pdrecord.MouldCode)
|
||||
elif _size=='6600*1200':
|
||||
_fact_volumn=PdVolume.get_fact_volume_12(self.transmitter_controller,_pdrecord.MouldCode)
|
||||
if _fact_volumn is not None and _fact_volumn>0:
|
||||
#更新派单表
|
||||
_ret_flag=self.pd_record_bll.start_pd(_pdrecord.MouldCode,_fact_volumn)
|
||||
# print(f"send_pd_data:7,{_pdrecord.MouldCode},{_fact_volumn}")
|
||||
_ret_flag=self.pd_record_bll.start_pd(PD_StatusEnum.PD_Send_Finish.value,_pdrecord.MouldCode,_fact_volumn)
|
||||
_cur_code=_pdrecords[0].MouldCode
|
||||
if _ret_flag:
|
||||
_opc_pd={
|
||||
"ID":_pdrecord.ID,
|
||||
"ArtifactActionID":_pdrecord.ArtifactActionID,
|
||||
"MouldCodePD":_pdrecord.MouldCode,
|
||||
"MouldCode":_cur_code,
|
||||
"TaskID":_pdrecord.TaskID,
|
||||
"ProduceMixID":_pdrecord.ProduceMixID,
|
||||
"BetonVolume":_fact_volumn,
|
||||
"PlannedVolume":_pdrecord.PlannedVolume
|
||||
"BetonVolume":round(_fact_volumn,1),
|
||||
"PlannedVolume":round(_pdrecord.PlannedVolume,1),
|
||||
"Flag":_pd_flag,
|
||||
"Msg":"已扫码" if _pd_flag==1 else "未扫码",
|
||||
"Time":time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
|
||||
}
|
||||
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)))
|
||||
@ -456,66 +523,19 @@ class FeedingControlSystem:
|
||||
return True
|
||||
else:
|
||||
print(f'{_pdrecord.MouldCode} 未派单,当前状态为:{_pdrecord.Status}')
|
||||
return False
|
||||
return True
|
||||
else:
|
||||
print(f'{_pdrecord.MouldCode} 未获取到数据-(等待扫码)')
|
||||
return False
|
||||
else:
|
||||
print(f'接口数据为空')
|
||||
return False
|
||||
else:
|
||||
print(f'当前浇筑模具为空')
|
||||
return False
|
||||
else:
|
||||
return None
|
||||
return False
|
||||
|
||||
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(
|
||||
@ -540,33 +560,62 @@ class FeedingControlSystem:
|
||||
|
||||
#发送到LED屏
|
||||
|
||||
time.sleep(app_set_config.led_interval)
|
||||
time.sleep(2)
|
||||
|
||||
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()
|
||||
try:
|
||||
if var_name=='pd_set_mode':
|
||||
self.state.pd_set_mode=val
|
||||
elif var_name=='pd_set_volume' and val:
|
||||
if self.state.pd_status!=PD_StatusEnum.PD_Ready:
|
||||
_notify=json.loads(val)
|
||||
_ret=self.pd_record_bll.update_PD_record_byid(_notify['ID'],{'BetonVolumeUpd':_notify['Volume']})
|
||||
if _ret:
|
||||
#刷新派单表
|
||||
try:
|
||||
self.state.opc_queue.put_nowait(('sys_pd_refresh',1))
|
||||
except queue.Full:
|
||||
pass
|
||||
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}')
|
||||
print('当前状态为派单中,不能修改方量')
|
||||
print('当前状态为派单中,不能修改方量')
|
||||
# self.state._db_pd_set_volume=val
|
||||
elif (var_name=='pd_notify_finish' or var_name=='pd_notify') and val:
|
||||
# self.state._db_pd_notify=val
|
||||
_notify=json.loads(val)
|
||||
if 'ID' in _notify and 'Time' in _notify and _notify['ID'] and _notify['Time']:
|
||||
if _notify['Flag']==5:
|
||||
#搅拌楼下料完毕
|
||||
_ret=self.pd_record_bll.update_pd_notify(_notify['ID'],{'GStatus':5,'Status':5,'FBetonVolume':_notify['BetonVolume'],'ErpID':_notify['ErpID'],'EndTime':time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())})
|
||||
if _ret:
|
||||
_time=datetime.strptime(_notify['Time'], '%Y-%m-%d %H:%M:%S')
|
||||
if _time+timedelta(seconds=10)>=datetime.now():
|
||||
# 发送命令
|
||||
# from service.jbl_service import app_jbl_service
|
||||
# _is_jb_finished=app_jbl_service.is_finish_jb()
|
||||
if self.state._upper_door_position==Upper_Door_Position.JBL:
|
||||
print("发送命令:搅拌楼--->振捣室")
|
||||
print("发送命令:搅拌楼--->振捣室")
|
||||
time.sleep(3)
|
||||
self.relay_controller.control_upper_to_zd()
|
||||
else:
|
||||
print("搅拌楼下料未完成,或者料斗未在搅拌楼")
|
||||
print("搅拌楼下料未完成,或者料斗未在搅拌楼")
|
||||
else:
|
||||
print("pd_notify:时间已过期")
|
||||
print("pd_notify:时间已过期")
|
||||
else:
|
||||
print("pd_notify:更新失败")
|
||||
print("pd_notify:更新失败")
|
||||
else:
|
||||
self.pd_record_bll.update_pd_notify(_notify['ID'],{'GStatus':_notify['Flag'],'ErpID':_notify['ErpID']})
|
||||
else:
|
||||
print(f'pd_notify:ID数据为空或Time数据为空')
|
||||
print(f'pd_notify:ID数据为空或Time数据为空')
|
||||
print(f'on_opcua_notify收到,var_name:{var_name},val:{val}')
|
||||
except Exception as e:
|
||||
print(f'on_opcua_notify处理异常,var_name:{var_name},val:{val},e:{e}')
|
||||
|
||||
def start_freq_thread(self):
|
||||
"""启动频率上传线程"""
|
||||
@ -588,7 +637,8 @@ class FeedingControlSystem:
|
||||
_id=_cur_mould.ArtifactActionID
|
||||
_mcode=_cur_mould.MouldCode
|
||||
# freq=self.inverter_controller.read_frequency()
|
||||
freq=self.state._mould_frequency
|
||||
# freq=self.state._mould_frequency
|
||||
freq=0
|
||||
if freq>0:
|
||||
self.freq_record_bll.insert_freq_record(FreqRecordModel(ArtifactID=_code,ArtifactActionID=_id,MouldCode=_mcode,Freq=freq))
|
||||
else:
|
||||
|
||||
@ -51,6 +51,11 @@ class SystemState:
|
||||
#当前模具状态
|
||||
self._feed_status=FeedStatus.FNone
|
||||
|
||||
#控制派单状态,1为可以派单 0未派单 2完成派单
|
||||
self.pd_status=PD_StatusEnum.PD_Not
|
||||
#记录当前派单编号
|
||||
self.pd_mould_code=''
|
||||
|
||||
#下料比例变频
|
||||
self.vf_frequencys=[{'radio':0,'fre':230},{'radio':0.3,'fre':230},{'radio':0.6,'fre':230}]
|
||||
|
||||
@ -59,6 +64,7 @@ class SystemState:
|
||||
self.mould_vibrate_time=0
|
||||
#当前浇筑模具,ArtifactInfo对象需要属性:ArtifactID,ArtifactActionID,MouldCode (RFID情况下绑定)
|
||||
self.current_mould=None
|
||||
self.current_block_number=''
|
||||
|
||||
|
||||
# 视觉系统状态
|
||||
@ -91,18 +97,14 @@ class SystemState:
|
||||
self.save_db(public_name,value) #有影响的话改成异步
|
||||
else:
|
||||
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()
|
||||
return
|
||||
elif public_name=='mould_finish_ratio':
|
||||
if time.time()-self.opc_timestamp>=1:
|
||||
if time.time()-self.opc_timestamp>=1 or value>=1:
|
||||
if value>1:
|
||||
value=1
|
||||
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()
|
||||
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:
|
||||
@ -117,16 +119,21 @@ class SystemState:
|
||||
def save_db(self,public_name,val):
|
||||
if not val:
|
||||
return
|
||||
if public_name=="db_mould_status":
|
||||
_code=val['mould_code']
|
||||
if _code:
|
||||
_status=val['status']
|
||||
if _status==3:
|
||||
#完成生产
|
||||
self.bll_artifact.finish_produce(_code,val['weight'])
|
||||
elif _status==2:
|
||||
#开始生产
|
||||
self.bll_artifact.start_produce(_code)
|
||||
try:
|
||||
if public_name=="db_mould_status":
|
||||
_code=val['mould_code']
|
||||
if _code:
|
||||
_status=val['status']
|
||||
if _status==3:
|
||||
#完成生产
|
||||
self.bll_artifact.finish_produce(_code,val['weight'])
|
||||
self.opc_queue.put_nowait(('sys_segment_refresh',1))
|
||||
elif _status==2:
|
||||
#开始生产
|
||||
self.bll_artifact.start_produce(_code)
|
||||
self.opc_queue.put_nowait(('sys_segment_refresh',1))
|
||||
except queue.Full:
|
||||
pass
|
||||
|
||||
|
||||
class FeedStatus(IntEnum):
|
||||
@ -155,11 +162,32 @@ class Upper_Door_Position(IntEnum):
|
||||
ZDS = 2
|
||||
Returning = 3
|
||||
|
||||
class PD_StatusEnum(IntEnum):
|
||||
# 预定派单
|
||||
PD_Ready = 1
|
||||
# 不派单
|
||||
PD_Not = 0
|
||||
# 完成派单下发
|
||||
PD_Send_Finish = 2
|
||||
#派单超时
|
||||
PD_TimeOut=3
|
||||
#未扫码
|
||||
PD_UnScan=4
|
||||
#已完成
|
||||
PD_Finish=5
|
||||
|
||||
#派单错误
|
||||
PD_Error=1001
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# class Upper_PLC_Status(IntEnum):
|
||||
# # 即将振捣室
|
||||
# PLC_ZDS_Ready = 4
|
||||
# #到达振捣室
|
||||
# #到达
|
||||
# PLC_ZDS_Finish = 5
|
||||
# #即将搅拌楼
|
||||
# PLC_JBL_Ready = 64
|
||||
|
||||
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
BIN
doc/控制程序对接.docx
Binary file not shown.
@ -97,7 +97,7 @@ class InverterController:
|
||||
self.inverter.write_register(0x2000, 1) # 1=正转运行
|
||||
print("启动变频器")
|
||||
elif action == 'stop':
|
||||
self.inverter.write_register(0x2000, 5) # 6=减速停机,5自由停机
|
||||
self.inverter.write_register(0x2000, 6) # 6=减速停机,5自由停机
|
||||
print("停止变频器")
|
||||
_ret=True
|
||||
else:
|
||||
|
||||
224
hardware/metric_device.py
Normal file
224
hardware/metric_device.py
Normal file
@ -0,0 +1,224 @@
|
||||
# metric_device.py
|
||||
import socket
|
||||
import threading
|
||||
import time
|
||||
import statistics
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional, Dict, Any, List
|
||||
from dataclasses import dataclass, asdict
|
||||
|
||||
@dataclass
|
||||
class DeviceReading:
|
||||
"""标准化设备读数"""
|
||||
weight: Optional[int] # 重量(None 表示 OL/ER)
|
||||
raw_data: bytes # 原始字节
|
||||
status: str # 'ST', 'US', 'OL', 'ER'
|
||||
timestamp: float # 本地接收时间(秒,time.time())
|
||||
rtt_ms: float # 网络往返延迟(毫秒)
|
||||
is_valid: bool # 是否含有效重量
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return {
|
||||
**asdict(self),
|
||||
"timestamp_iso": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.timestamp)),
|
||||
"weight_kg": self.weight / 1000.0 if self.weight is not None else None,
|
||||
}
|
||||
|
||||
|
||||
class MetricDevice(ABC):
|
||||
"""抽象计量设备基类 —— 支持 get() + RTT 统计"""
|
||||
def __init__(self, ip: str, port: int, name: str = ""):
|
||||
self.ip = ip
|
||||
self.port = port
|
||||
self.name = name or f"{ip}:{port}"
|
||||
self._latest_reading: Optional[DeviceReading] = None
|
||||
self._lock = threading.Lock()
|
||||
|
||||
# RTT 统计(滑动窗口)
|
||||
self._rtt_window: List[float] = []
|
||||
self._rtt_max_len = 100 # 保留最近 100 次 RTT
|
||||
self._rtt_stats = {
|
||||
"min_ms": float('inf'),
|
||||
"max_ms": 0.0,
|
||||
"avg_ms": 0.0,
|
||||
"count": 0,
|
||||
}
|
||||
|
||||
def _update_rtt(self, rtt_ms: float):
|
||||
"""更新 RTT 统计"""
|
||||
self._rtt_window.append(rtt_ms)
|
||||
if len(self._rtt_window) > self._rtt_max_len:
|
||||
self._rtt_window.pop(0)
|
||||
|
||||
rtt_list = self._rtt_window
|
||||
self._rtt_stats.update({
|
||||
"min_ms": min(rtt_list) if rtt_list else 0,
|
||||
"max_ms": max(rtt_list) if rtt_list else 0,
|
||||
"avg_ms": statistics.mean(rtt_list) if rtt_list else 0,
|
||||
"count": len(rtt_list),
|
||||
})
|
||||
|
||||
def get_rtt_stats(self) -> Dict[str, float]:
|
||||
"""获取 RTT 统计信息"""
|
||||
with self._lock:
|
||||
return self._rtt_stats.copy()
|
||||
|
||||
def get(self) -> Optional[Dict[str, Any]]:
|
||||
""" 统一 get() 接口:返回最新读数(含 RTT)"""
|
||||
with self._lock:
|
||||
if self._latest_reading is None:
|
||||
return None
|
||||
return self._latest_reading.to_dict()
|
||||
|
||||
@abstractmethod
|
||||
def _connect_and_run(self):
|
||||
"""子类实现连接与数据接收循环"""
|
||||
pass
|
||||
|
||||
def start(self):
|
||||
"""启动后台线程"""
|
||||
thread = threading.Thread(target=self._connect_and_run, daemon=True, name=f"Dev-{self.name}")
|
||||
thread.start()
|
||||
|
||||
def _update_reading(self, reading: DeviceReading):
|
||||
"""线程安全更新最新读数"""
|
||||
with self._lock:
|
||||
self._latest_reading = reading
|
||||
|
||||
|
||||
class TCPScaleDevice(MetricDevice):
|
||||
"""TCP ASCII 协议称重设备"""
|
||||
def __init__(self, ip: str, port: int, name: str = ""):
|
||||
super().__init__(ip, port, name)
|
||||
self._buffer = b""
|
||||
self._socket: Optional[socket.socket] = None
|
||||
self._running = True
|
||||
self._valid_weight = None # 有效重量
|
||||
|
||||
def _parse_line(self, line: bytes) -> Optional[DeviceReading]:
|
||||
try:
|
||||
clean = line.strip()
|
||||
if not clean:
|
||||
return None
|
||||
parts = clean.split(b',')
|
||||
if len(parts) < 3:
|
||||
return DeviceReading(
|
||||
weight=None, raw_data=clean, status="??",
|
||||
timestamp=time.time(), rtt_ms=0.0, is_valid=False
|
||||
)
|
||||
|
||||
status = parts[0].decode('ascii', errors='replace').upper()
|
||||
mode = parts[1].decode('ascii', errors='replace').upper()
|
||||
weight_str = parts[2].decode('ascii', errors='replace')
|
||||
|
||||
weight = None
|
||||
is_valid = False
|
||||
|
||||
if weight_str.replace('+', '').replace('-', '').isdigit():
|
||||
try:
|
||||
weight = int(weight_str)
|
||||
is_valid = True
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return DeviceReading(
|
||||
weight=weight,
|
||||
raw_data=clean,
|
||||
status=status,
|
||||
timestamp=time.time(),
|
||||
rtt_ms=0.0, # 稍后更新
|
||||
is_valid=is_valid,
|
||||
)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _extract_lines(self, data: bytes) -> List[bytes]:
|
||||
"""自适应行分割"""
|
||||
self._buffer += data
|
||||
lines = []
|
||||
if b'\r\n' in self._buffer or b'\n' in self._buffer:
|
||||
norm = self._buffer.replace(b'\r\n', b'\n')
|
||||
parts = norm.split(b'\n')
|
||||
*complete, self._buffer = parts
|
||||
lines = [line for line in complete if line]
|
||||
else:
|
||||
# 无换行符:整包作为单行
|
||||
if self._buffer:
|
||||
lines = [self._buffer]
|
||||
self._buffer = b""
|
||||
return lines
|
||||
|
||||
def _connect_and_run(self):
|
||||
while self._running:
|
||||
try:
|
||||
# 重连逻辑
|
||||
if self._socket is None:
|
||||
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self._socket.settimeout(5.0)
|
||||
self._socket.connect((self.ip, self.port))
|
||||
|
||||
# 接收数据
|
||||
t0 = time.perf_counter()
|
||||
data = self._socket.recv(1024)
|
||||
recv_rtt = (time.perf_counter() - t0) * 1000
|
||||
|
||||
if not data:
|
||||
break
|
||||
|
||||
# 更新 RTT
|
||||
self._update_rtt(recv_rtt * 2)
|
||||
|
||||
# 解析
|
||||
lines = self._extract_lines(data)
|
||||
for line in lines:
|
||||
reading = self._parse_line(line)
|
||||
if reading:
|
||||
reading.rtt_ms = recv_rtt * 2 # 近似 RTT
|
||||
self._update_reading(reading)
|
||||
if reading.is_valid: # 重量有效
|
||||
self._update_valid_weight(reading.weight)
|
||||
|
||||
except socket.timeout:
|
||||
continue
|
||||
except (OSError, socket.error) as e:
|
||||
print(f"[{self.name}] Error: {e}")
|
||||
if self._socket:
|
||||
self._socket.close()
|
||||
self._socket = None
|
||||
time.sleep(2)
|
||||
except Exception as e:
|
||||
print(f"[{self.name}] Fatal: {e}")
|
||||
break
|
||||
|
||||
if self._socket:
|
||||
self._socket.close()
|
||||
|
||||
def _update_valid_weight(self, weight: int):
|
||||
with self._lock:
|
||||
self._valid_weight = weight
|
||||
|
||||
def get_valid_weight(self) -> Optional[int]:
|
||||
with self._lock:
|
||||
return self._valid_weight
|
||||
|
||||
|
||||
# ===== 设计模式:设备工厂 + 管理器 =====
|
||||
class DeviceManager:
|
||||
"""设备工厂 + 单例管理"""
|
||||
_instances: Dict[str, MetricDevice] = {}
|
||||
_lock = threading.Lock()
|
||||
|
||||
@classmethod
|
||||
def get_device(cls, ip: str, port: int, name: str = "") -> MetricDevice:
|
||||
""" 工厂方法:按 (ip, port) 单例返回设备"""
|
||||
key = f"{ip}:{port}"
|
||||
with cls._lock:
|
||||
if key not in cls._instances:
|
||||
device = TCPScaleDevice(ip, port, name)
|
||||
device.start()
|
||||
cls._instances[key] = device
|
||||
return cls._instances[key]
|
||||
|
||||
@classmethod
|
||||
def get_all_devices(cls) -> List[MetricDevice]:
|
||||
return list(cls._instances.values())
|
||||
@ -305,13 +305,13 @@ class RelayController:
|
||||
|
||||
def control_upper_to_jbl(self):
|
||||
"""控制上料斗到搅拌楼"""
|
||||
# self.control(self.UPPER_TO_ZD, 'close')
|
||||
# self.control(self.UPPER_TO_JBL, 'open')
|
||||
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')
|
||||
self.control(self.UPPER_TO_JBL, 'close')
|
||||
self.control(self.UPPER_TO_ZD, 'open')
|
||||
|
||||
def close_all(self):
|
||||
"""关闭所有继电器"""
|
||||
@ -324,3 +324,5 @@ class RelayController:
|
||||
self.control(self.DOOR_LOWER_CLOSE, 'close')
|
||||
self.control(self.DOOR_UPPER_OPEN, 'close')
|
||||
self.control(self.DOOR_UPPER_CLOSE, 'close')
|
||||
|
||||
|
||||
|
||||
@ -1,200 +0,0 @@
|
||||
# hardware/transmitter.py
|
||||
import socket
|
||||
import threading
|
||||
from config.ini_manager import ini_manager
|
||||
from config.settings import app_set_config
|
||||
import time
|
||||
|
||||
class TransmitterController:
|
||||
def __init__(self):
|
||||
self.upper_ip = ini_manager.upper_transmitter_ip
|
||||
self.upper_port = ini_manager.upper_transmitter_port
|
||||
self.lower_ip = ini_manager.lower_transmitter_ip
|
||||
self.lower_port = ini_manager.lower_transmitter_port
|
||||
|
||||
# 存储最新重量值
|
||||
self.latest_weights = {1: None, 2: None}
|
||||
# 存储连接状态
|
||||
self.connection_status = {1: False, 2: False}
|
||||
# 线程控制
|
||||
self.running = True
|
||||
self.threads = {}
|
||||
# 连接配置
|
||||
self.TIMEOUT = 5 # 连接超时时间
|
||||
self.BUFFER_SIZE = 1024
|
||||
|
||||
# 启动后台接收线程
|
||||
self._start_receiver_threads()
|
||||
|
||||
def _start_receiver_threads(self):
|
||||
"""启动后台接收线程"""
|
||||
for transmitter_id in [1, 2]:
|
||||
if (transmitter_id == 1 and self.upper_ip and self.upper_port) or \
|
||||
(transmitter_id == 2 and self.lower_ip and self.lower_port):
|
||||
thread = threading.Thread(
|
||||
target=self._continuous_receiver,
|
||||
args=(transmitter_id,),
|
||||
daemon=True,
|
||||
name=f'transmitter_receiver_{transmitter_id}'
|
||||
)
|
||||
thread.start()
|
||||
self.threads[transmitter_id] = thread
|
||||
print(f"启动变送器 {transmitter_id} 后台接收线程")
|
||||
|
||||
def _continuous_receiver(self, transmitter_id):
|
||||
"""后台持续接收数据的线程函数"""
|
||||
while self.running:
|
||||
IP = None
|
||||
PORT = None
|
||||
|
||||
if transmitter_id == 1:
|
||||
IP = self.upper_ip
|
||||
PORT = self.upper_port
|
||||
elif transmitter_id == 2:
|
||||
IP = self.lower_ip
|
||||
PORT = self.lower_port
|
||||
|
||||
if not IP or not PORT:
|
||||
time.sleep(5)
|
||||
continue
|
||||
|
||||
sock = None
|
||||
try:
|
||||
# 创建连接
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(self.TIMEOUT)
|
||||
sock.connect((IP, PORT))
|
||||
self.connection_status[transmitter_id] = True
|
||||
print(f"变送器 {transmitter_id} 连接成功: {IP}:{PORT}")
|
||||
|
||||
# 持续接收数据
|
||||
while self.running:
|
||||
try:
|
||||
data = sock.recv(self.BUFFER_SIZE)
|
||||
if data:
|
||||
# 提取有效数据包
|
||||
packet = self.get_latest_valid_packet(data)
|
||||
if packet:
|
||||
# 解析重量
|
||||
weight = self.parse_weight(packet)
|
||||
if weight is not None:
|
||||
self.latest_weights[transmitter_id] = weight
|
||||
# 可选:打印接收到的重量
|
||||
# print(f"变送器 {transmitter_id} 重量: {weight}")
|
||||
else:
|
||||
# 连接关闭
|
||||
print(f"变送器 {transmitter_id} 连接关闭")
|
||||
break
|
||||
except socket.timeout:
|
||||
# 超时是正常的,继续接收
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f"接收数据异常: {e}")
|
||||
break
|
||||
|
||||
except ConnectionRefusedError:
|
||||
print(f"变送器 {transmitter_id} 连接失败:{IP}:{PORT} 拒绝连接")
|
||||
except Exception as e:
|
||||
print(f"变送器 {transmitter_id} 异常:{e}")
|
||||
finally:
|
||||
self.connection_status[transmitter_id] = False
|
||||
if sock:
|
||||
try:
|
||||
sock.close()
|
||||
except:
|
||||
pass
|
||||
# 重试间隔
|
||||
time.sleep(3)
|
||||
|
||||
# 直接读取 变送器返回的数据(从缓存中获取)
|
||||
def read_data_sub(self, transmitter_id):
|
||||
|
||||
"""
|
||||
Args: transmitter_id 为1 表示上料斗, 为2 表示下料斗
|
||||
return: 读取成功返回重量 weight: int, 失败返回 None
|
||||
"""
|
||||
# 直接返回缓存的最新重量值
|
||||
return self.latest_weights.get(transmitter_id)
|
||||
|
||||
def get_connection_status(self, transmitter_id):
|
||||
"""
|
||||
获取变送器连接状态
|
||||
Args: transmitter_id 为1 表示上料斗, 为2 表示下料斗
|
||||
return: 连接状态 bool
|
||||
"""
|
||||
return self.connection_status.get(transmitter_id, False)
|
||||
|
||||
def stop(self):
|
||||
"""停止后台线程"""
|
||||
self.running = False
|
||||
# 等待线程结束
|
||||
for thread in self.threads.values():
|
||||
if thread.is_alive():
|
||||
thread.join(timeout=2)
|
||||
print("变送器后台接收线程已停止")
|
||||
|
||||
def get_latest_valid_packet(self, raw_data):
|
||||
"""
|
||||
解决TCP粘包:
|
||||
从原始数据中,筛选所有有效包,返回最新的一个有效包
|
||||
有效包标准: 1. 能UTF-8解码 2. 按逗号拆分≥3个字段 3. 第三个字段含数字(重量)
|
||||
"""
|
||||
DELIMITER = b'\r\n'
|
||||
# 1. 按分隔符拆分,过滤空包
|
||||
packets = [p for p in raw_data.split(DELIMITER) if p]
|
||||
if not packets:
|
||||
return None
|
||||
|
||||
valid_packets = []
|
||||
for packet in packets:
|
||||
try:
|
||||
# 过滤无效ASCII字符(只保留可见字符)
|
||||
valid_chars = [c for c in packet if 32 <= c <= 126]
|
||||
filtered_packet = bytes(valid_chars)
|
||||
# 2. 验证解码
|
||||
data_str = filtered_packet.decode('utf-8').strip()
|
||||
# 3. 验证字段数量
|
||||
parts = data_str.split(',')
|
||||
if len(parts) < 3:
|
||||
continue
|
||||
# 4. 验证重量字段含数字
|
||||
weight_part = parts[2].strip()
|
||||
if not any(char.isdigit() for char in weight_part):
|
||||
continue
|
||||
# 满足所有条件,加入有效包列表
|
||||
valid_packets.append(packet)
|
||||
except (UnicodeDecodeError, IndexError):
|
||||
# 解码失败或字段异常,跳过该包
|
||||
continue
|
||||
|
||||
# 返回最后一个有效包(最新),无有效包则返回None
|
||||
return valid_packets[-1] if valid_packets else None
|
||||
|
||||
def parse_weight(self, packet_data):
|
||||
"""解析重量函数:提取重量数值(如从 b'ST,NT,+0000175\r\n' 中提取 175)"""
|
||||
try:
|
||||
data_str = packet_data.decode('utf-8').strip()
|
||||
parts = data_str.split(',')
|
||||
# 确保有完整的数据包,三个字段
|
||||
if len(parts) < 3:
|
||||
print(f"parse_weight: 包格式错误(字段不足):{data_str}")
|
||||
return None
|
||||
|
||||
weight_part = parts[2].strip()
|
||||
return int(''.join(filter(str.isdigit, weight_part)))
|
||||
except (IndexError, ValueError, UnicodeDecodeError) as e:
|
||||
# print(f"数据解析失败:{e},原始数据包:{packet_data}")
|
||||
return None
|
||||
|
||||
def read_data(self,transmitter_id):
|
||||
"""获取重量函数:根据变送器ID获取当前重量,三次"""
|
||||
max_try_times=5
|
||||
try_times=0
|
||||
while try_times<max_try_times:
|
||||
weight=self.read_data_sub(transmitter_id)
|
||||
if weight is not None:
|
||||
return weight
|
||||
try_times+=1
|
||||
print(f'-----获取重量异常-------------- transmitter_id: {transmitter_id}')
|
||||
print(f'-----获取重量异常-------------- transmitter_id: {transmitter_id}')
|
||||
return None
|
||||
32
hardware/transmitter_device.py
Normal file
32
hardware/transmitter_device.py
Normal file
@ -0,0 +1,32 @@
|
||||
# hardware/transmitter.py
|
||||
from pymodbus.exceptions import ModbusException
|
||||
import socket
|
||||
from config.ini_manager import ini_manager
|
||||
from config.settings import app_set_config
|
||||
import time
|
||||
from metric_device import DeviceManager
|
||||
from enum import Enum, unique
|
||||
|
||||
@unique
|
||||
class HopperType(Enum):
|
||||
UPPER = 1 # 上料斗
|
||||
LOWER = 2 # 下料斗
|
||||
|
||||
class TransmitterController:
|
||||
def __init__(self, relay_controller=None):
|
||||
self.relay_controller = relay_controller
|
||||
UPPER_HOPPER_IP = ini_manager.upper_transmitter_ip
|
||||
UPPER_HOPPER_PORT = ini_manager.upper_transmitter_port
|
||||
LOWER_HOPPER_IP = ini_manager.lower_transmitter_ip
|
||||
LOWER_HOPPER_PORT = ini_manager.lower_transmitter_port
|
||||
self.upper_scale = DeviceManager.get_device(UPPER_HOPPER_IP, UPPER_HOPPER_PORT, "upper_scale") # 上料斗
|
||||
self.lower_scale = DeviceManager.get_device(LOWER_HOPPER_IP, LOWER_HOPPER_PORT, "lower_scale") # 下料斗
|
||||
|
||||
def read_data(self, transmitter_id):
|
||||
"""获取重量函数:
|
||||
Args: transmitter_id 为指定的上/下料斗的id
|
||||
"""
|
||||
if transmitter_id == HopperType.UPPER:
|
||||
return self.upper_scale.get_valid_weight()
|
||||
elif transmitter_id == HopperType.LOWER:
|
||||
return self.lower_scale.get_valid_weight()
|
||||
@ -66,7 +66,7 @@ class OpcuaClientFeed(Thread):
|
||||
self.name="opcua_feed_client"
|
||||
self.target_var_paths = []
|
||||
#订阅配置文件中的参数
|
||||
self.subscription_name=["pd_notify","pd_set_mode","pd_set_volume"]
|
||||
self.subscription_name=["pd_notify","pd_notify_finish","pd_set_mode","pd_set_volume"]
|
||||
# self.server_url = ""
|
||||
self.read_opc_config() # 读取配置文件
|
||||
|
||||
@ -88,7 +88,7 @@ class OpcuaClientFeed(Thread):
|
||||
"""停止线程+断开连接"""
|
||||
self.is_running = False
|
||||
self.disconnect()
|
||||
self.wait()
|
||||
# self.wait()
|
||||
print("opcua客户端线程已退出")
|
||||
|
||||
def connect(self):
|
||||
|
||||
@ -83,6 +83,7 @@ class SimpleOPCUAServer:
|
||||
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_notify_finish=self.pd.add_variable(self.namespace, "pd_notify_finish", 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))
|
||||
|
||||
@ -106,6 +107,7 @@ class SimpleOPCUAServer:
|
||||
self.feed_status.set_writable(True)
|
||||
self.pd_data.set_writable(True)
|
||||
self.pd_notify.set_writable(True)
|
||||
self.pd_notify_finish.set_writable(True)
|
||||
self.pd_set_mode.set_writable(True)
|
||||
self.pd_set_volume.set_writable(True)
|
||||
self.sys_set_mode.set_writable(True)
|
||||
|
||||
@ -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\x9c\x94G\xc6\x9e\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x1e\
|
||||
\x00\x00\x04\x02\x00\x00\x00\x00\x00\x01\x00\x01\xfb\xa6\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9f\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a \
|
||||
\x00\x00\x05(\x00\x00\x00\x00\x00\x01\x00\x02\xbc.\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa6\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a%\
|
||||
\x00\x00\x01\x04\x00\x00\x00\x00\x00\x01\x00\x00+8\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9f\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a \
|
||||
\x00\x00\x00T\x00\x00\x00\x00\x00\x01\x00\x00\x11r\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa0\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a \
|
||||
\x00\x00\x05\xd0\x00\x00\x00\x00\x00\x01\x00\x02\xddk\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa1\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x22\
|
||||
\x00\x00\x02\xa6\x00\x00\x00\x00\x00\x01\x00\x01\x08\xf0\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa1\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x22\
|
||||
\x00\x00\x00:\x00\x00\x00\x00\x00\x01\x00\x00\x01\x05\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa0\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a \
|
||||
\x00\x00\x02\xd8\x00\x00\x00\x00\x00\x01\x00\x01\x1d/\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa0\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a \
|
||||
\x00\x00\x01\xda\x00\x00\x00\x00\x00\x01\x00\x00\x84\x83\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa6\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a%\
|
||||
\x00\x00\x03\x8e\x00\x00\x00\x00\x00\x01\x00\x01N/\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9f\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x1e\
|
||||
\x00\x00\x04\x84\x00\x00\x00\x00\x00\x01\x00\x02\x92\xa4\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9f\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a \
|
||||
\x00\x00\x01$\x00\x00\x00\x00\x00\x01\x00\x00-\x82\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9a\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x1d\
|
||||
\x00\x00\x02x\x00\x00\x00\x00\x00\x01\x00\x00\xf5\xe3\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa8\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a%\
|
||||
\x00\x00\x01L\x00\x00\x00\x00\x00\x01\x00\x00c\xfb\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa3\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x22\
|
||||
\x00\x00\x02\x92\x00\x00\x00\x00\x00\x01\x00\x01\x02\xaa\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa3\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a#\
|
||||
\x00\x00\x03\x02\x00\x00\x00\x00\x00\x01\x00\x01%\xbb\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xaa\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a&\
|
||||
\x00\x00\x05p\x00\x00\x00\x00\x00\x01\x00\x02\xc7\x9f\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa4\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a$\
|
||||
\x00\x00\x03\xae\x00\x00\x00\x00\x00\x01\x00\x01Q\xcb\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xab\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a(\
|
||||
\x00\x00\x00\x22\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa4\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a#\
|
||||
\x00\x00\x04\xc0\x00\x00\x00\x00\x00\x01\x00\x02\x96\x82\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa1\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a!\
|
||||
\x00\x00\x00\x88\x00\x00\x00\x00\x00\x01\x00\x00 \x01\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa6\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a%\
|
||||
\x00\x00\x06\x9a\x00\x00\x00\x00\x00\x01\x00\x03Yo\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa4\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a#\
|
||||
\x00\x00\x04\xa4\x00\x00\x00\x00\x00\x01\x00\x02\x94\xf7\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa0\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a!\
|
||||
\x00\x00\x03^\x00\x00\x00\x00\x00\x01\x00\x010\x95\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa7\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a%\
|
||||
\x00\x00\x00\xe8\x00\x00\x00\x00\x00\x01\x00\x00((\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa1\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a!\
|
||||
\x00\x00\x06l\x00\x00\x00\x00\x00\x01\x00\x036\xa7\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa6\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a$\
|
||||
\x00\x00\x02\x18\x00\x00\x00\x00\x00\x01\x00\x00\xe1c\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9b\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x1d\
|
||||
\x00\x00\x02`\x00\x00\x00\x00\x00\x01\x00\x00\xf4\xe4\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa4\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a#\
|
||||
\x00\x00\x03\xc4\x00\x00\x00\x00\x00\x01\x00\x01\xa8}\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa5\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a$\
|
||||
\x00\x00\x03>\x00\x00\x00\x00\x00\x01\x00\x01/\xe2\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9b\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x1e\
|
||||
\x00\x00\x01\xf8\x00\x00\x00\x00\x00\x01\x00\x00\xe0\xd7\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9d\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x1e\
|
||||
\x00\x00\x01\x80\x00\x00\x00\x00\x00\x01\x00\x00z\x9d\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9d\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x1e\
|
||||
\x00\x00\x05\xea\x00\x00\x00\x00\x00\x01\x00\x02\xeb\xec\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9d\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x1e\
|
||||
\x00\x00\x06T\x00\x00\x00\x00\x00\x01\x00\x032L\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xaa\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a&\
|
||||
\x00\x00\x06\x86\x00\x00\x00\x00\x00\x01\x00\x03?\x19\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa2\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x22\
|
||||
\x00\x00\x04h\x00\x00\x00\x00\x00\x01\x00\x02\x8a\xb0\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa8\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a&\
|
||||
\x00\x00\x05>\x00\x00\x00\x00\x00\x01\x00\x02\xbc\xd1\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa8\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a&\
|
||||
\x00\x00\x05\x0c\x00\x00\x00\x00\x00\x01\x00\x02\xb9\x8e\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa0\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a \
|
||||
\x00\x00\x00\xcc\x00\x00\x00\x00\x00\x01\x00\x00#\xb3\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa0\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a \
|
||||
\x00\x00\x00\x9e\x00\x00\x00\x00\x00\x01\x00\x00!\x14\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9d\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x1e\
|
||||
\x00\x00\x06\xb2\x00\x00\x00\x00\x00\x01\x00\x03_2\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa3\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x22\
|
||||
\x00\x00\x01`\x00\x00\x00\x00\x00\x01\x00\x00rC\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa8\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a&\
|
||||
\x00\x00\x064\x00\x00\x00\x00\x00\x01\x00\x03)\xfc\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa8\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a&\
|
||||
\x00\x00\x05\xa6\x00\x00\x00\x00\x00\x01\x00\x02\xd7\xf5\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa5\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a$\
|
||||
\x00\x00\x01\xa0\x00\x00\x00\x00\x00\x01\x00\x00{\x07\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa7\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a%\
|
||||
\x00\x00\x03\x1e\x00\x00\x00\x00\x00\x01\x00\x01&\xcb\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa7\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a%\
|
||||
\x00\x00\x04\xdc\x00\x00\x00\x00\x00\x01\x00\x02\x98\x0d\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa5\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a$\
|
||||
\x00\x00\x06\x0a\x00\x00\x00\x00\x00\x01\x00\x02\xecg\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa1\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a!\
|
||||
\x00\x00\x01\xc0\x00\x00\x00\x00\x00\x01\x00\x00\x84.\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9b\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x1d\
|
||||
\x00\x00\x04\xf2\x00\x00\x00\x00\x00\x01\x00\x02\x9c\x95\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9e\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x1e\
|
||||
\x00\x00\x04L\x00\x00\x00\x00\x00\x01\x00\x02h\xd9\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9b\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x1e\
|
||||
\x00\x00\x03\xd6\x00\x00\x00\x00\x00\x01\x00\x01\xaa\x0a\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa8\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a%\
|
||||
\x00\x00\x00n\x00\x00\x00\x00\x00\x01\x00\x00\x1d\x5c\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xaa\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a&\
|
||||
\x00\x00\x05\x88\x00\x00\x00\x00\x00\x01\x00\x02\xc8\xac\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa6\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a%\
|
||||
\x00\x00\x02\xea\x00\x00\x00\x00\x00\x01\x00\x01\x22\x08\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9f\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a \
|
||||
\x00\x00\x06 \x00\x00\x00\x00\x00\x01\x00\x03&\x04\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9e\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x1e\
|
||||
\x00\x00\x020\x00\x00\x00\x00\x00\x01\x00\x00\xea\x99\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa4\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a#\
|
||||
\x00\x00\x05Z\x00\x00\x00\x00\x00\x01\x00\x02\xc5\x8e\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa7\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a%\
|
||||
\x00\x00\x04\x22\x00\x00\x00\x00\x00\x01\x00\x01\xffL\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa2\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x22\
|
||||
\x00\x00\x01:\x00\x00\x00\x00\x00\x01\x00\x00.\xdc\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa2\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x22\
|
||||
\x00\x00\x03\xf0\x00\x00\x00\x00\x00\x01\x00\x01\xabj\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa2\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x22\
|
||||
\x00\x00\x03v\x00\x00\x00\x00\x00\x01\x00\x01I\x0f\
|
||||
\x00\x00\x01\x9c\x94G\xc6\xa3\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x22\
|
||||
\x00\x00\x02H\x00\x00\x00\x00\x00\x01\x00\x00\xf4\x8f\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9e\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x1e\
|
||||
\x00\x00\x02\xc0\x00\x00\x00\x00\x00\x01\x00\x01\x1c\xda\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9d\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x1e\
|
||||
\x00\x00\x044\x00\x00\x00\x00\x00\x01\x00\x02h\x84\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9e\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x1e\
|
||||
\x00\x00\x00\xb4\x00\x00\x00\x00\x00\x01\x00\x00#^\
|
||||
\x00\x00\x01\x9c\x94G\xc6\x9e\
|
||||
\x00\x00\x01\x9c\xec\x0b\x8a\x1e\
|
||||
"
|
||||
|
||||
def qInitResources():
|
||||
|
||||
56
service/jbl_service.py
Normal file
56
service/jbl_service.py
Normal file
@ -0,0 +1,56 @@
|
||||
import os
|
||||
import sys
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from service.api_http_client import BaseHttpClient
|
||||
from config.ini_manager import ini_manager
|
||||
|
||||
|
||||
class JBLService(BaseHttpClient):
|
||||
"""搅拌楼服务类,提供搅拌楼服务相关的API调用"""
|
||||
|
||||
def __init__(self):
|
||||
"""初始化搅拌楼服务"""
|
||||
super().__init__()
|
||||
self._host = ini_manager.jbl_api_url
|
||||
|
||||
def is_finish_jb(self)->bool:
|
||||
"""
|
||||
获取搅拌机状态信息
|
||||
Returns:
|
||||
是否下料完成
|
||||
"""
|
||||
url = f"{self._host}/API/flag"
|
||||
|
||||
# 调用API获取数据
|
||||
response_data = self.request(method='GET', url=url, timeout=ini_manager.api_timeout,
|
||||
retries=ini_manager.api_max_retries, retry_interval=ini_manager.api_retry_interval)
|
||||
|
||||
# 解析数据
|
||||
data = response_data.get('records', {})
|
||||
if not data:
|
||||
print(f"未获取到搅拌机记录信息")
|
||||
return None
|
||||
|
||||
_flag=data.get('Flag', '')
|
||||
if _flag=='1xnwq)A':
|
||||
print(f"搅拌机下料完成")
|
||||
return True
|
||||
else:
|
||||
print(f"搅拌机状态:{_flag}, 下料未完成")
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
app_jbl_service = JBLService()
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 创建楼服务实例
|
||||
status = app_jbl_service.is_finish_jb()
|
||||
if status:
|
||||
print("搅拌机下料完成")
|
||||
else:
|
||||
print("搅拌机下料未完成")
|
||||
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
[api]
|
||||
max_retries = 3
|
||||
retry_interval = 1.0
|
||||
timeout = 30.0
|
||||
timeout = 3
|
||||
auth_timeout = 43200
|
||||
base_url = https://www.shnthy.com:9154
|
||||
login_model = {"Program": 11, "SC": "1000000001", "loginName": "leduser", "password": "bfcda35cf4eba92d4583931bbe4ff72ffdfa8b5c9c4b72611bd33f5babee069d"}
|
||||
jbl_api_url=http://10.6.242.111:5002
|
||||
|
||||
[app]
|
||||
log_path = logs/app.log
|
||||
|
||||
143
tests/pd_service.py
Normal file
143
tests/pd_service.py
Normal file
@ -0,0 +1,143 @@
|
||||
import sys
|
||||
import os
|
||||
import math
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from hardware.transmitter import TransmitterController
|
||||
|
||||
|
||||
class PdService:
|
||||
def __init__(self):
|
||||
print('派单测试初始化')
|
||||
self.transmitter_controller=TransmitterController()
|
||||
# self.pd_record_bll=PDRecordBll()
|
||||
|
||||
|
||||
def get_fact_volumn_12(self,block_number:str='') -> float:
|
||||
"""获取实际派单发量"""
|
||||
# _now_volume=0
|
||||
if not block_number:
|
||||
print('分块号不能为空')
|
||||
return
|
||||
_pd_volume=0
|
||||
_init_lower_weight=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 block_number=='B1':
|
||||
_left_volume=_left_volume-0.5
|
||||
if _left_volume>0:
|
||||
_pd_volume=1.6-_left_volume
|
||||
_pd_volume=math.ceil(_pd_volume*10)/10
|
||||
else:
|
||||
_pd_volume=1.6
|
||||
elif block_number in ['B3','L1']:
|
||||
_pd_volume=1.5
|
||||
elif block_number=='B2':
|
||||
#浇B3
|
||||
_pd_volume=1.6
|
||||
# self.prev_pd_volume=2
|
||||
#调整
|
||||
elif block_number=='L2':
|
||||
|
||||
if _init_lower_weight>950 and _init_lower_weight<=1350:
|
||||
_pd_volume=1.5
|
||||
elif _init_lower_weight>1350 and _init_lower_weight<=1600:
|
||||
_pd_volume=1.4
|
||||
elif _init_lower_weight<=950 and _init_lower_weight>800:
|
||||
_pd_volume=1.6
|
||||
else:
|
||||
|
||||
#1.9方,大约L2和F的量
|
||||
_pd_volume=1.9
|
||||
# if _weight>1300:
|
||||
#留0.15 math.floor(_now_volume*10)/10 保留一位小数,丢掉其他的
|
||||
# if prev_pd_volume>0:
|
||||
# _pd_volume=_pd_volume-(_left_volume+prev_pd_volume-1.86)
|
||||
# else:
|
||||
#不知道上一块叫的多少
|
||||
_pd_volume=_pd_volume-(_left_volume+1.6-1.46)
|
||||
_pd_volume=math.ceil(_pd_volume*10)/10+0.1
|
||||
# _pd_volume=math.ceil(_pd_volume*10)/10
|
||||
if _pd_volume>2.1:
|
||||
_pd_volume=2.1
|
||||
elif _pd_volume<0.8:
|
||||
_pd_volume=0.8
|
||||
|
||||
|
||||
|
||||
return _pd_volume
|
||||
|
||||
def get_fact_volumn(self,block_number:str='') -> float:
|
||||
"""获取实际派单发量"""
|
||||
# _now_volume=0
|
||||
if not block_number:
|
||||
print('分块号不能为空')
|
||||
return
|
||||
_pd_volume=0
|
||||
_init_lower_weight=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 block_number=='B1':
|
||||
_left_volume=_left_volume-0.6
|
||||
if _left_volume>0:
|
||||
_pd_volume=2-_left_volume
|
||||
_pd_volume=math.ceil(_pd_volume*10)/10
|
||||
else:
|
||||
_pd_volume=2
|
||||
elif block_number in ['B3','L1']:
|
||||
_pd_volume=1.9
|
||||
elif block_number=='B2':
|
||||
#浇B3
|
||||
_pd_volume=2
|
||||
# self.prev_pd_volume=2
|
||||
#调整
|
||||
elif block_number=='L2':
|
||||
|
||||
if _init_lower_weight>1250 and _init_lower_weight<=1650:
|
||||
_pd_volume=1.9
|
||||
elif _init_lower_weight>1650 and _init_lower_weight<=1900:
|
||||
_pd_volume=1.8
|
||||
elif _init_lower_weight<=1250 and _init_lower_weight>1100:
|
||||
_pd_volume=2
|
||||
else:
|
||||
|
||||
#2.4方,大约L2和F的量
|
||||
_pd_volume=2.4
|
||||
# if _weight>1300:
|
||||
#留0.15 math.floor(_now_volume*10)/10 保留一位小数,丢掉其他的
|
||||
# if prev_pd_volume>0:
|
||||
# _pd_volume=_pd_volume-(_left_volume+prev_pd_volume-1.86)
|
||||
# else:
|
||||
#不知道上一块叫的多少
|
||||
_pd_volume=_pd_volume-(_left_volume+1.9-1.86)
|
||||
_pd_volume=math.ceil(_pd_volume*10)/10+0.1
|
||||
# _pd_volume=math.ceil(_pd_volume*10)/10
|
||||
if _pd_volume>2.1:
|
||||
_pd_volume=2.1
|
||||
elif _pd_volume<0.8:
|
||||
_pd_volume=0.8
|
||||
|
||||
|
||||
|
||||
return _pd_volume
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
system = PdService()
|
||||
|
||||
volume= system.get_fact_volumn('L2',1.9)
|
||||
print("派单方量:",volume)
|
||||
|
||||
|
||||
@ -7,13 +7,14 @@ 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_state import SystemState,PD_StatusEnum
|
||||
from core.system import FeedingControlSystem
|
||||
from busisness.models import ArtifactInfo,FreqRecordModel
|
||||
from busisness.blls import FreqRecordBll
|
||||
from hardware.relay import RelayController
|
||||
|
||||
system=FeedingControlSystem()
|
||||
system.state.current_mould=ArtifactInfo(MouldCode="SHR2B3-5")
|
||||
system.state.current_mould=ArtifactInfo(MouldCode="SHR2F-8")
|
||||
|
||||
def test_pd_record_send():
|
||||
"""测试派单记录发送"""
|
||||
@ -22,12 +23,24 @@ def test_pd_record_send():
|
||||
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 test_upper_to_jbl():
|
||||
"""测试上料斗到搅拌楼"""
|
||||
relay = RelayController()
|
||||
relay.control_upper_to_jbl()
|
||||
|
||||
def test_upper_to_zd():
|
||||
"""测试上料斗到料斗"""
|
||||
relay = RelayController()
|
||||
relay.control_upper_to_zd()
|
||||
|
||||
|
||||
|
||||
def add(a, b):
|
||||
@ -89,11 +102,21 @@ if __name__ == "__main__":
|
||||
# 运行所有测试
|
||||
|
||||
system.opcua_client_feed.start()
|
||||
system.start_opc_queue_thread()
|
||||
|
||||
test_pd_record_send()
|
||||
system.opcua_client_feed.start_accept()
|
||||
# system.start_opc_queue_thread()
|
||||
|
||||
# system.start_pd_thread()
|
||||
# time.sleep(10)
|
||||
# system.state.pd_status=PD_StatusEnum.PD_Ready
|
||||
# test_pd_record_send()
|
||||
# pytest.main([f'{__file__}::test_api_process'])
|
||||
# pytest.main([f'{__file__}::test_freq_record_send'])
|
||||
# pytest.main([f'{__file__}::test_upper_to_jbl'])
|
||||
# relay = RelayController()
|
||||
# relay.control(relay.UPPER_TO_ZD, 'close')
|
||||
# relay.control(relay.UPPER_TO_JBL, 'close')
|
||||
# pytest.main([f'{__file__}::test_upper_to_zd'])
|
||||
|
||||
while True:
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
BIN
vision/charge_3cls/charge0324.rknn
Normal file
BIN
vision/charge_3cls/charge0324.rknn
Normal file
Binary file not shown.
131
vision/charge_3cls/charge_cls_rknn.py
Normal file
131
vision/charge_3cls/charge_cls_rknn.py
Normal file
@ -0,0 +1,131 @@
|
||||
import os
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from collections import deque
|
||||
|
||||
class StableClassJudge:
|
||||
"""
|
||||
连续三帧稳定判决器:
|
||||
- class0 / class1 连续 3 帧 -> 输出
|
||||
- class2 -> 清空计数,重新统计
|
||||
"""
|
||||
|
||||
def __init__(self, stable_frames=3, ignore_class=2):
|
||||
self.stable_frames = stable_frames
|
||||
self.ignore_class = ignore_class
|
||||
self.buffer = deque(maxlen=stable_frames)
|
||||
|
||||
def reset(self):
|
||||
self.buffer.clear()
|
||||
|
||||
def update(self, class_id):
|
||||
if class_id == self.ignore_class:
|
||||
self.reset()
|
||||
return None
|
||||
|
||||
self.buffer.append(class_id)
|
||||
|
||||
if len(self.buffer) < self.stable_frames:
|
||||
return None
|
||||
|
||||
if len(set(self.buffer)) == 1:
|
||||
stable_class = self.buffer[0]
|
||||
self.reset()
|
||||
return stable_class
|
||||
|
||||
return None
|
||||
|
||||
# ---------------------------
|
||||
# 三分类映射
|
||||
# ---------------------------
|
||||
CLASS_NAMES = {
|
||||
0: "插好",
|
||||
1: "未插好",
|
||||
2: "有遮挡"
|
||||
}
|
||||
|
||||
# ---------------------------
|
||||
# RKNN 全局实例(只加载一次)
|
||||
# ---------------------------
|
||||
_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
|
||||
|
||||
rknn = RKNNLite(verbose=False)
|
||||
ret = rknn.load_rknn(model_path)
|
||||
if ret != 0:
|
||||
raise RuntimeError(f"Load RKNN failed: {ret}")
|
||||
|
||||
ret = rknn.init_runtime(core_mask=RKNNLite.NPU_CORE_0)
|
||||
if ret != 0:
|
||||
raise RuntimeError(f"Init runtime failed: {ret}")
|
||||
|
||||
_global_rknn = rknn
|
||||
print(f"[INFO] RKNN 模型加载成功:{model_path}")
|
||||
return rknn
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# 预处理(输入 uint8,RKNN 内部转 float32)
|
||||
# ---------------------------
|
||||
def resize_stretch(image, size=640):
|
||||
return cv2.resize(image, (size, size))
|
||||
|
||||
def preprocess_image_for_rknn(img, size=640):
|
||||
# 输入必须是 uint8 [0,255],即使模型是 float32
|
||||
img_resized = resize_stretch(img, size=size)
|
||||
img_rgb = cv2.cvtColor(img_resized, cv2.COLOR_BGR2RGB)
|
||||
input_tensor = np.expand_dims(img_rgb, axis=0).astype(np.uint8) # NHWC, uint8
|
||||
return np.ascontiguousarray(input_tensor)
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# 单次 RKNN 推理(三分类,float32 模型)
|
||||
# ---------------------------
|
||||
def rknn_classify_preprocessed(input_tensor, model_path):
|
||||
rknn = init_rknn_model(model_path)
|
||||
outs = rknn.inference([input_tensor])
|
||||
|
||||
# 直接得到 logits
|
||||
probs = outs[0].flatten().astype(np.float32) # shape: (3,)
|
||||
class_id = int(np.argmax(probs))
|
||||
return class_id, probs
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# 单张图片推理
|
||||
# ---------------------------
|
||||
def classify_single_image(frame, model_path, size=640):
|
||||
if frame is None:
|
||||
raise FileNotFoundError("输入帧为空")
|
||||
|
||||
input_tensor = preprocess_image_for_rknn(frame, size=size)
|
||||
class_id, probs = rknn_classify_preprocessed(input_tensor, model_path)
|
||||
class_name = CLASS_NAMES.get(class_id, f"未知类别 ({class_id})")
|
||||
|
||||
return {
|
||||
"class_id": class_id,
|
||||
"class": class_name,
|
||||
"score": round(float(probs[class_id]), 4),
|
||||
"raw": probs.tolist()
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# 示例调用
|
||||
# ---------------------------
|
||||
if __name__ == "__main__":
|
||||
model_path = "charge0324.rknn"
|
||||
image_path = "class2.jpg"
|
||||
|
||||
frame = cv2.imread(image_path)
|
||||
if frame is None:
|
||||
raise FileNotFoundError(f"无法读取图片:{image_path}")
|
||||
|
||||
result = classify_single_image(frame, model_path)
|
||||
print("[RESULT]", result)
|
||||
88
vision/charge_3cls/charge_utils.py
Normal file
88
vision/charge_3cls/charge_utils.py
Normal file
@ -0,0 +1,88 @@
|
||||
import os
|
||||
import cv2
|
||||
#from rknnlite.api import RKNNLite
|
||||
import time
|
||||
|
||||
# classify_single_image, StableClassJudge, CLASS_NAMES 已在 muju_cls_rknn 中定义
|
||||
from .charge_cls_rknn import classify_single_image, StableClassJudge, CLASS_NAMES
|
||||
|
||||
# 获取当前文件所在目录的绝对路径
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
def run_stable_charge_loop():
|
||||
"""
|
||||
image_source: cv2.VideoCapture 对象
|
||||
"""
|
||||
_ret=None
|
||||
# 使用相对于当前文件的绝对路径
|
||||
model_path = os.path.join(current_dir, "charge0324.rknn")
|
||||
# roi_file = os.path.join(current_dir, "roi_coordinates", "muju_roi.txt")
|
||||
RTSP_URL = "rtsp://admin:XJ123456@192.168.250.60:554/streaming/channels/101"
|
||||
stable_frames=5
|
||||
print(f"正在连接 RTSP 流: {RTSP_URL}")
|
||||
cap =None
|
||||
try:
|
||||
cap = cv2.VideoCapture(RTSP_URL)
|
||||
# 降低 RTSP 延迟(部分摄像头支持)
|
||||
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
|
||||
|
||||
if not cap.isOpened():
|
||||
print("无法打开 RTSP 流,请检查网络、账号密码或 URL")
|
||||
return None
|
||||
|
||||
print("RTSP 流连接成功,开始推理...")
|
||||
|
||||
judge = StableClassJudge(
|
||||
stable_frames=stable_frames,
|
||||
ignore_class=2 # 忽略“有遮挡”类别参与稳定判断
|
||||
)
|
||||
|
||||
if not hasattr(cap, "read"):
|
||||
raise TypeError("image_source 必须是 cv2.VideoCapture 实例")
|
||||
_max_count=10
|
||||
while True:
|
||||
_max_count=_max_count-1
|
||||
|
||||
ret, frame = cap.read()
|
||||
if not ret:
|
||||
print("无法读取视频帧(可能是流断开或结束)")
|
||||
continue
|
||||
# 上下左右翻转
|
||||
# frame = cv2.flip(frame, -1)
|
||||
|
||||
# ---------------------------
|
||||
# 单帧推理
|
||||
# ---------------------------
|
||||
result = classify_single_image(frame, model_path)
|
||||
|
||||
|
||||
class_id = result["class_id"]
|
||||
class_name = result["class"]
|
||||
score = result["score"]
|
||||
|
||||
print(f"[FRAME] {class_name} | conf={score:.3f}")
|
||||
if score>0.8:
|
||||
# ---------------------------
|
||||
# 稳定判断
|
||||
# ---------------------------
|
||||
stable_class_id = judge.update(class_id)
|
||||
|
||||
if stable_class_id is not None:
|
||||
_ret=CLASS_NAMES[stable_class_id]
|
||||
if _ret is None:
|
||||
print("-------当前振捣棒检测为空,继续等待稳定------")
|
||||
continue
|
||||
if _ret=="插好":
|
||||
break
|
||||
print(f"-------当前振捣棒检测为:{_ret},继续等待稳定------")
|
||||
else:
|
||||
print("-------当前振捣棒检测为空,继续等待稳定------")
|
||||
|
||||
time.sleep(0.1)
|
||||
finally:
|
||||
if cap is not None:
|
||||
cap.release()
|
||||
return _ret
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
vision/charge_3cls/class1.png
Normal file
BIN
vision/charge_3cls/class1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 MiB |
BIN
vision/charge_3cls/class2.png
Normal file
BIN
vision/charge_3cls/class2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 MiB |
@ -17,7 +17,8 @@ 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,Upper_Door_Position
|
||||
from core.system_state import SystemState,FeedStatus,Upper_Door_Position,PD_StatusEnum
|
||||
from core.core_utils import CoreUtils
|
||||
from dataclasses import asdict
|
||||
import json
|
||||
import math
|
||||
@ -301,6 +302,9 @@ class VisualCallback:
|
||||
def _arch_loop(self):
|
||||
"""破拱线程"""
|
||||
while self._is_running:
|
||||
if self._is_finish:
|
||||
time.sleep(2)
|
||||
continue
|
||||
try:
|
||||
current_time = time.time()
|
||||
# 检查下料斗破拱(只有在下料过程中才检查)
|
||||
@ -314,8 +318,9 @@ class VisualCallback:
|
||||
if (_weight_changed< 200) and \
|
||||
(current_time - self._last_arch_time) >= 2:
|
||||
self._last_arch_time = current_time
|
||||
print('---------------------第一阶段振动5秒(小于200KG)-----------------')
|
||||
self.relay_feed.control_arch_lower_open_sync(5)
|
||||
print('---------------------第一阶段振动3秒(小于200KG)-----------------')
|
||||
if self._current_angle>25:
|
||||
self.relay_feed.control_arch_lower_open_sync(3)
|
||||
self._last_arch_one_weight = _arch_weight
|
||||
continue
|
||||
self._last_arch_one_weight = _arch_weight
|
||||
@ -345,8 +350,9 @@ class VisualCallback:
|
||||
if (_weight_changed < 100) and \
|
||||
(current_time - self._last_arch_time) >= 2:
|
||||
self._last_arch_time = current_time
|
||||
print('---------------------第三阶段振动5秒(小于100KG)-----------------')
|
||||
self.relay_feed.control_arch_lower_open_sync(5)
|
||||
print('---------------------第三阶段振动3秒(小于100KG)-----------------')
|
||||
if self._current_angle>25:
|
||||
self.relay_feed.control_arch_lower_open_sync(3)
|
||||
self._last_arch_three_weight = _arch_weight
|
||||
continue
|
||||
self._last_arch_three_weight = _arch_weight
|
||||
@ -379,7 +385,8 @@ class VisualCallback:
|
||||
(current_time - self._last_arch_time) >= 2:
|
||||
self._last_arch_time = current_time
|
||||
print(f'---------------------第五阶段振动3秒(小于{_min_arch_weight}kg))-----------------')
|
||||
self.relay_feed.control_arch_lower_open_sync(3)
|
||||
if self._current_angle>25:
|
||||
self.relay_feed.control_arch_lower_open_sync(3)
|
||||
self._last_arch_five_weight = _arch_weight
|
||||
continue
|
||||
self._last_arch_five_weight = _arch_weight
|
||||
@ -475,7 +482,7 @@ class VisualCallback:
|
||||
|
||||
def _run_feed(self):
|
||||
_is_api_request=True
|
||||
while True:
|
||||
while self.state.running:
|
||||
# print("------------已启动----------------")
|
||||
if self._is_feed_start:
|
||||
|
||||
@ -490,15 +497,23 @@ class VisualCallback:
|
||||
_is_aligned=self._aligned_get_times(1)
|
||||
if _is_aligned:
|
||||
_is_api_request=True
|
||||
if self.state.pd_status==PD_StatusEnum.PD_Ready:
|
||||
self.state.pd_status=PD_StatusEnum.PD_TimeOut
|
||||
print('------------启动程序后,进入第一块-------------')
|
||||
self._is_first_module=False
|
||||
self._mould_before_aligned=True
|
||||
if self._cur_mould_model:
|
||||
self.state._db_mould_status={
|
||||
self.state.current_mould=self._cur_mould_model
|
||||
self.state.current_block_number=CoreUtils.get_number_by_mould_code(self._cur_mould_model.MouldCode)
|
||||
self.state._db_mould_status={
|
||||
'mould_code':self._cur_mould_model.MouldCode,
|
||||
'status':2,
|
||||
'weight':0,
|
||||
}
|
||||
else:
|
||||
self.state.current_mould=None
|
||||
self.state.current_block_number=''
|
||||
|
||||
|
||||
_current_weight=self.transmitter_controller.read_data(2)
|
||||
if _current_weight:
|
||||
@ -508,6 +523,8 @@ class VisualCallback:
|
||||
return
|
||||
|
||||
self.state._feed_status=FeedStatus.FCheckGB
|
||||
# if not self.state.current_block_number=='F':
|
||||
# self.state.pd_status=PD_StatusEnum.PD_Ready
|
||||
# self.is_start_visual=True
|
||||
self.run_feed_all()
|
||||
elif self._is_finish and self._is_finish_ratio>=0.7:
|
||||
@ -519,6 +536,8 @@ class VisualCallback:
|
||||
if _is_not_aligned:
|
||||
#标志位
|
||||
self._mould_before_aligned=False
|
||||
print('------------连续盖板未对齐完成,进入派单标志-------------')
|
||||
self.state.pd_status=PD_StatusEnum.PD_Ready
|
||||
#print('------------连续盖板未对齐-------------')
|
||||
else:
|
||||
if _is_api_request:
|
||||
@ -529,6 +548,19 @@ class VisualCallback:
|
||||
print('------------进入连续生产-------------')
|
||||
self._mould_before_aligned=True
|
||||
_is_api_request=True
|
||||
if self.state.pd_status==PD_StatusEnum.PD_Ready:
|
||||
self.state.pd_status=PD_StatusEnum.PD_TimeOut
|
||||
if self._cur_mould_model:
|
||||
self.state.current_mould=self._cur_mould_model
|
||||
self.state.current_block_number=CoreUtils.get_number_by_mould_code(self._cur_mould_model.MouldCode)
|
||||
self.state._db_mould_status={
|
||||
'mould_code':self._cur_mould_model.MouldCode,
|
||||
'status':2,
|
||||
'weight':0,
|
||||
}
|
||||
else:
|
||||
self.state.current_mould=None
|
||||
self.state.current_block_number=''
|
||||
|
||||
_current_weight=self.transmitter_controller.read_data(2)
|
||||
if not _current_weight:
|
||||
@ -545,6 +577,9 @@ class VisualCallback:
|
||||
self.init_val()
|
||||
self._init_lower_weight=_current_weight
|
||||
self.state._feed_status=FeedStatus.FCheckGB
|
||||
# if not self.state.current_block_number=='F':
|
||||
#这里需要判断状态是否发送完成,如果没发送完成,应该报警(时间太长),
|
||||
# self.state.pd_status=PD_StatusEnum.PD_Ready
|
||||
self.run_feed_all()
|
||||
|
||||
# else:
|
||||
@ -665,9 +700,9 @@ class VisualCallback:
|
||||
self._mould_need_weight=0.54*2416
|
||||
need_total_weight=self._mould_need_weight
|
||||
if initial_lower_weight>100:
|
||||
self.state._feed_status=FeedStatus.FFeed5
|
||||
self.state.vf_status=2
|
||||
if not self._is_finish:
|
||||
self.state._feed_status=FeedStatus.FFeed5
|
||||
self.state.vf_status=2
|
||||
self.is_start_visual=True
|
||||
self._is_feed_stage=5
|
||||
while not self._is_finish:
|
||||
@ -716,11 +751,13 @@ class VisualCallback:
|
||||
first_finish_weight=0
|
||||
self._mould_need_weight=1.91*2416
|
||||
need_total_weight=self._mould_need_weight
|
||||
if self._is_finish:
|
||||
return
|
||||
# start_time=None
|
||||
self.is_start_visual=True
|
||||
self.state._feed_status=FeedStatus.FFeed1
|
||||
self.state.vf_status=1
|
||||
if initial_lower_weight>100:
|
||||
self.is_start_visual=True
|
||||
#下料斗的料全部下完
|
||||
self._is_feed_stage=1
|
||||
while not self._is_finish:
|
||||
@ -745,6 +782,8 @@ class VisualCallback:
|
||||
self.close_lower_door_visual()
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
self.is_start_visual=False
|
||||
_current_lower_weight=loc_mitter.read_data(2)
|
||||
if _current_lower_weight is None:
|
||||
print("-------下料斗重量异常---------")
|
||||
@ -759,7 +798,8 @@ class VisualCallback:
|
||||
#print('------------上料斗未就位----------------')
|
||||
# print('------------上料斗未就位----------------')
|
||||
time.sleep(1)
|
||||
|
||||
if self._is_finish:
|
||||
return
|
||||
if self.plc_data in self.plc_valid_data:
|
||||
print(f'------------上料斗就位(上料斗往下料斗阶段)-------------')
|
||||
#打开上料斗出砼门,开5就,开三分之一下
|
||||
@ -801,6 +841,8 @@ class VisualCallback:
|
||||
else:
|
||||
loc_relay.control_upper_close_sync(6+loc_time_count)
|
||||
self.state._upper_door_closed=0
|
||||
if self._is_finish:
|
||||
return
|
||||
self.is_start_visual=True
|
||||
initial_lower_weight=loc_mitter.read_data(2)
|
||||
if initial_lower_weight is None:
|
||||
@ -828,14 +870,17 @@ class VisualCallback:
|
||||
break
|
||||
# print(f'------------已下料: {first_finish_weight+second_finish_weight}kg-------------')
|
||||
time.sleep(0.5)
|
||||
self.is_start_visual=False
|
||||
_current_lower_weight=loc_mitter.read_data(2)
|
||||
if _current_lower_weight is None:
|
||||
print("-------下料斗重量异常(第二次下到模)---------")
|
||||
return
|
||||
first_finish_weight=first_finish_weight+initial_lower_weight-_current_lower_weight
|
||||
|
||||
print(f'------------已下料(第二次): {first_finish_weight}kg-------------')
|
||||
print(f'------------已下料(第二次): {first_finish_weight}kg-------------')
|
||||
|
||||
if self._is_finish:
|
||||
return
|
||||
self._is_feed_stage=0
|
||||
if self.plc_data in self.plc_valid_data:
|
||||
self.state._upper_door_closed=2
|
||||
@ -1147,7 +1192,7 @@ class VisualCallback:
|
||||
if self._is_feed_stage==1 or self._is_feed_stage==3:
|
||||
#根据溢料状态动态调整目标角度
|
||||
if overflow_detected == "大堆料":
|
||||
if not self.state.mould_vibrate_status:
|
||||
if not self.state._mould_vibrate_status:
|
||||
TARGET_ANGLE = 15.0 # 临时控制变频器堆料时很小
|
||||
else:
|
||||
TARGET_ANGLE = 35.0 # 大堆料时控制在15度左右
|
||||
@ -1379,11 +1424,13 @@ class VisualCallback:
|
||||
# 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:
|
||||
if self.state._upper_door_position!=Upper_Door_Position.ZDS:
|
||||
self.state._upper_door_position=Upper_Door_Position.ZDS
|
||||
self.relay_controller.control(self.relay_controller.UPPER_TO_ZD, 'close')
|
||||
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
|
||||
self.relay_controller.control(self.relay_controller.UPPER_TO_JBL, 'close')
|
||||
else:
|
||||
if self.state._upper_door_position!=Upper_Door_Position.Returning:
|
||||
self.state._upper_door_position=Upper_Door_Position.Returning
|
||||
@ -1415,14 +1462,20 @@ class VisualCallback:
|
||||
|
||||
def get_current_mould(self):
|
||||
"""获取当前要浇筑的管片"""
|
||||
# if self.state.pd_status==PD_StatusEnum.PD_Ready:
|
||||
# self.state.pd_status=PD_StatusEnum.PD_TimeOut
|
||||
_not_poured=app_web_service.get_not_pour_artifacts()
|
||||
if _not_poured is not None and len(_not_poured)>=1:
|
||||
_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
|
||||
# self.state.current_mould=_cur_poured_model
|
||||
# self.state.current_block_number=CoreUtils.get_number_by_mould_code(_cur_poured_model.MouldCode)
|
||||
print(f'当前要浇筑的管片 {json.dumps(asdict(_cur_poured_model), ensure_ascii=False)}')
|
||||
else:
|
||||
self._cur_mould_model=None
|
||||
# self.state.current_mould=None
|
||||
# self.state.current_block_number=''
|
||||
print('当前没有未浇筑的管片')
|
||||
|
||||
def __del__(self):
|
||||
|
||||
@ -741,6 +741,8 @@ class VisualCallback:
|
||||
self.close_lower_door_visual()
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
self.is_start_visual=False
|
||||
_current_lower_weight=loc_mitter.read_data(2)
|
||||
if _current_lower_weight is None:
|
||||
print("-------下料斗重量异常---------")
|
||||
@ -1131,7 +1133,7 @@ class VisualCallback:
|
||||
if self._is_feed_stage==1 or self._is_feed_stage==3:
|
||||
#根据溢料状态动态调整目标角度
|
||||
if overflow_detected == "大堆料":
|
||||
if not self.state.mould_vibrate_status:
|
||||
if not self.state._mould_vibrate_status:
|
||||
TARGET_ANGLE = 15.0 # 临时控制变频器堆料时很小
|
||||
else:
|
||||
TARGET_ANGLE = 35.0 # 大堆料时控制在15度左右
|
||||
|
||||
1470
vision/visual_callback_dq_12.py
Normal file
1470
vision/visual_callback_dq_12.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user