|
@@ -38,11 +38,16 @@ namespace CyberX8_Simulator.Devices
|
|
|
private bool _isLP3Docked;
|
|
|
public int WaferSize = 200;
|
|
|
private int _speed = 20;
|
|
|
- private string _carrier1ID = "12345678";
|
|
|
- private string _carrier2ID = "12345678";
|
|
|
- private string _carrier3ID = "12345678";
|
|
|
+
|
|
|
private List<int> _lpStationNumber;
|
|
|
- private List<int> _dummyStationNumber;
|
|
|
+ private List<int> _dummyStationNumber;
|
|
|
+
|
|
|
+
|
|
|
+ private readonly Queue<(string ack, TaskCompletionSource<bool> tcs)> _pendingAcks = new Queue<(string, TaskCompletionSource<bool>)>();
|
|
|
+ private readonly object _syncRoot = new object();
|
|
|
+ private bool _isPaused;
|
|
|
+
|
|
|
+
|
|
|
public string SlotMap
|
|
|
{
|
|
|
get { return string.Join("", _slotMap); }
|
|
@@ -55,8 +60,6 @@ namespace CyberX8_Simulator.Devices
|
|
|
public bool _isMaintain { get; set; }
|
|
|
public bool _isWaferPresent { get; set; }
|
|
|
public bool _isMaintainDoorOpen { get; private set; }
|
|
|
-
|
|
|
-
|
|
|
public bool _isProtrude1 { get; set; }
|
|
|
public bool _isProtrude2 { get; set; }
|
|
|
public bool _isVAC { get; set; }
|
|
@@ -87,6 +90,31 @@ namespace CyberX8_Simulator.Devices
|
|
|
GetStationNumber();
|
|
|
}
|
|
|
|
|
|
+ private void Pause()
|
|
|
+ {
|
|
|
+ lock (_syncRoot)
|
|
|
+ {
|
|
|
+ _isPaused = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Resume()
|
|
|
+ {
|
|
|
+ lock (_syncRoot)
|
|
|
+ {
|
|
|
+ _isPaused = false;
|
|
|
+
|
|
|
+ // 发送所有积压的消息
|
|
|
+ while (_pendingAcks.Count > 0)
|
|
|
+ {
|
|
|
+ var (ack, tcs) = _pendingAcks.Dequeue();
|
|
|
+ OnWriteMessage(ack);
|
|
|
+ tcs.TrySetResult(true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
private bool OnSendEvent()
|
|
|
{
|
|
|
if (IsConnected)
|
|
@@ -170,12 +198,13 @@ namespace CyberX8_Simulator.Devices
|
|
|
string msg = $"EVT:SIGSTAT/System/{data:X8}/00000000;";
|
|
|
OnWriteMessage(msg);
|
|
|
}
|
|
|
- private void OnWork(string str)
|
|
|
+ private async void OnWork(string str)
|
|
|
{
|
|
|
string ack = "";
|
|
|
if (str.StartsWith("RQ LOAD")) //查询手指是否带片
|
|
|
{
|
|
|
ack = "LOAD A OFF\n_RDY";
|
|
|
+ OnWriteMessage(ack);
|
|
|
}
|
|
|
else if (str.StartsWith("RSR")) //返回扫片结果
|
|
|
{
|
|
@@ -199,152 +228,78 @@ namespace CyberX8_Simulator.Devices
|
|
|
}
|
|
|
ack = "MAP" + dummywafer;
|
|
|
}
|
|
|
+ OnWriteMessage(ack);
|
|
|
}
|
|
|
else if (str.StartsWith("PAUSE"))
|
|
|
{
|
|
|
+ Pause();
|
|
|
ack = "PAUSE_RDY";
|
|
|
+ OnWriteMessage(ack);
|
|
|
}
|
|
|
else if(str.StartsWith("RESUME"))
|
|
|
{
|
|
|
+ Resume();
|
|
|
ack = "RESUME_RDY";
|
|
|
+ OnWriteMessage(ack);
|
|
|
_ = Task.Run(DelayAnswerResume); // 启动后台任务
|
|
|
+
|
|
|
|
|
|
}
|
|
|
else //默认回复
|
|
|
{
|
|
|
+ ack = "_RDY";
|
|
|
if (str.StartsWith("PICK") || str.StartsWith("PLACE") || str.StartsWith("MAP")
|
|
|
- || str.StartsWith("ALIGNER ALGN") || str.StartsWith("ALIGNER HOME"))
|
|
|
+ || str.StartsWith("ALIGNER ALGN") || str.StartsWith("ALIGNER HOME"))
|
|
|
+ {
|
|
|
+ var tcs = new TaskCompletionSource<bool>();
|
|
|
+
|
|
|
+ lock (_syncRoot)
|
|
|
+ {
|
|
|
+ // 如果已经暂停,直接加入队列
|
|
|
+ if (_isPaused)
|
|
|
+ {
|
|
|
+ _pendingAcks.Enqueue((ack, tcs));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 启动延迟任务
|
|
|
+ var delayTask = Task.Delay(3200);
|
|
|
+
|
|
|
+ // 创建监控任务
|
|
|
+ var monitorTask = Task.Run(async () =>
|
|
|
+ {
|
|
|
+ while (!delayTask.IsCompleted)
|
|
|
+ {
|
|
|
+ await Task.Delay(100);
|
|
|
+ lock (_syncRoot)
|
|
|
+ {
|
|
|
+ if (_isPaused)
|
|
|
+ {
|
|
|
+ _pendingAcks.Enqueue((ack, tcs));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 延迟完成且未被暂停
|
|
|
+ OnWriteMessage(ack);
|
|
|
+ tcs.SetResult(true);
|
|
|
+ });
|
|
|
+
|
|
|
+ await monitorTask;
|
|
|
+ }
|
|
|
+ else
|
|
|
{
|
|
|
- Thread.Sleep((ushort)2200);
|
|
|
+ OnWriteMessage(ack);
|
|
|
}
|
|
|
- ack = "_RDY";
|
|
|
- }
|
|
|
- OnWriteMessage(ack);
|
|
|
- //// match basic
|
|
|
- //Match m1 = Regex.Match(strACK, SCMD);
|
|
|
-
|
|
|
- //// get mock delay time
|
|
|
- //string sBasic = m1.Groups[1].Value;
|
|
|
-
|
|
|
- //if (string.IsNullOrEmpty(sBasic))
|
|
|
- // return;
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- //EfemOperation op = EfemConstant.ToOperation(sBasic);
|
|
|
- //ushort millionSec = this.SimuOperationTime(op);
|
|
|
-
|
|
|
- //// delay
|
|
|
- ////await Task.Delay(millionSec);
|
|
|
- //Thread.Sleep(millionSec);
|
|
|
-
|
|
|
- //// build the INF string
|
|
|
- //string strINF = string.Empty;
|
|
|
-
|
|
|
- //switch (EfemConstant.ToOperation(strACK))
|
|
|
- //{
|
|
|
- // case EfemOperation.GetWaferInfo:
|
|
|
- // strINF = strACK.Replace(ACK, "INF");
|
|
|
- // strINF = strINF.TrimEnd(';');
|
|
|
- // string map1 = string.Join("", _slotMap);
|
|
|
- // strINF += $"/{map1};";
|
|
|
- // break;
|
|
|
- // case EfemOperation.Home:
|
|
|
- // int data1 = 0b011111111010100000;
|
|
|
- // string s1 = $"EVT:SIGSTAT/LP1/00000000/00000003;\rEVT:SIGSTAT/LP2/0000000/00000003;\rEVT:SIGSTAT/LP3/000000/00000003;\rEVT:SIGSTAT/System/{data1}/00000;\r";
|
|
|
-
|
|
|
- // strINF = s1 + strACK.Replace(ACK, "INF");
|
|
|
- // SendSystemData();
|
|
|
- // SendLP1Data();
|
|
|
- // SendLP2Data();
|
|
|
- // SendLP3Data();
|
|
|
- // SendAlignData();
|
|
|
- // OnWriteMessage(strACK.Replace(ACK, "INF"));
|
|
|
- // return;
|
|
|
- // case EfemOperation.Map:
|
|
|
- // if(strACK.Contains("BF"))
|
|
|
- // {
|
|
|
- // string map = string.Join("", _slotDummy);
|
|
|
- // string strEVT = strACK.Replace(ACK,"EVT");
|
|
|
- // strEVT = strEVT.Replace("WAFSH","MAPDT");
|
|
|
- // strEVT = strEVT.TrimEnd(';');
|
|
|
- // string str = $"{strEVT}/{map};";
|
|
|
- // OnWriteMessage(str);
|
|
|
- // }
|
|
|
- // else if (strACK.Contains("LP"))
|
|
|
- // {
|
|
|
- // string map = string.Join("", _slotMap);
|
|
|
- // string strEVT = strACK.Replace(ACK, "EVT");
|
|
|
- // strEVT = strEVT.Replace("WAFSH", "MAPDT");
|
|
|
- // strEVT = strEVT.TrimEnd(';');
|
|
|
- // string str = $"{strEVT}/{map};";
|
|
|
- // OnWriteMessage(str);
|
|
|
- // }
|
|
|
- // strINF = strACK.Replace(ACK, "INF");
|
|
|
- // break;
|
|
|
- // case EfemOperation.Load:
|
|
|
- // strINF = strACK.Replace(ACK, "INF");
|
|
|
- // break;
|
|
|
- // case EfemOperation.StateTrack:
|
|
|
- // strINF = "INF:STATE/TRACK/NONE/NONE;";
|
|
|
- // break;
|
|
|
- // case EfemOperation.CarrierId:
|
|
|
-
|
|
|
-
|
|
|
- // break;
|
|
|
-
|
|
|
- // case EfemOperation.Size:
|
|
|
-
|
|
|
- // break;
|
|
|
- // case EfemOperation.Dock:
|
|
|
- // Match mDock = Regex.Match(strACK, SFOUP);
|
|
|
- // if (mDock.Success)
|
|
|
- // {
|
|
|
- // UpdateLocked(mDock.Groups[1].Value, true);
|
|
|
- // }
|
|
|
- // strINF = strACK.Replace(ACK, "INF");
|
|
|
- // break;
|
|
|
- // case EfemOperation.Undock:
|
|
|
- // Match mUnDock = Regex.Match(strACK, SFOUP);
|
|
|
- // if (mUnDock.Success)
|
|
|
- // {
|
|
|
- // UpdateLocked(mUnDock.Groups[1].Value, false);
|
|
|
- // }
|
|
|
- // strINF = strACK.Replace(ACK, "INF");
|
|
|
- // break;
|
|
|
- // case EfemOperation.Clamp:
|
|
|
- // Match mClamp= Regex.Match(strACK, SFOUP);
|
|
|
- // if (mClamp.Success)
|
|
|
- // {
|
|
|
- // UpdateClamped(mClamp.Groups[1].Value, true);
|
|
|
- // }
|
|
|
- // strINF = strACK.Replace(ACK, "INF");
|
|
|
- // break;
|
|
|
- // case EfemOperation.Unclamp:
|
|
|
- // Match mUnClamp = Regex.Match(strACK, SFOUP);
|
|
|
- // if (mUnClamp.Success)
|
|
|
- // {
|
|
|
- // UpdateClamped(mUnClamp.Groups[1].Value, false);
|
|
|
- // }
|
|
|
- // strINF = strACK.Replace(ACK, "INF");
|
|
|
- // break;
|
|
|
- // case EfemOperation.Speed:
|
|
|
-
|
|
|
- // break;
|
|
|
- // case EfemOperation.Align:
|
|
|
- // case EfemOperation.Pick:
|
|
|
- // case EfemOperation.Place:
|
|
|
- // case EfemOperation.Orgsh:
|
|
|
- // case EfemOperation.Light:
|
|
|
- // case EfemOperation.SigStatus:
|
|
|
- // default:
|
|
|
- // strINF = strACK.Replace(ACK, "INF");
|
|
|
- // break;
|
|
|
- //}
|
|
|
-
|
|
|
- //OnWriteMessage(strINF);
|
|
|
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
private void DelayAnswerResume()
|
|
|
{
|
|
|
Thread.Sleep((ushort)3000);
|