billet_trace_pusher.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. import logging
  2. from utils.statepoint import *
  3. from utils.s7data import *
  4. from models.data_sender import *
  5. class Trace_pusher:
  6. def __init__(self, data_s7: S7data, logger: logging.Logger, sender: Sender, strand_position: list, manual_change_heat_sig: Statepoint, hostsend_flag=False, hostmove_flag=False):
  7. self.data_s7 = data_s7
  8. self.logger = logger
  9. self.sender = sender
  10. self.manual_change_heat_sig = manual_change_heat_sig
  11. self.strands_cutting = [[], [], [], [], [], [], [], []]
  12. self.strands_buffer = [[], [], [], [], [], [], [], []]
  13. self.locks = [threading.Lock() for i in range(8)]
  14. self.strand_position = strand_position
  15. self.hostsend_flag = hostsend_flag
  16. self.hostmove_flag = hostmove_flag
  17. self.heat_filter_index = 1
  18. self.heat_filter = ["00000000"] + ['' for i in range(9)]
  19. self.old_heatNo = "00000000"
  20. self.current_heatNo = "00000000"
  21. self.old_heatData = {}
  22. self.current_heatData = {}
  23. self.total = 0
  24. self.strand = [0, 0, 0, 0, 0, 0, 0, 0]
  25. self.count_lock = threading.Lock()
  26. self.sizing_count_heatNo = "00000000"
  27. self.sizing_count = {}
  28. self.pusher_left_list = []
  29. self.pusher_right_list = []
  30. self.bed_left = [[], [], []]
  31. self.bed_right = [[], [], []]
  32. self.pusher_left = data_s7.make_point('推钢机激光')
  33. self.pusher_right = data_s7.make_point('推钢机激光')
  34. self.pusher_left.hmd_add(0)
  35. self.pusher_right.hmd_add(0)
  36. self.manual_change_heat_sig.set_excite_action(self.manual_change_heat)
  37. self.pusher_left.set_convertor(lambda data: data < min(self.strand_position))
  38. self.pusher_right.set_convertor(lambda data: data > max(self.strand_position))
  39. self.pusher_left.set_excite_action(lambda: self.arrive_cooling_bed('left'))
  40. self.pusher_right.set_excite_action(lambda: self.arrive_cooling_bed('right'))
  41. self.billet_out = [[], [], [], [], [], [], [], []]
  42. self.length_cutting = []
  43. for i in range(8):
  44. self.length_cutting.append(self.data_s7.make_point(f'L{i+1}定尺'))
  45. self.drawing_speed = []
  46. for i in range(8):
  47. self.drawing_speed.append(self.data_s7.make_point(f'L{i+1}拉速'))
  48. # 5号机 + 6号机
  49. # 拉速积分得到长度
  50. self.integration_total = [0] * 8
  51. self.integration_lock = threading.Lock()
  52. self.billet_out_sig = [self.data_s7.make_point(f"L{i+1}拉速", Integration_speed_mpmin) for i in range(8)]
  53. self.speed_to_zero_sig = [self.data_s7.make_point(f"L{i+1}拉速") for i in range(8)]
  54. self.billet_position = [
  55. self.data_s7.make_point('L1切割信号[1]'),
  56. self.data_s7.make_point('L2切割信号[1]'),
  57. self.data_s7.make_point('L3切割信号[1]'),
  58. self.data_s7.make_point('L4切割信号[1]'),
  59. self.data_s7.make_point('L5切割信号[1]'),
  60. self.data_s7.make_point('L6切割信号[1]'),
  61. self.data_s7.make_point('L7切割信号[1]'),
  62. self.data_s7.make_point('L8切割信号[1]')
  63. ]
  64. self.barrier = [
  65. self.data_s7.make_point('L1挡板'),
  66. self.data_s7.make_point('L2挡板'),
  67. self.data_s7.make_point('L3挡板'),
  68. self.data_s7.make_point('L4挡板'),
  69. self.data_s7.make_point('L5挡板'),
  70. self.data_s7.make_point('L6挡板'),
  71. self.data_s7.make_point('L7挡板'),
  72. self.data_s7.make_point('L8挡板')
  73. ]
  74. self.barrier_checker = [False, False, False, False, False, False, False, False]
  75. if self.hostsend_flag:
  76. self.hostsend_barrier = [
  77. self.data_s7.make_point('热送挡板[0]'),
  78. self.data_s7.make_point('热送挡板[1]'),
  79. self.data_s7.make_point('热送挡板[2]'),
  80. self.data_s7.make_point('热送挡板[3]'),
  81. self.data_s7.make_point('热送挡板[4]'),
  82. self.data_s7.make_point('热送挡板[5]'),
  83. self.data_s7.make_point('热送挡板[6]'),
  84. self.data_s7.make_point('热送挡板[7]')
  85. ]
  86. if self.hostmove_flag:
  87. self.hostmove_sig = data_s7.make_point('热送辊道状态[1]')
  88. self.hostmove_sig.set_excite_action(lambda: self.billet_to_stack("步进冷床", self.get_billet('right')))
  89. for i in range(8):
  90. self.billet_position[i].allow_update(False)
  91. self.billet_position[i].set_state(False)
  92. self.billet_out_sig[i].allow_update(False)
  93. self.billet_out_sig[i].set_state(False)
  94. # 6号机 + 5号机
  95. # 拉速补充钢坯
  96. self.billet_out_sig[i].set_convertor(lambda data, i=i: round(data*1000) >= self.integration_total[i] + self.length_cutting[i].data)
  97. self.billet_out_sig[i].set_excite_action(lambda i=i: self.billet_out_action(i))
  98. # 拉速为0时表示此时拉速补偿已经不可用
  99. self.speed_to_zero_sig[i].set_convertor(lambda data: data == 0)
  100. self.speed_to_zero_sig[i].set_excite_action(lambda i=i: self.billet_out_sig[i].allow_update(False))
  101. # 正常情况进入跟踪
  102. self.billet_position[i].set_excite_action(lambda i=i: self.billet_in_buffer_action(i))
  103. self.billet_position[i].allow_update()
  104. # 挡板抬起
  105. self.barrier[i].allow_update(False)
  106. self.barrier[i].set_state(False)
  107. self.barrier[i].set_convertor(lambda data: not bool(data))
  108. self.barrier[i].set_excite_action(lambda i=i: self.barrier_up_action(i))
  109. self.barrier[i].set_reset_action(lambda i=i: self.barrier_down_action(i))
  110. self.barrier[i].allow_update()
  111. # 5号机直轧挡板抬起
  112. if self.hostsend_flag:
  113. self.hostsend_barrier[i].allow_update(False)
  114. self.hostsend_barrier[i].set_state(False)
  115. self.hostsend_barrier[i].set_convertor(lambda data: not bool(data))
  116. self.hostsend_barrier[i].set_excite_action(lambda i=i: self.hostsend_barrier_up_action(i))
  117. self.hostsend_barrier[i].set_reset_action(lambda i=i: self.logger.debug(f"{i+1}流热送挡板关闭"))
  118. self.hostsend_barrier[i].allow_update()
  119. def integration_start(self, i):
  120. if self.speed_to_zero_sig[i].data == 0:
  121. return None
  122. self.billet_out_sig[i].data = 0
  123. self.billet_out_sig[i].state = False
  124. self.integration_total[i] = 0
  125. self.billet_out_sig[i].allow_update()
  126. def billet_out_action(self, i):
  127. billetNo = self.current_heatNo + '0' + str(i+1) + '99'
  128. sizing = self.length_cutting[i].data
  129. speed = self.drawing_speed[i].data
  130. if self.strands_cutting[i]:
  131. return None
  132. # [坯号, 炉次信息, 定尺, 拉速, 开切时间, 停切时间]
  133. self.billet_out[i] = [billetNo, self.current_heatData, sizing, speed, time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), '']
  134. # 等待20s,若无开切信号,即为漏坯
  135. start_time = time.time()
  136. while time.time() - start_time < 20:
  137. if self.strands_cutting[i]:
  138. return None
  139. time.sleep(0.5)
  140. # 漏钢时,积分数据不会被清零,需要累加
  141. with self.integration_lock:
  142. self.integration_total[i] += sizing
  143. self.billet_in_buffer_action(i)
  144. def billet_in_buffer_action(self, i):
  145. with self.locks[i]:
  146. if self.strands_cutting[i]:
  147. time.sleep(15)
  148. self.logger.info(f"[TRACE]{i+1}流新增钢坯存储")
  149. self.strands_buffer[i] = self.strands_cutting[i]
  150. self.strands_cutting[i] = []
  151. elif self.billet_out[i]:
  152. self.logger.info(f"[TRACE]{i+1}流无已经开切的钢坯,算法补入")
  153. self.strands_buffer[i] = self.billet_out[i]
  154. else:
  155. self.logger.info(f"[TRACE]{i+1}流无已经开切的钢坯,但未检测到可补入的钢坯信息")
  156. self.billet_out[i] = []
  157. if self.barrier[i].state and self.barrier_checker[i] == False:
  158. self.barrier_up_action(i)
  159. def barrier_up_action(self, i):
  160. time.sleep(1)
  161. with self.locks[i]:
  162. if self.strands_buffer[i]:
  163. self.barrier_checker[i] = True
  164. time.sleep(10)
  165. billetData = self.strands_buffer[i]
  166. self.strands_buffer[i] = []
  167. if self.strand_position[i] <= self.pusher_left.data:
  168. self.logger.info(f"[TRACE]{i+1}流钢坯通过挡板进入推钢区域,在推钢机北侧")
  169. self.pusher_left_list.append(billetData)
  170. else:
  171. self.logger.info(f"[TRACE]{i+1}流钢坯通过挡板进入推钢区域,在推钢机南侧")
  172. self.pusher_right_list.append(billetData)
  173. if self.hostsend_flag and self.hostsend_barrier[i].state:
  174. self.hostsend_barrier_up_action(i)
  175. def barrier_down_action(self, i):
  176. if self.barrier_checker[i]:
  177. self.logger.debug(f"[TRACE]{i+1}流挡板关闭")
  178. else:
  179. self.logger.error(f"[TRACE]{i+1}流挡板关闭,期间无钢坯流出")
  180. self.barrier_checker[i] = False
  181. def hostsend_barrier_up_action(self, i):
  182. with self.count_lock:
  183. gp_tmp = []
  184. if self.strand_position[i] <= self.pusher_left.data:
  185. index = -1
  186. for j in range(len(self.pusher_left_list)-1, -1, -1):
  187. if self.pusher_left_list[j][0][-3] == str(i+1):
  188. index = j
  189. break
  190. if index == -1:
  191. pass
  192. #self.logger.warning(f"[TRACE]推钢机北侧未找到{i+1}流的热送钢坯")
  193. else:
  194. gp_tmp = self.pusher_left_list[index]
  195. self.pusher_left_list = self.pusher_left_list[:index] + self.pusher_left_list[index+1:]
  196. else:
  197. index = -1
  198. for j in range(len(self.pusher_right_list)-1, -1, -1):
  199. if self.pusher_right_list[j][0][-3] == str(i+1):
  200. index = j
  201. break
  202. if index == -1:
  203. pass
  204. #self.logger.warning(f"[TRACE]推钢机南侧未找到{i+1}流的热送钢坯")
  205. else:
  206. gp_tmp = self.pusher_right_list[index]
  207. self.pusher_right_list = self.pusher_right_list[:index] + self.pusher_right_list[index+1:]
  208. if gp_tmp:
  209. # self.logger.info(f"[TRACE]{i+1}流钢坯热送")
  210. if not gp_tmp[0].startswith('0') and gp_tmp[0][:8] not in self.heat_filter:
  211. self.change_heat(gp_tmp)
  212. if not self.current_heatNo.startswith('0'):
  213. self.hostsend(gp_tmp)
  214. def change_heat(self, data):
  215. # 换炉代码
  216. self.old_heatNo = self.current_heatNo
  217. self.old_heatData = self.current_heatData
  218. self.current_heatNo = data[0][:8]
  219. self.current_heatData = data[1]
  220. self.heat_filter[self.heat_filter_index] = self.current_heatNo
  221. self.heat_filter_index = (self.heat_filter_index + 1) % 10
  222. # 上一炉终止信号在这里发
  223. self.total = 0
  224. self.strand = [0, 0, 0, 0, 0, 0, 0, 0]
  225. def manual_change_heat(self):
  226. with self.count_lock:
  227. tmp = self.manual_change_heat_sig.data
  228. if tmp['heatNo'] == self.current_heatNo:
  229. return None
  230. self.logger.info(f"手动换炉触发:{self.current_heatNo}->{tmp['heatNo']}")
  231. tmp['sendTime'] = tmp['startPourTime']
  232. self.sender.begin_pour(tmp, max(self.data_s7.get_value('大包重量1'), self.data_s7.get_value('大包重量2')))
  233. self.change_heat([tmp['heatNo'], tmp])
  234. def arrive_cooling_bed(self, direc):
  235. with self.count_lock:
  236. if direc == 'left':
  237. self.logger.debug(f"北侧冷床上推入{len(self.pusher_left_list)}根钢坯")
  238. tmp = self.pusher_left_list
  239. self.pusher_left_list = []
  240. elif direc == 'right':
  241. self.logger.debug(f"南侧冷床上推入{len(self.pusher_right_list)}根钢坯")
  242. tmp = self.pusher_right_list
  243. self.pusher_right_list = []
  244. for i in tmp:
  245. if not i[0].startswith('0') and i[0][:8] not in self.heat_filter:
  246. self.change_heat(i)
  247. break
  248. self.billet_to_bed(tmp, direc)
  249. def billet_to_bed(self, billets, direc):
  250. if direc == 'left':
  251. if len(billets):
  252. self.billet_to_bed_impl(billets, self.bed_left)
  253. self.logger.debug(f"北侧冷床目前情况:{len(self.bed_left[0])}根|{len(self.bed_left[1])}根|{len(self.bed_left[2])}根")
  254. elif direc == 'right':
  255. if len(billets):
  256. self.billet_to_bed_impl(billets, self.bed_right)
  257. self.logger.debug(f"南侧冷床目前情况:{len(self.bed_right[2])}根|{len(self.bed_right[1])}根|{len(self.bed_right[0])}根")
  258. def billet_to_bed_impl(self, billets: list, dst: list):
  259. i = 0
  260. count = 0
  261. while i < len(dst) and dst[i]:
  262. count += 1
  263. i += 1
  264. if count == 0:
  265. dst[0].extend(billets)
  266. elif count == 3 and (len(billets) >= 4 or len(dst[count-1]) >= 4 or len(dst[count-1]) + len(billets) > 4):
  267. self.logger.error(f"组坯异常!")
  268. # test 把无法组坯的不正常钢坯 直接上传服务器
  269. tmp = dst[0]
  270. if len(tmp) < 4:
  271. self.billet_union(tmp)
  272. dst.remove(dst[0])
  273. dst.append(billets)
  274. count -= 1
  275. elif len(billets) >= 4 or len(dst[count-1]) >= 4 or len(dst[count-1]) + len(billets) > 4:
  276. dst[count].extend(billets)
  277. else:
  278. dst[count-1].extend(billets)
  279. count -= 1
  280. if len(dst[count]) >= 4:
  281. self.billet_union(dst[count])
  282. def billet_union(self, billets):
  283. ccmNo = self.current_heatData['ccmNo'] if self.current_heatData else '0'
  284. if len(billets) < 4:
  285. for i in billets:
  286. strandNo = i[0][9]
  287. billetNo = self.current_heatNo + ccmNo + strandNo + '{:0>2}'.format(99)
  288. i[0] = billetNo
  289. i[1] = self.current_heatData
  290. if self.current_heatData:
  291. self.sender.billet_upload(self.current_heatData, billetNo, self.total, i[2], i[3], i[4], i[5], '', error=True)
  292. self.logger.warning(f"{self.current_heatNo}炉发送异常钢坯{len(billets)}根")
  293. return None
  294. if self.sizing_count_heatNo != self.current_heatNo:
  295. self.sizing_count_heatNo = self.current_heatNo
  296. self.sizing_count = {}
  297. # 组坯时,定尺为此组内定尺最小值
  298. sizing = billets[0][2]
  299. for i in billets:
  300. if i[2] < sizing:
  301. sizing = i[2]
  302. if sizing not in self.sizing_count:
  303. self.sizing_count[sizing] = 0
  304. self.sizing_count[sizing] += 1
  305. billet_unionNo = self.current_heatNo + '{:0>5}'.format(int(sizing)) + '{:0>2}'.format(self.sizing_count[sizing])
  306. billetsNo = []
  307. for i in billets:
  308. strandNo = i[0][9]
  309. self.total += 1
  310. self.strand[int(strandNo)-1] += 1
  311. billetNo = self.current_heatNo + ccmNo + strandNo + '{:0>2}'.format(self.strand[int(strandNo)-1])
  312. billetsNo.append(billetNo)
  313. i[0] = billetNo
  314. i[1] = self.current_heatData
  315. i[2] = sizing
  316. if self.current_heatData:
  317. self.sender.billet_upload(self.current_heatData, billetNo, self.total, i[2], i[3], i[4], i[5], billet_unionNo)
  318. if self.current_heatData:
  319. self.sender.billet_union(self.current_heatData, billet_unionNo, billetsNo, sizing)
  320. self.logger.info(f"{self.current_heatNo}炉组号{billet_unionNo}钢坯{len(billets)}根:\n {'、'.join(billetsNo)}")
  321. def data_from_casting(self, i, data, extend=False):
  322. with self.locks[i]:
  323. if extend:
  324. if self.strands_cutting[i] and self.strands_cutting[i][0] == data[0]:
  325. self.logger.info(f"{i+1}流补充了钢坯停切时间")
  326. self.strands_cutting[i] = data
  327. elif self.strands_buffer[i] and self.strands_buffer[i][0] == data[0]:
  328. self.logger.info(f"{i+1}流补充了钢坯停切时间")
  329. self.strands_buffer[i] = data
  330. else:
  331. self.logger.warning(f"{i+1}流对已经离开的钢坯补充停切时间,无效")
  332. else:
  333. with self.integration_lock:
  334. # 验证现在拉速积分是否有效
  335. if self.billet_out_sig[i].permitted_update:
  336. # 有效:检测并把所有短尺的钢坯定尺都改为定尺软件给的
  337. if round(self.billet_out_sig[i].data * 1000) - self.integration_total[i] < 10000:
  338. data[2] = self.data_s7.get_value(f"L{i+1}短尺")
  339. # 对拉速积分进行切割信号校准
  340. self.billet_out_sig[i].data = 0
  341. self.integration_total[i] = 0
  342. else:
  343. # 无效:开启拉速积分
  344. self.integration_start(i)
  345. if self.strands_cutting[i]:
  346. self.logger.warning(f"{i+1}流有钢坯开切冲突")
  347. self.strands_cutting[i] = data
  348. else:
  349. self.logger.info(f"{i+1}流钢坯开切")
  350. self.strands_cutting[i] = data
  351. def hostsend(self, i):
  352. ccmNo = self.current_heatData['ccmNo']
  353. strandNo = i[0][9]
  354. self.total += 1
  355. self.strand[int(strandNo)-1] += 1
  356. billetNo = self.current_heatNo + ccmNo + strandNo + '{:0>2}'.format(self.strand[int(strandNo)-1])
  357. i[0] = billetNo
  358. i[1] = self.current_heatData
  359. self.sender.billet_upload(self.current_heatData, billetNo, self.total, i[2], i[3], i[4], i[5], '')
  360. self.logger.info(f"{self.current_heatNo}炉{strandNo}流钢坯热送:{billetNo}")
  361. self.sender.host_send(ccmNo, billetNo, "棒一")
  362. def show_coolbed(self):
  363. print(f"北侧冷床目前情况:{len(self.bed_left[0])}根|{len(self.bed_left[1])}根|{len(self.bed_left[2])}根")
  364. print(f"南侧冷床目前情况:{len(self.bed_right[2])}根|{len(self.bed_right[1])}根|{len(self.bed_right[0])}根")
  365. def clean_status(self):
  366. self.logger.debug(f"[TRACE]小冷床状态清空")
  367. with self.count_lock:
  368. self.pusher_left_list = []
  369. self.pusher_right_list = []
  370. self.bed_left = [[], [], []]
  371. self.bed_right = [[], [], []]
  372. def init_coolbed(self):
  373. left_count = input(f"输入北侧冷床钢坯根数序列(由外到内):")
  374. right_count = input(f"输入南侧冷床钢坯根数序列(由外到内):")
  375. if not (left_count.isdigit() and len(left_count) == 3 and right_count.isdigit() and len(right_count) == 3):
  376. warnings.warn("冷床格式化错误,请输入正确格式")
  377. return None
  378. left_count = [int(i) for i in left_count]
  379. right_count = [int(i) for i in right_count]
  380. temp = ['000000000000', {}, 0, 0, '', '']
  381. with self.count_lock:
  382. self.bed_left = [[temp.copy() for i in range(left_count[j])] for j in range(3)]
  383. self.bed_right = [[temp.copy() for i in range(right_count[j])] for j in range(3)]
  384. def get_billet(self, direc):
  385. with self.count_lock:
  386. if direc == "left":
  387. return self.get_billet_action(self.bed_left)
  388. if direc == "right":
  389. return self.get_billet_action(self.bed_right)
  390. def get_billet_action(self, src: list):
  391. for i in range(len(src)-1, -1, -1):
  392. if len(src[i]) >= 4:
  393. tmp = [j[0] for j in src.pop(i)]
  394. src.append([])
  395. if tmp[0].startswith('0'):
  396. return []
  397. return tmp
  398. return []
  399. def billet_to_stack(self, stackNo, billets):
  400. if billets:
  401. self.logger.info(f"有钢坯放入{stackNo}堆垛")
  402. self.sender.stack_add('6', billets, "", time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), "6#小冷床(南)", '步进冷床堆垛')
  403. # [坯号, 炉次信息, 定尺, 拉速, 开切时间, 停切时间]