from typing import Literal
from Data_Code import OutputJsonTemplate
from .wafer import Wafer, ScheduleHistory
from .transferModule import Task
from .transferModuleDouble import move_cost_cal
from CONFIG import LOAD_COLOR, UNLOAD_COLOR, CONVERTING_COLOR, OPEN_COLOR, CLOSE_COLOR, PROCESS_COLOR
class LoadLockDouble: # 已完成receive_cmd
def __init__(self, deviceID: str):
self.output_json = []
self.deviceID = deviceID # 设备编号,如LLA,LLB
self.mode = 0 # 0: 大气, 1: 真空
self.wafer_S1 = None
self.wafer_S2 = None
# 状态转换单独一个变量记录
self.is_s1_busy = False
self.is_s2_busy = False
self.is_converting = False
# 时间变量
self.convert_start_time = 0 # 开始转换的时间
self.convert_end_time = 0 # 结束转换的时间
self.convert2vacuum_cost = 15 # 大气转换到真空时间
self.convert2air_cost = 20 # 真空转换到大气时间
self.change_valve_cost = 1 # 改变阀门状态花费时间
self.is_valve_s1_open = False # 阀门s1是否开启
self.is_valve_s2_open = False # 阀门s2是否开启
self.s1_start_time = 0 # s1开始忙碌改变的时间
self.s1_end_time = 0 # s1结束的时间
self.task_queue = Task() # 任务队列
self.s2_start_time = 0 # s2
self.s2_end_time = 0 # s2
self.schedule_hist = []
def receive_cmd(self, cmd: Task):
self.task_queue = cmd
def get_device(self) -> str:
return self.deviceID
def is_contain_wafer(self, channel:Literal[0, 1]) -> bool:
if channel == 0 :
return self.wafer_S1 is not None
elif channel == 1 :
return self.wafer_S2 is not None
raise ValueError('channel must be 0 or 1')
def load_wafer_S1(self, wafer: Wafer, time: float, load_cost: float=4):
self.wafer_S1 = wafer
self.is_s1_busy = True
self.s1_start_time = time
self.s1_end_time = time + load_cost
self.schedule_hist.append(ScheduleHistory(self.deviceID, self.s1_start_time, self.s1_end_time, LOAD_COLOR, f"S1入晶圆{wafer.id + 1}"))
def unload_wafer_S1(self, time: float, unload_cost: float=4) -> Wafer:
return_wafer = self.wafer_S1
self.wafer_S1 = None
self.is_s1_busy = True
self.s1_start_time = time
self.s1_end_time = time + unload_cost
self.schedule_hist.append(ScheduleHistory(self.deviceID, self.s1_start_time, self.s1_end_time, UNLOAD_COLOR, f"S1出晶圆{return_wafer.id + 1}"))
return return_wafer
def load_wafer_S2(self, wafer: Wafer, time:float,unload_cost:float=4):
self.wafer_S2 = wafer
self.is_s2_busy = True
self.s2_start_time = time
self.s2_end_time = time + unload_cost
self.schedule_hist.append(ScheduleHistory(self.deviceID, self.s2_start_time, self.s2_end_time, LOAD_COLOR, f"S2入晶圆{wafer.id + 1}"))
def unload_wafer_S2(self, time: float, unload_cost: float=4) -> Wafer:
return_wafer = self.wafer_S2
self.wafer_S2 = None
self.is_s2_busy = True
self.s2_start_time = time
self.s2_end_time = time + unload_cost
self.schedule_hist.append(ScheduleHistory(self.deviceID, self.s2_start_time, self.s2_end_time, UNLOAD_COLOR, f"S2出晶圆{return_wafer.id + 1}"))
return return_wafer
def convert_mode(self, time: float):
"""
转换模式,大气或真空
"""
self.is_converting = True
self.convert_start_time = time
if self.mode == 0:
self.convert_end_time = time + self.convert2vacuum_cost
self.mode = 1
else:
self.convert_end_time = time + self.convert2air_cost
self.mode = 0
mode = '真空' if self.mode == 1 else '大气'
convert_id = 6 if self.mode == 1 else 7
self.schedule_hist.append(
ScheduleHistory(self.deviceID, self.convert_start_time, self.convert_end_time, CONVERTING_COLOR, f"转换模式到{mode}"))
self.output_json.append(
OutputJsonTemplate(StartTime=self.convert_start_time, EndTime=self.convert_end_time, MoveID=len(self.output_json),
MoveType=convert_id,
ModuleName=self.deviceID, MatID='', srcSlotID=1,
destSlotID=1,
srcStation='', destStation=''))
def open_valve_s1(self, time: float):
"""
s1打开阀门
"""
if self.wafer_S1 is not None:
self.wafer_S1.stay_time = 0
self.is_s1_busy = True
self.is_valve_s1_open = True
self.s1_start_time = time
self.s1_end_time = time + self.change_valve_cost
self.schedule_hist.append(ScheduleHistory(self.deviceID, self.s1_start_time, self.s1_end_time, OPEN_COLOR, "S1打开阀门"))
# self.valve_is_changing = True
self.output_json.append(
OutputJsonTemplate(StartTime=self.s1_start_time, EndTime=self.s1_end_time, MoveID=len(self.output_json),
MoveType=4,
ModuleName=self.deviceID, MatID='', srcSlotID=1,
destSlotID=1,
srcStation='', destStation=''))
def close_valve_s1(self, time: float):
"""
s1关闭阀门
"""
self.is_s1_busy = True
self.is_valve_s1_open = False
self.s1_start_time = time
self.s1_end_time = time + self.change_valve_cost
self.schedule_hist.append(ScheduleHistory(self.deviceID, self.s1_start_time, self.s1_end_time, CLOSE_COLOR, "S1关闭阀门"))
# self.valve_is_changing = True
self.output_json.append(
OutputJsonTemplate(StartTime=self.s1_start_time, EndTime=self.s1_end_time, MoveID=len(self.output_json),
MoveType=5,
ModuleName=self.deviceID, MatID='', srcSlotID=1,
destSlotID=1,
srcStation='', destStation=''))
def open_valve_s2(self, time: float):
"""
s2打开阀门
"""
if self.wafer_S2 is not None:
self.wafer_S2.stay_time = 0
self.is_s2_busy = True
self.is_valve_s2_open = True
self.s2_start_time = time
self.s2_end_time = time + self.change_valve_cost
self.schedule_hist.append(ScheduleHistory(self.deviceID, self.s2_start_time, self.s2_end_time, OPEN_COLOR, "S2打开阀门"))
# self.valve_is_changing = True
self.output_json.append(
OutputJsonTemplate(StartTime=self.s2_start_time, EndTime=self.s2_end_time, MoveID=len(self.output_json),
MoveType=4,
ModuleName=self.deviceID, MatID='', srcSlotID=2,
destSlotID=2,
srcStation='', destStation=''))
def close_valve_s2(self, time: float):
"""
s2关闭阀门
"""
self.is_s2_busy = True
self.is_valve_s2_open = False
self.s2_start_time = time
self.s2_end_time = time + self.change_valve_cost
self.schedule_hist.append(ScheduleHistory(self.deviceID, self.s2_start_time, self.s2_end_time, CLOSE_COLOR, "S2关闭阀门"))
# self.valve_is_changing = True
self.output_json.append(
OutputJsonTemplate(StartTime=self.s2_start_time, EndTime=self.s2_end_time, MoveID=len(self.output_json),
MoveType=5,
ModuleName=self.deviceID, MatID='', srcSlotID=2,
destSlotID=2,
srcStation='', destStation=''))
def is_hectic(self, time: float, *args) -> bool:
if len(args) != 0:
if args[0] == 0:
return self.is_s1_busy or (len(self.task_queue) > 0) or (time < self.task_queue.end_time) or self.is_converting
elif args[0] == 1:
return self.is_s2_busy or (len(self.task_queue) > 0) or (time < self.task_queue.end_time) or self.is_converting
return (self.is_s2_busy and self.is_s1_busy) or len(self.task_queue) > 0 or time < self.task_queue.end_time or self.is_converting
def update(self, time: float,state:list):
'''
检查器, 在执行判断前,先检查是否有之前的任务执行完成,更新变量
在step方法最前面调用,这里需要检查3个变量
'''
if self.is_converting:
if time >= self.convert_end_time:
self.is_converting = False
if self.is_s1_busy:
if time >= self.s1_end_time:
self.is_s1_busy = False
if self.is_s2_busy:
if time >= self.s2_end_time:
self.is_s2_busy = False
if (not self.is_s2_busy) or (not self.is_s1_busy):
for module, time_tik in self.task_queue:
if self.deviceID in (cmd := module.split('.')) and time >= time_tik:
if cmd[1] == 'open_s2' and not self.is_s2_busy:
valves_state = []
for device_id in [10, 11]:
valves_state.append(state[device_id].is_valve_down_open)
for device_id in range(6, 10):
valves_state.append(state[device_id].is_valve_open)
for device_id in [4, 5]:
valves_state.append(state[device_id].is_valve_s2_open)
if any(valves_state):
self.task_queue.clear_task()
# print(f'{self.deviceID} 开门失败,其他模块阀门已经打开')
break
else:
if self.is_converting:
self.task_queue.clear_task()
# print(f'{self.deviceID} 开门失败,正在进行状态转换')
break
else:
if self.wafer_S2 is not None:
self.wafer_S2.move_time = time - self.wafer_S2.last_step_end_time - 1 - move_cost_cal(state, self.wafer_S2.last_pos, self.deviceID)
self.open_valve_s2(time)
elif cmd[1] == 'open_s1' and not self.is_s1_busy:
if self.is_converting:
self.task_queue.clear_task()
# print(f'{self.deviceID} 开门失败,正在进行状态转换')
break
else:
if self.wafer_S1 is not None:
self.wafer_S1.move_time = time - self.wafer_S1.last_step_end_time - 1 - move_cost_cal(state, self.wafer_S1.last_pos, self.deviceID)
self.open_valve_s1(time)
self.task_queue.delete_task(module)
break
if len(self.task_queue) == 0 and self.task_queue.end_time <= time:
if self.is_valve_s1_open:
self.close_valve_s1(time)
elif self.is_valve_s2_open:
self.close_valve_s2(time)
if (not self.is_s2_busy) and (not self.is_s1_busy):
if len(self.task_queue) == 0 and self.task_queue.end_time <= time:
if self.wafer_S1 is not None:
if isinstance(process_step:=self.wafer_S1.route[0],str):
if process_step == f'{self.deviceID[-1]}S1':
if process_cost:=self.wafer_S1.route_cost[0] == 0:
self.wafer_S1.last_pos = self.deviceID
self.wafer_S1.complete_process()
self.wafer_S1.last_process_end_time = time
else:
if (not self.is_valve_s1_open) and (not self.is_valve_s2_open):
self.wafer_S1.last_pos = self.deviceID
self.wafer_S1.complete_process()
self._process(1,time,process_cost)
elif isinstance(process_step:=self.wafer_S1.route[0],list):
if f'{self.deviceID[-1]}S1' in process_step:
if process_cost:=self.wafer_S1.route_cost[0] == 0:
self.wafer_S1.last_pos = self.deviceID
self.wafer_S1.complete_process()
self.wafer_S1.last_process_end_time = time
else:
if (not self.is_valve_s1_open) and (not self.is_valve_s2_open):
self.wafer_S1.last_pos = self.deviceID
self.wafer_S1.complete_process()
self._process(1,time,process_cost)
if self.wafer_S2 is not None:
if isinstance(process_step:=self.wafer_S2.route[0],str):
if process_step == f'{self.deviceID[-1]}S2':
if process_cost:=self.wafer_S2.route_cost[0] == 0:
self.wafer_S2.last_pos = self.deviceID
self.wafer_S2.complete_process()
self.wafer_S2.last_process_end_time = time
else:
if (not self.is_valve_s1_open) and (not self.is_valve_s2_open):
self.wafer_S2.last_pos = self.deviceID
self.wafer_S2.complete_process()
self._process(2,time,process_cost)
elif isinstance(process_step:=self.wafer_S2.route[0],list):
if f'{self.deviceID[-1]}S2' in process_step:
if process_cost:=self.wafer_S2.route_cost[0] == 0:
self.wafer_S2.last_pos = self.deviceID
self.wafer_S2.complete_process()
self.wafer_S2.last_process_end_time = time
else:
if (not self.is_valve_s1_open) and (not self.is_valve_s2_open):
self.wafer_S2.last_pos = self.deviceID
self.wafer_S2.complete_process()
self._process(2,time,process_cost)
if self.wafer_S1 is not None:
if isinstance(m1 := self.wafer_S1.route[0],str) and m1[0] != self.deviceID[-1] :
self.wafer_S1.stay_time = time - self.wafer_S1.last_process_end_time
elif isinstance(m1 := self.wafer_S1.route[0],list) and f'{self.deviceID[-1]}S1' not in m1:
self.wafer_S1.stay_time = time - self.wafer_S1.last_process_end_time
if self.wafer_S2 is not None :
if isinstance(m1 := self.wafer_S2.route[0],str) and m1[0] != self.deviceID[-1] :
self.wafer_S2.stay_time = time - self.wafer_S2.last_process_end_time
elif isinstance(m1 := self.wafer_S2.route[0],list) and f'{self.deviceID[-1]}S2' not in m1:
self.wafer_S2.stay_time = time - self.wafer_S2.last_process_end_time
def get_obs(self) -> list:
have_task = len(self.task_queue) > 0
wafer1_id = self.wafer_S1.id + 1 if self.wafer_S1 is not None else 0
wafer2_id = self.wafer_S2.id + 1 if self.wafer_S2 is not None else 0
obs = [have_task, wafer1_id, wafer2_id,
self.mode, self.is_s1_busy, self.is_s2_busy, self.is_converting,
self.is_valve_s1_open, self.is_valve_s2_open]
return map(int, obs)
def _process(self, which_arm:int, time:float, process_cost:float):
if which_arm == 1:
self.is_s1_busy = True
self.s1_start_time = time
self.s1_end_time = time + process_cost
self.wafer_S1.last_process_end_time = self.s1_end_time
self.is_process_s1 = True
self.schedule_hist.append(ScheduleHistory(self.deviceID, self.s1_start_time, self.s1_end_time, PROCESS_COLOR, f"S1冷却晶圆{self.wafer_S1.id + 1}"))
self.output_json.append(
OutputJsonTemplate(StartTime=self.s1_start_time, EndTime=self.s1_end_time, MoveID=len(self.output_json),
MoveType=8,
ModuleName=self.deviceID,MatID=f'{self.wafer_S1.lp_id}.'
f'{self.wafer_S1.id + 1 if self.wafer_S1.id < 25
else self.wafer_S1.id - 24 if self.wafer_S1.id < 50
else self.wafer_S1.id - 49}',
srcSlotID=1,
destSlotID=1,
srcStation='', destStation=''))
elif which_arm == 2:
self.is_s2_busy = True
self.s2_start_time = time
self.s2_end_time = time + process_cost
self.wafer_S2.last_process_end_time = self.s2_end_time
self.is_process_s2 = True
self.schedule_hist.append(ScheduleHistory(self.deviceID, self.s2_start_time, self.s2_end_time, PROCESS_COLOR, f"S2冷却晶圆{self.wafer_S2.id + 1}"))
self.output_json.append(
OutputJsonTemplate(StartTime=self.s2_start_time, EndTime=self.s2_end_time, MoveID=len(self.output_json),
MoveType=8,
ModuleName=self.deviceID, MatID=f'{self.wafer_S2.lp_id}.'
f'{self.wafer_S2.id + 1 if self.wafer_S2.id < 25
else self.wafer_S2.id - 24 if self.wafer_S2.id < 50
else self.wafer_S2.id - 49}', srcSlotID=2,
destSlotID=2,
srcStation='', destStation=''))
def receive_task(self,cmd,time):
if cmd == 1:
self.convert_mode(time)
def __str__(self)->str:
# 打印所有变量, 每个变量一行
wf1_info = None if self.wafer_S1 is None else self.wafer_S1.id
wf2_info = None if self.wafer_S2 is None else self.wafer_S2.id
return (f'S1上晶圆:{wf1_info},工作中:{self.is_s1_busy},开始时间:{self.s1_start_time},结束时间:{self.s1_end_time}\n'
f'S2上晶圆:{wf2_info},工作中:{self.is_s2_busy},开始时间:{self.s2_start_time},结束时间:{self.s2_end_time}\n'
f'当前模式:{self.mode},是否在转换:{self.is_converting},转换开始时间:{self.convert_start_time},结束时间:{self.convert_end_time}\n'
f'下侧阀门状态:{self.is_valve_s1_open},上侧阀门状态:{self.is_valve_s2_open}\n')