|
|
|
@ -116,159 +116,164 @@ namespace Rs.MotionPlat.Flow
|
|
|
|
|
private void Client_DataRecived(System.Net.Sockets.Socket socket, byte[] data)
|
|
|
|
|
{
|
|
|
|
|
string msg = Encoding.UTF8.GetString(data);
|
|
|
|
|
|
|
|
|
|
foreach (string json in Scheduling.GetJsons(msg))
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
Scheduling schedule = JsonConvert.DeserializeObject<Scheduling>(json);
|
|
|
|
|
EInstruction eInstructioneInstruction = schedule.Instruction;
|
|
|
|
|
if(eInstructioneInstruction!= EInstruction.InquireStatus)
|
|
|
|
|
{
|
|
|
|
|
LogHelper.Debug(" >>> " +json);
|
|
|
|
|
}
|
|
|
|
|
switch (eInstructioneInstruction)
|
|
|
|
|
foreach (string json in Scheduling.GetJsons(msg))
|
|
|
|
|
{
|
|
|
|
|
case EInstruction.TransferSiloTray:
|
|
|
|
|
ReportToTestCenter(schedule);
|
|
|
|
|
MachineManage.Instance.SetLoadUnloadStatus(ERunState.Busying);
|
|
|
|
|
NgTrayToInputTrayFlow.Instance.MoveToInput();
|
|
|
|
|
MachineManage.Instance.SetLoadUnloadStatus(ERunState.Waiting);
|
|
|
|
|
Ops.Stop();
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.TestFinished:
|
|
|
|
|
MessageQueue.Instance.Insert("测试完成:"+JsonConvert.SerializeObject(schedule, new StringEnumConverter()));
|
|
|
|
|
ReportToTestCenter(schedule);
|
|
|
|
|
MachineManage.Instance.TestStatus = ETestStatus.Finished;
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.Rearrange:
|
|
|
|
|
Rearrange(json);
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.InquireSiloStatus:
|
|
|
|
|
InquireSiloStatus(json);
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.RequestSwitchTray:
|
|
|
|
|
RequestSwitchTray(json);
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.InquireConnectionInfo:
|
|
|
|
|
InquireConnectionInfo(json);
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.LoadAndUnload:
|
|
|
|
|
LoadAndUnload(json);
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.CancelLoadAndUnload:
|
|
|
|
|
//schedulResult = new SchedulingResult() { Instruction = EInstruction.CancelLoadAndUnload, State = ERunState.Busying, GroupID = sm.GroupID, TurnoverID = sm.TurnoverID };
|
|
|
|
|
ReportToTestCenter(schedule);
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.InquireStatus:
|
|
|
|
|
SchedulingStatusInfo statusInto = JsonConvert.DeserializeObject<SchedulingStatusInfo>(json);
|
|
|
|
|
switch (statusInto.Type)
|
|
|
|
|
{
|
|
|
|
|
case SchedulingStatusInfo.InfoType.State:
|
|
|
|
|
statusInto.Info = MachineManage.Instance.GetLoadUnloadStatus().ToString();// ERunState.Waiting.ToString();
|
|
|
|
|
break;
|
|
|
|
|
case SchedulingStatusInfo.InfoType.RunStatus:
|
|
|
|
|
statusInto.Info = MachineManage.Instance.GetCenterRunStatus().ToString();// ERunStatus.Started.ToString();
|
|
|
|
|
break;
|
|
|
|
|
case SchedulingStatusInfo.InfoType.RunMode:
|
|
|
|
|
statusInto.Info = MachineManage.Instance.RunMode.ToString();
|
|
|
|
|
break;
|
|
|
|
|
case SchedulingStatusInfo.InfoType.InitializeState:
|
|
|
|
|
statusInto.Info = MachineManage.Instance.InitializeState.ToString();// EInitializeState.Initialized.ToString();
|
|
|
|
|
break;
|
|
|
|
|
case SchedulingStatusInfo.InfoType.AssignMode:
|
|
|
|
|
statusInto.Info = EAssignMode.Normal.ToString();
|
|
|
|
|
break;
|
|
|
|
|
case SchedulingStatusInfo.InfoType.TesterState:
|
|
|
|
|
statusInto.Info = JsonConvert.SerializeObject(MachineManage.Instance.GetTesterInfo());//.ToString();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
ReportToTestCenter(statusInto);
|
|
|
|
|
//LogHelper.Debug(statusInto.ToString());
|
|
|
|
|
ReplayTaskEvent?.Invoke(statusInto.ToString());
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.SwitchStatus:
|
|
|
|
|
SchedulingStatusInfo switchStatus = JsonConvert.DeserializeObject<SchedulingStatusInfo>(msg);
|
|
|
|
|
SwitchStatus(switchStatus);
|
|
|
|
|
//MachineManage.Instance.MachineStatus= EMachineStatus.
|
|
|
|
|
//if (ss.TryConvertInfoTo<ERunMode>(out ERunMode runMode))
|
|
|
|
|
// MachineManage.Instance.RunMode = runMode;
|
|
|
|
|
//client.Send(ss.ToString());
|
|
|
|
|
//ReplayTaskEvent?.Invoke(ss.ToString());
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.ClearAlarm:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.GetRecipeList:
|
|
|
|
|
SchedulingResult sr = JsonConvert.DeserializeObject<SchedulingResult>(msg);
|
|
|
|
|
sr.Message = "Default";
|
|
|
|
|
client.Send(sr.ToString());
|
|
|
|
|
ReplayTaskEvent?.Invoke(sr.ToString());
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.SetRecipe:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.GetCurrentRecipe:
|
|
|
|
|
SchedulingResult srcur = JsonConvert.DeserializeObject<SchedulingResult>(msg);
|
|
|
|
|
srcur.Message = GlobalVar.CurRecipe;
|
|
|
|
|
client.Send(srcur.ToString());
|
|
|
|
|
ReplayTaskEvent?.Invoke(srcur.ToString());
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.TurnoverReady:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.ChangeState:
|
|
|
|
|
SchedulingResult cs = JsonConvert.DeserializeObject<SchedulingResult>(msg);
|
|
|
|
|
client.Send(cs.ToString());
|
|
|
|
|
ReplayTaskEvent?.Invoke(cs.ToString());
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.ShowMessage:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.CloseMessage:
|
|
|
|
|
SchedulingMessageBox mbox = JsonConvert.DeserializeObject<SchedulingMessageBox>(json);
|
|
|
|
|
TestCenterMessageBox.RecivedMsg(mbox);
|
|
|
|
|
//TestCenterMessageBoxManager.GetMsgbox(mbox.ID)?.Recived(mbox);
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.MachineButtonDown:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.MachineButtonUp:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.TrayProductRange:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.ClearTrayProductRange:
|
|
|
|
|
schedulResult = JsonConvert.DeserializeObject<SchedulingResult>(msg);
|
|
|
|
|
client.Send(schedulResult.ToString());
|
|
|
|
|
ReplayTaskEvent?.Invoke(schedulResult.ToString());
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.BeginSwitchTray:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.MoveScanner:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.InquireMoveScannerState:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.SetAlarms:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.CancelAlarms:
|
|
|
|
|
SchedulingAlarms alarms= JsonConvert.DeserializeObject<SchedulingAlarms>(msg);
|
|
|
|
|
if(alarms!=null&&alarms.Alarms!=null&&alarms.Alarms.Count>0)
|
|
|
|
|
{
|
|
|
|
|
foreach (AlarmItem alarm in alarms.Alarms)
|
|
|
|
|
Scheduling schedule = JsonConvert.DeserializeObject<Scheduling>(json);
|
|
|
|
|
EInstruction eInstructioneInstruction = schedule.Instruction;
|
|
|
|
|
if (eInstructioneInstruction != EInstruction.InquireStatus)
|
|
|
|
|
{
|
|
|
|
|
LogHelper.Debug(" >>> " + json);
|
|
|
|
|
}
|
|
|
|
|
switch (eInstructioneInstruction)
|
|
|
|
|
{
|
|
|
|
|
case EInstruction.TransferSiloTray:
|
|
|
|
|
ReportToTestCenter(schedule);
|
|
|
|
|
MachineManage.Instance.SetLoadUnloadStatus(ERunState.Busying);
|
|
|
|
|
NgTrayToInputTrayFlow.Instance.MoveToInput();
|
|
|
|
|
MachineManage.Instance.SetLoadUnloadStatus(ERunState.Waiting);
|
|
|
|
|
Ops.Stop();
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.TestFinished:
|
|
|
|
|
MessageQueue.Instance.Insert("测试完成:" + JsonConvert.SerializeObject(schedule, new StringEnumConverter()));
|
|
|
|
|
ReportToTestCenter(schedule);
|
|
|
|
|
MachineManage.Instance.TestStatus = ETestStatus.Finished;
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.Rearrange:
|
|
|
|
|
Rearrange(json);
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.InquireSiloStatus:
|
|
|
|
|
InquireSiloStatus(json);
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.RequestSwitchTray:
|
|
|
|
|
RequestSwitchTray(json);
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.InquireConnectionInfo:
|
|
|
|
|
InquireConnectionInfo(json);
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.LoadAndUnload:
|
|
|
|
|
LoadAndUnload(json);
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.CancelLoadAndUnload:
|
|
|
|
|
//schedulResult = new SchedulingResult() { Instruction = EInstruction.CancelLoadAndUnload, State = ERunState.Busying, GroupID = sm.GroupID, TurnoverID = sm.TurnoverID };
|
|
|
|
|
ReportToTestCenter(schedule);
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.InquireStatus:
|
|
|
|
|
SchedulingStatusInfo statusInto = JsonConvert.DeserializeObject<SchedulingStatusInfo>(json);
|
|
|
|
|
switch (statusInto.Type)
|
|
|
|
|
{
|
|
|
|
|
case SchedulingStatusInfo.InfoType.State:
|
|
|
|
|
statusInto.Info = MachineManage.Instance.GetLoadUnloadStatus().ToString();// ERunState.Waiting.ToString();
|
|
|
|
|
break;
|
|
|
|
|
case SchedulingStatusInfo.InfoType.RunStatus:
|
|
|
|
|
statusInto.Info = MachineManage.Instance.GetCenterRunStatus().ToString();// ERunStatus.Started.ToString();
|
|
|
|
|
break;
|
|
|
|
|
case SchedulingStatusInfo.InfoType.RunMode:
|
|
|
|
|
statusInto.Info = MachineManage.Instance.RunMode.ToString();
|
|
|
|
|
break;
|
|
|
|
|
case SchedulingStatusInfo.InfoType.InitializeState:
|
|
|
|
|
statusInto.Info = MachineManage.Instance.InitializeState.ToString();// EInitializeState.Initialized.ToString();
|
|
|
|
|
break;
|
|
|
|
|
case SchedulingStatusInfo.InfoType.AssignMode:
|
|
|
|
|
statusInto.Info = EAssignMode.Normal.ToString();
|
|
|
|
|
break;
|
|
|
|
|
case SchedulingStatusInfo.InfoType.TesterState:
|
|
|
|
|
statusInto.Info = JsonConvert.SerializeObject(MachineManage.Instance.GetTesterInfo());//.ToString();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
ReportToTestCenter(statusInto);
|
|
|
|
|
//LogHelper.Debug(statusInto.ToString());
|
|
|
|
|
ReplayTaskEvent?.Invoke(statusInto.ToString());
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.SwitchStatus:
|
|
|
|
|
SchedulingStatusInfo switchStatus = JsonConvert.DeserializeObject<SchedulingStatusInfo>(msg);
|
|
|
|
|
SwitchStatus(switchStatus);
|
|
|
|
|
//MachineManage.Instance.MachineStatus= EMachineStatus.
|
|
|
|
|
//if (ss.TryConvertInfoTo<ERunMode>(out ERunMode runMode))
|
|
|
|
|
// MachineManage.Instance.RunMode = runMode;
|
|
|
|
|
//client.Send(ss.ToString());
|
|
|
|
|
//ReplayTaskEvent?.Invoke(ss.ToString());
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.ClearAlarm:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.GetRecipeList:
|
|
|
|
|
SchedulingResult sr = JsonConvert.DeserializeObject<SchedulingResult>(msg);
|
|
|
|
|
sr.Message = "Default";
|
|
|
|
|
client.Send(sr.ToString());
|
|
|
|
|
ReplayTaskEvent?.Invoke(sr.ToString());
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.SetRecipe:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.GetCurrentRecipe:
|
|
|
|
|
SchedulingResult srcur = JsonConvert.DeserializeObject<SchedulingResult>(msg);
|
|
|
|
|
srcur.Message = GlobalVar.CurRecipe;
|
|
|
|
|
client.Send(srcur.ToString());
|
|
|
|
|
ReplayTaskEvent?.Invoke(srcur.ToString());
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.TurnoverReady:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.ChangeState:
|
|
|
|
|
SchedulingResult cs = JsonConvert.DeserializeObject<SchedulingResult>(msg);
|
|
|
|
|
client.Send(cs.ToString());
|
|
|
|
|
ReplayTaskEvent?.Invoke(cs.ToString());
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.ShowMessage:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.CloseMessage:
|
|
|
|
|
SchedulingMessageBox mbox = JsonConvert.DeserializeObject<SchedulingMessageBox>(json);
|
|
|
|
|
TestCenterMessageBox.RecivedMsg(mbox);
|
|
|
|
|
//TestCenterMessageBoxManager.GetMsgbox(mbox.ID)?.Recived(mbox);
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.MachineButtonDown:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.MachineButtonUp:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.TrayProductRange:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.ClearTrayProductRange:
|
|
|
|
|
schedulResult = JsonConvert.DeserializeObject<SchedulingResult>(msg);
|
|
|
|
|
client.Send(schedulResult.ToString());
|
|
|
|
|
ReplayTaskEvent?.Invoke(schedulResult.ToString());
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.BeginSwitchTray:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.MoveScanner:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.InquireMoveScannerState:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.SetAlarms:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.CancelAlarms:
|
|
|
|
|
SchedulingAlarms alarms = JsonConvert.DeserializeObject<SchedulingAlarms>(msg);
|
|
|
|
|
if (alarms != null && alarms.Alarms != null && alarms.Alarms.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
if(!alarmInfos.ContainsKey(alarm.NO))
|
|
|
|
|
foreach (AlarmItem alarm in alarms.Alarms)
|
|
|
|
|
{
|
|
|
|
|
alarmInfos.Add(alarm.NO, alarm);
|
|
|
|
|
if (!alarmInfos.ContainsKey(alarm.NO))
|
|
|
|
|
{
|
|
|
|
|
alarmInfos.Add(alarm.NO, alarm);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.TakingError:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.CancelTakingError:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.ScanBarcode:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.TakingError:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.CancelTakingError:
|
|
|
|
|
break;
|
|
|
|
|
case EInstruction.ScanBarcode:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
LogHelper.Debug("receivedata:" + msg);
|
|
|
|
|
LogHelper.Debug("接收数据异常:"+ex.Message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//string msg=Encoding.UTF8.GetString(data);
|
|
|
|
|
//LogHelper.TestCenterLog("接收:"+msg);
|
|
|
|
|
//ReciveTaskEvent?.Invoke(msg);
|
|
|
|
@ -297,6 +302,13 @@ namespace Rs.MotionPlat.Flow
|
|
|
|
|
SiloRearrange info = JsonConvert.DeserializeObject<SiloRearrange>(json);
|
|
|
|
|
ReportToTestCenter(info);
|
|
|
|
|
Thread.Sleep(200);
|
|
|
|
|
|
|
|
|
|
//TurnoverInfos tis = new TurnoverInfos();
|
|
|
|
|
//tis.GroupID = 0;
|
|
|
|
|
//tis.Instruction = EInstruction.RearrangeResult;
|
|
|
|
|
//ReportToTestCenter(tis);
|
|
|
|
|
//MachineManage.Instance.SetLoadUnloadStatus(ERunState.Waiting);
|
|
|
|
|
//return;
|
|
|
|
|
if (info.SiloType == SchedulingSiloBase.ESiloType.Multifunction)
|
|
|
|
|
{
|
|
|
|
|
TraySlot ts = GlobalTray.MultiTray.GetSlot(Controls.ESlotStatus.NotHave);
|
|
|
|
@ -310,15 +322,18 @@ namespace Rs.MotionPlat.Flow
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
List<TurnoverInfo> rearrangeList = RearrangeManager.GeneralRearrangeTask(TurnoverType.Multifunction,TurnoverType.Multifunction, GlobalTray.MultiTray, ts, 1);
|
|
|
|
|
RearrangeTask.AddRanage(rearrangeList);
|
|
|
|
|
RearrangeFlow.Instance.Rearrange();
|
|
|
|
|
Task.Run(() => {
|
|
|
|
|
List<TurnoverInfo> rearrangeList = RearrangeManager.GeneralRearrangeTask(TurnoverType.Multifunction, TurnoverType.Multifunction, GlobalTray.MultiTray, ts, 1);
|
|
|
|
|
RearrangeTask.AddRanage(rearrangeList);
|
|
|
|
|
RearrangeFlow.Instance.Rearrange();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (info.SiloType == SchedulingSiloBase.ESiloType.Passed)
|
|
|
|
|
{
|
|
|
|
|
TraySlot ts = GlobalTray.OkTary.GetSlot(Controls.ESlotStatus.NotHave);
|
|
|
|
|
if (ts==null || ts.Column==0)//无需重排
|
|
|
|
|
if (ts == null || ts.Column == 0)//无需重排
|
|
|
|
|
{
|
|
|
|
|
TurnoverInfos tis = new TurnoverInfos();
|
|
|
|
|
tis.GroupID = 0;
|
|
|
|
@ -328,9 +343,13 @@ namespace Rs.MotionPlat.Flow
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
List<TurnoverInfo> rearrangeList = RearrangeManager.GeneralRearrangeTask(TurnoverType.Passed,TurnoverType.Passed, GlobalTray.OkTary, ts, 1);
|
|
|
|
|
RearrangeTask.AddRanage(rearrangeList);
|
|
|
|
|
RearrangeFlow.Instance.Rearrange();
|
|
|
|
|
Task.Run(() => {
|
|
|
|
|
List<TurnoverInfo> rearrangeList = RearrangeManager.GeneralRearrangeTask(TurnoverType.Passed, TurnoverType.Passed, GlobalTray.OkTary, ts, 1);
|
|
|
|
|
RearrangeTask.AddRanage(rearrangeList);
|
|
|
|
|
|
|
|
|
|
RearrangeFlow.Instance.Rearrange();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (info.SiloType == SchedulingSiloBase.ESiloType.Failed)
|
|
|
|
@ -346,9 +365,12 @@ namespace Rs.MotionPlat.Flow
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
List<TurnoverInfo> rearrangeList = RearrangeManager.GeneralRearrangeTask(TurnoverType.Failed,TurnoverType.Failed, GlobalTray.NgTray,ts, 1);
|
|
|
|
|
RearrangeTask.AddRanage(rearrangeList);
|
|
|
|
|
RearrangeFlow.Instance.Rearrange();
|
|
|
|
|
Task.Run(() => {
|
|
|
|
|
List<TurnoverInfo> rearrangeList = RearrangeManager.GeneralRearrangeTask(TurnoverType.Failed, TurnoverType.Failed, GlobalTray.NgTray, ts, 1);
|
|
|
|
|
RearrangeTask.AddRanage(rearrangeList);
|
|
|
|
|
RearrangeFlow.Instance.Rearrange();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|