123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- from utils.statepoint import *
- from utils.mqttdata import *
- from utils.s7data import *
- from models.data_sender import *
- import logging
- _Debug = 0
- class Counter:
- @property
- def data_mqtt(self):
- if self._data_mqtt.cli == None:
- raise ValueError('The MQTT connection to MES has not been initialized.')
- return self._data_mqtt
-
- @property
- def data_s7(self):
- if self._data_s7 == None:
- raise ValueError('The S7 connection to the casting machine PLC has not been initialized.')
- return self._data_s7
-
- @property
- def sender(self):
- if self._sender == None:
- raise ValueError('The sender has not been set.')
- return self._sender
-
- # 数据点定义
- def create_data_point(self, ccmNo):
- self.ladle_weight_1 = self.data_s7.make_point('大包重量1')
- self.ladle_weight_2 = self.data_s7.make_point('大包重量2')
- self.begin_pour = self.data_mqtt.make_point(f'{ccmNo}#开浇信号')
- self.end_pour = self.data_mqtt.make_point(f'{ccmNo}#停浇信号')
- if _Debug:
- self.begin_pour_ring = self.data_s7.make_point('浇铸信号[2]')
- self.begin_cutting = []
- for i in range(8):
- self.begin_cutting.append(self.data_s7.make_point(f'L{i+1}切割信号[0]'))
- self.end_cutting = []
- for i in range(8):
- self.end_cutting.append(self.data_s7.make_point(f'L{i+1}切割信号[1]'))
- self.length_cutting = []
- for i in range(8):
- self.length_cutting.append(self.data_s7.make_point(f'L{i+1}定尺'))
- self.drawing_speed = []
- for i in range(8):
- self.drawing_speed.append(self.data_s7.make_point(f'L{i+1}拉速'))
- def init_data_point(self):
- # 统一初始化数据点
- self.begin_pour.allow_update(False)
- self.begin_pour.set_state(False)
- self.end_pour.allow_update(False)
- self.end_pour.set_state(False)
- for i in range(8):
- self.begin_cutting[i].allow_update(False)
- self.begin_cutting[i].set_state(False)
- self.begin_cutting[i].data = 0
- self.end_cutting[i].allow_update(False)
- self.end_cutting[i].set_state(False)
- self.end_cutting[i].data = 0
- # 数据点逻辑设置
- self.begin_pour.set_excite_action(self.begin_pour_action)
- self.end_pour.set_excite_action(self.end_pour_action)
- for i in range(8):
- self.begin_cutting[i].set_excite_action(lambda i=i: self.begin_cutting_action(i))
- self.begin_cutting[i].set_keep_time(3000)
- self.end_cutting[i].set_excite_action(lambda i=i: self.end_cutting_action(i))
- self.end_cutting[i].set_reset_action(lambda i=i: self.end_cutting[i].allow_update(False))
- # 统一开启数据点
- self.begin_pour.allow_update()
- self.end_pour.allow_update()
- for i in range(8):
- self.begin_cutting[i].allow_update()
- def __init__(self, data_mqtt: Mqttdata, data_s7: S7data, ccmNo, logger: logging.Logger, sender: Sender):
- # 模块入口、出口、日志定义
- self._data_mqtt = data_mqtt
- self._data_s7 = data_s7
- self._sender = sender
- self.logger = logger
- self.logger.info(f"[Counter]分炉分坯模块:{ccmNo}号机模块启动")
- # 配置必须的数据点
- self.create_data_point(ccmNo)
- self.init_data_point()
- #分炉分坯功能
- self.last_cutting_timestamp = 0
- self.strand = [0, 0, 0, 0, 0, 0, 0, 0]
- self.cutting_state_heat = [{}, {}, {}, {}, {}, {}, {}, {}]
- self.cutting_state_heat_index = [0, 0, 0, 0, 0, 0, 0, 0]
- self.total = 0
- self.limit_count = 0
- self.limit_target = 24
- self.limit_flag = False
- self.lock = threading.Lock()
- self.old_heat = {}
- self.new_heat = {}
- self.last_cutting_strand = 0
- def begin_pour_action(self):
- # 标志是否为铸机开机的第一次开浇
- flag = (time.time() - self.last_cutting_timestamp) > 1800
- # 大包重量选择算法
- ladle_weight = max(self.ladle_weight_1.data, self.ladle_weight_2.data)
- # 写入日志
- if flag:
- self.logger.info(f'[Counter]首次开浇:{self.begin_pour.data['heatNo']}')
- elif self.new_heat == {}:
- self.logger.info(f'[Counter]炉次开浇:{self.begin_pour.data['heatNo']},当前出坯炉次:{"未知" if self.old_heat == {} else self.old_heat["heatNo"]}')
- else:
- self.logger.warning(f'[Counter]炉次开浇:{self.begin_pour.data['heatNo']},炉次{self.new_heat['heatNo']}被覆盖')
- # 维护换炉操作
- if flag:
- self.old_heat = self.begin_pour.data
- self.new_heat = {}
- else:
- self.new_heat = self.begin_pour.data
- self.start_limit()
- # 使用sender向外发送信号
- self.sender.begin_pour(self.begin_pour.data, ladle_weight)
- def end_pour_action(self):
- # 写入日志
- self.logger.info(f'[Counter]炉次停浇:{self.end_pour.data["heatNo"]}')
- # 使用sender向外发送信号
- if self.old_heat:
- self.sender.end_pour(self.end_pour.data)
- def cutting_data_comple(self, i):
- count = 3
- # 补充计入模块
- while count:
- time.sleep(0.5)
- sizing = self.length_cutting[i].data
- speed = self.drawing_speed[i].data
- if 0 < sizing < 30000 and 0 < speed < 10:
- self.strand_add(i+1, sizing, speed)
- return None
- count -= 1
-
- time.sleep(0.5)
- sizing = self.length_cutting[i].data
- speed = self.drawing_speed[i].data
- if not (0 < sizing < 30000 and 0 < speed < 10):
- self.logger.warning("[Counter]请注意,定尺/拉速数据持续异常,已按照异常数据发送")
- self.strand_add(i+1, sizing, speed)
- self.end_cutting[i].allow_update()
- def begin_cutting_action(self, i):
- # 写入日志
- self.logger.info(f'[Counter]{i+1}流:开始切割')
- # 计入模块
- sizing = self.length_cutting[i].data
- speed = self.drawing_speed[i].data
- if 0 < sizing < 30000 and 0 < speed < 10:
- self.strand_add(i+1, sizing, speed)
- self.end_cutting[i].allow_update()
- else:
- threading.Thread(target=self.cutting_data_comple, args=(i,)).start()
- def end_cutting_action(self, i):
- # 写入日志
- self.logger.info(f'[Counter]{i+1}流:完成切割')
- # 复位流状态
- cutting_state_heat = self.cutting_state_heat[i]
- cutting_state_heat_index = self.cutting_state_heat_index[i]
- self.cutting_state_heat[i] = {}
- self.cutting_state_heat_index[i] = 0
- if cutting_state_heat:
- # 使用sender向外发送信号
- self.sender.end_cut(cutting_state_heat, cutting_state_heat_index)
- # 检查自己是否是本炉最后一根钢坯
- if self.last_cutting_strand == i+1:
- self.last_cutting_strand = 0
- self.sender.heat_last(cutting_state_heat)
- def strand_add(self, sno, sizing, speed):
- with self.lock:
- # 维护辅助时间戳
- self.last_cutting_timestamp = time.time()
- #维护内部正确性的主要算法
- if self.limit_flag:
- self.limit_count += 1
- if self.limit_count == self.limit_target:
- # 此处已经在切割本炉最后一根
- if self.old_heat:
- self.last_cutting_strand = sno
- if self.limit_count > self.limit_target:
- # 此处已经在切割新炉第一根
- self.change_heat()
- self.total = 0
- self.strand = [0, 0, 0, 0, 0, 0, 0, 0]
- self.limit_count = 0
- self.limit_flag = False
- self.strand[sno-1] += 1
- self.total += 1
- heatData = self.old_heat
- heatIndex = self.total
- strandIndex = self.strand[sno-1]
-
- if heatData:
- # 记录当前流状态,帮助停切信号判断钢坯信息
- self.cutting_state_heat[sno-1] = heatData
- self.cutting_state_heat_index[sno-1] = heatIndex
- # 生成坯号,使用sender向外发送信号
- ccmNo = heatData['ccmNo']
- billetNo = heatData['heatNo'] + ccmNo + str(sno) + '{:0>2}'.format(strandIndex)
- self.logger.info(f"[Counter]{sno}流:{heatData['heatNo']}炉第{heatIndex}根计入系统,坯号:{billetNo}")
- self.sender.begin_cut(heatData, billetNo, heatIndex, sizing, speed)
- # 使用sender发送炉次首次开切信号
- if heatIndex == 1:
- self.sender.heat_first(heatData)
- else:
- self.logger.info(f"[Counter]{sno}流:未知炉第{heatIndex}根,本炉无法计入系统,下一炉开始正常")
- def start_limit(self):
- # 根据实际情况设置本炉总支数
- with self.lock:
- if self.old_heat:
- if self.total % 4 == 1:
- self.limit_target = 23
- elif self.total % 4 == 2:
- if self.total >= 28:
- self.limit_target = 22
- else:
- self.limit_target = 26
- elif self.total % 4 == 3:
- self.limit_target = 25
- else:
- self.limit_target = 24
- else:
- self.limit_target = 24
-
- self.limit_flag = True
- def change_heat(self):
- # 异常情况
- if not self.new_heat:
- self.logger.error('[Counter]换炉:失败,无新炉次信息')
- return None
-
- # 写入日志
- if self.old_heat:
- self.logger.info(f'[Counter]换炉:{self.old_heat["heatNo"]}->{self.new_heat["heatNo"]}')
- else:
- self.logger.info(f'[Counter]换炉:未知炉次->{self.new_heat["heatNo"]}')
- #真正换炉过程
- self.old_heat = self.new_heat
- self.new_heat = {}
|