| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589 | 
							- using System;
 
- using System.Diagnostics;
 
- using System.Xml;
 
- using Aitex.Core.Common.DeviceData;
 
- using Aitex.Core.RT.DataCenter;
 
- using Aitex.Core.RT.Event;
 
- using Aitex.Core.RT.IOCore;
 
- using Aitex.Core.RT.Log;
 
- using Aitex.Core.RT.OperationCenter;
 
- using Aitex.Core.RT.SCCore;
 
- using Aitex.Core.Util;
 
- using MECF.Framework.Common.Event;
 
- namespace Aitex.Core.RT.Device.Unit
 
- {
 
-     public interface IValve
 
-     {
 
-         bool TurnValve(bool isOn, out string reason);
 
-     }
 
-     public class IoValve : BaseDevice, IDevice, IValve
 
-     {
 
-         public string GVName { get { return Name; } }
 
-         public string GVDeviceID { get { return DeviceID; } }
 
-         public bool GVIsDefaultOpen { get { return _isDefaultOpen; } }
 
-         public AlarmEventItem InterlockAlarm;
 
-         public AlarmEventItem InterlockWarning;
 
-         public DOAccessor DOOpen => _doOpen;
 
-         public DOAccessor DOClose => _doClose;
 
-         private bool _setPoint;
 
-         public bool IsILKOK = true;
 
-         [Subscription(AITValveDataPropertyName.SetPoint)]
 
-         public bool SetPoint    //True:open| False:close
 
-         {
 
-             get
 
-             {
 
-                 return _setPoint;
 
-                 //if (_doOpen == null)
 
-                 //    return false;
 
-                 //return _isNc ? _doOpen.Value : !_doOpen.Value;
 
-             }
 
-             set
 
-             {
 
-                 if (_doOpen != null)
 
-                 {
 
-                     if (_doOpen.SetValue(_isNc ? value : !value, out _, false))
 
-                     {
 
-                         _setPoint = value;
 
-                     }
 
-                 }
 
-                 if (_doClose != null)
 
-                 {
 
-                     if (_doClose.SetValue(_isNc ? !value : value, out _, false))
 
-                     {
 
-                         _setPoint = value;
 
-                     }
 
-                 }
 
-             }
 
-         }
 
-         [Subscription(AITValveDataPropertyName.Status)]
 
-         public bool Status    //True:open | False:close
 
-         {
 
-             get
 
-             {
 
-                 if (_diOpenSensor != null && _diCloseSensor != null)
 
-                     return _diOpenSensor.Value && !_diCloseSensor.Value;
 
-                 if (_diCloseSensor != null)
 
-                     return !_diCloseSensor.Value;
 
-                 if (_diOpen != null)
 
-                     return _isNc ? _diOpen.Value : !_diOpen.Value;
 
-                 if (_doOpen != null)
 
-                     return _isNc ? _doOpen.Value : !_doOpen.Value;
 
-                 if (_doClose != null)
 
-                     return _isNc ? !_doClose.Value : _doClose.Value;
 
-                 return false;
 
-             }
 
-         }
 
-         private AITValveData DeviceData
 
-         {
 
-             get
 
-             {
 
-                 AITValveData data = new AITValveData()
 
-                 {
 
-                     UniqueName = _uniqueName,
 
-                     DeviceName = GVName,
 
-                     DefaultValue = GVIsDefaultOpen,
 
-                     DeviceSchematicId = DeviceID,
 
-                     DisplayName = Display,
 
-                     Feedback = Status,
 
-                     SetPoint = SetPoint,
 
-                     ILKDiValue = ILKDiSensor == null ? true : ILKDiSensor.Value,
 
-                     IsILKOK = this.IsILKOK,
 
-                     VirtualFeedback = VirtualStatus,
 
-                 };
 
-                 return data;
 
-             }
 
-         }
 
-         /// <summary>
 
-         /// normal closed, 0 关闭,1打开
 
-         /// </summary>
 
-         public bool _isNc;
 
-         /// <summary>
 
-         /// default open
 
-         /// </summary>
 
-         public bool _isDefaultOpen;
 
-         private DIAccessor _diOpenSensor;
 
-         private DIAccessor _diCloseSensor;
 
-         private DIAccessor _diOpen;
 
-         private DOAccessor _doOpen;
 
-         private DOAccessor _doClose;
 
-         private DIAccessor ILKDiSensor;
 
-         private bool _operation;
 
-         public bool IsILKNot { get; set; } = false;
 
-         R_TRIG eventTrigger = new R_TRIG();
 
-         R_TRIG _mutexSignalTrigger = new R_TRIG();
 
-         DeviceTimer _timer = new DeviceTimer();
 
-         DeviceTimer _mutexSignalTimer = new DeviceTimer();
 
-         private string _uniqueName;
 
-         public bool VirtualStatus;
 
-         private string _writeLog = "";
 
-         private SCConfigItem _interlockNoneOrExist;
 
-         private SCConfigItem _interlockNormaly0nOrOff;
 
-         private SCConfigItem _interlockDelayOnTime;
 
-         private SCConfigItem _interlockDelayOffTime;
 
-         private SCConfigItem _interlockILKTime;
 
-         private float _interlockDelayTimeInMS;
 
-         private float _ilkDelayTimeInMS;
 
-         private Stopwatch _interlockDelayTimer = new Stopwatch();
 
-         private Stopwatch _ilkDelayTimer = new Stopwatch();
 
-         public IoValve(string module, XmlElement node, string ioModule = "")
 
-         {
 
-             var attrModule = node.GetAttribute("module");
 
-             base.Module = string.IsNullOrEmpty(attrModule) ? module : attrModule;
 
-             base.Name = node.GetAttribute("id");
 
-             base.Display = node.GetAttribute("display");
 
-             base.DeviceID = node.GetAttribute("schematicId");
 
-             _isNc = Convert.ToBoolean(node.GetAttribute("isNc"));
 
-             _isDefaultOpen = Convert.ToBoolean(node.GetAttribute("isDefaultOpen"));
 
-             _diOpenSensor = ParseDiNode("diOpenSensor", node, ioModule);
 
-             _diCloseSensor = ParseDiNode("diCloseSensor", node, ioModule);
 
-             _doOpen = ParseDoNode("doOpen", node, ioModule);
 
-             _diOpen = ParseDiNode("diOpen", node, ioModule);
 
-             _doClose = ParseDoNode("doClose", node, ioModule);
 
-             ILKDiSensor = ParseDiNode("ILKDi", node, ioModule);
 
-             var attIsILKNot = node.GetAttribute("IsILKNot");
 
-             IsILKNot = string.IsNullOrEmpty(attIsILKNot) ? false : Convert.ToBoolean(attIsILKNot);
 
-             _uniqueName = $"{Module}.{Name}";
 
-             InterlockAlarm= SubscribeAlarm(new AlarmEventItem()
 
-             {
 
-                 EventEnum = $"{Module}.{Name} InterlockAlarm",
 
-                 Description = $"{Name} Interlock alarm",
 
-                 Solution = "No information available. Press[Clear] to delete alarm message.",
 
-                 Explaination = "No information available.",
 
-                 AutoRecovery = false,
 
-                 Level = EventLevel.Alarm,
 
-                 Action = EventAction.Clear,
 
-                 Category = "TubeAlarm",
 
-             }, () => { Reset(); return true; });
 
-         }
 
-         public bool Initialize()
 
-         {
 
-             _operation = _isNc ? false : true;
 
-             if (_isNc == false && _isDefaultOpen == false)
 
-             {
 
-                 TurnValve(_isDefaultOpen, out string tempReason);
 
-             }
 
-             DATA.Subscribe($"Device.{Module}.{GVName}", () => DeviceData);
 
-             DATA.Subscribe($"{_uniqueName}.DeviceData", () => DeviceData);
 
-             DATA.Subscribe($"{Module}.{Name}.SetPoint", () => SetPoint);
 
-             DATA.Subscribe($"{Module}.{Name}.Feedback", () => Status);
 
-             OP.Subscribe($"{_uniqueName}.{AITValveOperation.GVTurnValve}", InvokeOpenCloseValve);
 
-             OP.Subscribe($"{_uniqueName}.{AITValveOperation.GVVirtualTurnValve}", InvokeOpenCloseVirtualValve);
 
-             DEVICE.Register(String.Format("{0}.{1}", Name, AITValveOperation.GVTurnValve), (out string reason, int time, object[] param) =>
 
-             {
 
-                 bool bOn = Convert.ToBoolean((string)param[0]);
 
-                 bool ret = TurnValve(bOn, out reason);
 
-                 if (ret)
 
-                 {
 
-                     reason = string.Format("Valve {0}{1}", Name, bOn ? "Open" : "Close");
 
-                     return true;
 
-                 }
 
-                 return false;
 
-             });
 
-             //for recipe
 
-             DEVICE.Register(String.Format("{0}", Name), (out string reason, int time, object[] param) =>
 
-             {
 
-                 bool bOn = Convert.ToBoolean((string)param[0]);
 
-                 bool ret = TurnValve(bOn, out reason);
 
-                 if (ret)
 
-                 {
 
-                     reason = string.Format("Valve {0}{1}", Name, bOn ? "Open" : "Close");
 
-                     return true;
 
-                 }
 
-                 return false;
 
-             });
 
-             //for recipe
 
-             OP.Subscribe($"{Module}.{Name}", (out string reason, int time, object[] param) =>
 
-             {
 
-                 reason = string.Empty;
 
-                 if (param[0].ToString() == "Continue")
 
-                 {
 
-                     EV.PostInfoLog(Module, $"Turn {Display} Continue");
 
-                     return true;
 
-                 }
 
-                 bool bOn = Convert.ToBoolean((string)param[0]);
 
-                 bool ret = TurnValve(bOn, out reason);
 
-                 if (!ret)
 
-                 {
 
-                     reason = string.Format("Valve {0}{1} failed", Name, bOn ? "Open" : "Close");
 
-                     return false;
 
-                 }
 
-                 return true;
 
-             });
 
-             var valveIndex = Name.Replace("ValveAV", "");
 
-             _interlockNoneOrExist = SC.GetConfigItem($"PM1.RecipeEditParameter.InterLock.{valveIndex}.NoneOrExist");
 
-             _interlockNormaly0nOrOff = SC.GetConfigItem($"PM1.RecipeEditParameter.InterLock.{valveIndex}.Normaly0nOrOff");
 
-             _interlockDelayOnTime = SC.GetConfigItem($"PM1.RecipeEditParameter.InterLock.{valveIndex}.DelayOnTime");
 
-             _interlockDelayOffTime = SC.GetConfigItem($"PM1.RecipeEditParameter.InterLock.{valveIndex}.DelayOffTime");
 
-             _interlockILKTime = SC.GetConfigItem($"PM1.RecipeEditParameter.InterLock.{valveIndex}.ILKTime");
 
-             return true;
 
-         }
 
-         public void Terminate()
 
-         {
 
-             string reason;
 
-             TurnValve(_isDefaultOpen, out reason);
 
-         }
 
-         public bool InvokeOpenCloseValve(string method, object[] args)
 
-         {
 
-             string reason;
 
-             bool op = Convert.ToBoolean(args[0]);
 
-             string name = op ? "Open" : "Close";
 
-             if (!TurnValve(op, out reason))
 
-             {
 
-                 if (InterlockWarning != null)
 
-                 {
 
-                     InterlockWarning.Description = $"{reason}";
 
-                     InterlockWarning.Set();
 
-                 }
 
-             
 
-                 return false;
 
-             }
 
-             EV.PostInfoLog(Module, $"{name} valve {Module}.{Name}");
 
-             return true;
 
-         }
 
-         public bool InvokeOpenCloseVirtualValve(string method, object[] args)
 
-         {
 
-             bool op = Convert.ToBoolean(args[0]);
 
-             VirtualStatus = op;
 
-             return true;
 
-         }
 
-         private bool _openOperation;
 
-         public void Monitor()
 
-         {
 
-             if (!string.IsNullOrEmpty(_writeLog))
 
-             {
 
-                 LOG.Write(_writeLog);
 
-                 _writeLog = "";
 
-             }
 
-            
 
-             if (_interlockDelayTimer.IsRunning)
 
-             {
 
-                 if (_interlockDelayTimer.ElapsedMilliseconds >= _interlockDelayTimeInMS)
 
-                 {
 
-                     _interlockDelayTimer.Stop();
 
-                     _interlockDelayTimer.Reset();
 
-                     bool bValue = _operation;
 
-                     _openOperation = _operation;
 
-                     if (_doOpen != null)
 
-                     {
 
-                         if (!_doOpen.Check(bValue, out var reason))
 
-                         {
 
-                             if (!_doOpen.SetValue(bValue, out reason, false))
 
-                             {
 
-                                 EV.PostWarningLog("System", $"interlock set DO {Module} to {bValue}:{reason}, {reason}");
 
-                             }
 
-                             // EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, $"{(_operation ? "Open" : "Close")}", ":Failed for interlock " + reason);
 
-                             _operation = SetPoint; //还原状态 防止定时检查状态再次写日志
 
-                             return;
 
-                         }
 
-                     }
 
-                     if (_doClose != null)
 
-                     {
 
-                         if (!_doClose.Check(bValue, out var reason))
 
-                         {
 
-                             if (!_doClose.SetValue(bValue, out reason, false))
 
-                             {
 
-                                 EV.PostWarningLog("System", $"interlock set DO {Module} to {bValue}:{reason}, {reason}");
 
-                             }
 
-                             //EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, $"{(_operation ? "Open" : "Close")}", ":Failed for interlock " + reason); ;
 
-                             _operation = SetPoint;//还原状态 防止定时检查状态再次写日志
 
-                             return;
 
-                         }
 
-                     }
 
-                     SetPoint = _isNc ? _operation : !_operation;
 
-                 }
 
-                 else
 
-                 {
 
-                     return;
 
-                 }
 
-             }
 
-             try
 
-             {
 
-                 if (_diOpenSensor != null && _diCloseSensor != null && _doOpen != null)
 
-                 {
 
-                     if (_diOpenSensor.Value != _diCloseSensor.Value)
 
-                     {
 
-                         _mutexSignalTimer.Start(2000);
 
-                     }
 
-                     _mutexSignalTrigger.CLK = _mutexSignalTimer.IsTimeout();
 
-                     if (_mutexSignalTrigger.Q)
 
-                     {
 
-                         EV.PostWarningLog(Module, $"Valve {Name} was abnormal,Reason:diOpenSensor's value is {_diOpenSensor.Value} and diCloseSensor's value is {_diCloseSensor.Value} too.");
 
-                     }
 
-                 }
 
-                 if (_timer.IsTimeout() && _doOpen != null)
 
-                 {
 
-                     _timer.Stop();
 
-                     if (Status != _operation)
 
-                     {
 
-                         if (_operation)
 
-                         {
 
-                             string reason;
 
-                             if (!_doOpen.Check(_isNc ? true : false, out reason))
 
-                                 EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Open", ":Failed for interlock " + reason);
 
-                             else
 
-                                 EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Open", $":Valve {Name} keep closed ");
 
-                         }
 
-                         else
 
-                         {
 
-                             string reason;
 
-                             if (!_doOpen.Check(_isNc ? true : false, out reason))
 
-                                 EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Close", ":Failed for interlock " + reason);
 
-                             else
 
-                                 EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Close", $":Valve {Name} keep open");
 
-                         }
 
-                     }
 
-                     _operation = SetPoint;
 
-                 }
 
-                 else if (_timer.IsIdle() && _doOpen != null)
 
-                 {
 
-                     eventTrigger.CLK = SetPoint != _operation;   // fire event only check at first, SetPoint set by interlock
 
-                     if (eventTrigger.Q)
 
-                     {
 
-                         if (_operation)
 
-                         {
 
-                             string reason;
 
-                             if (!_doOpen.Check(_isNc ? true : false, out reason))
 
-                                 EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1},Reason:{2}", Display, "Close", reason));
 
-                             else
 
-                                 EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1},Reason {2}", Display, "Close", "PLC kept"));
 
-                         }
 
-                         else
 
-                         {
 
-                             string reason;
 
-                             if (!_doOpen.Check(_isNc ? true : false, out reason))
 
-                                 EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1},Reason:{2}", Display, "Open", reason));
 
-                             else
 
-                                 EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1},Reason {2}", Display, "Open", "PLC Kept"));
 
-                         }
 
-                         _operation = SetPoint;
 
-                     }
 
-                 }
 
-             }
 
-             catch (Exception ex)
 
-             {
 
-                 LOG.Write(ex);
 
-             }
 
-             if (ILKDiSensor == null)
 
-             {
 
-                 IsILKOK = true;
 
-             }
 
-             else
 
-             {
 
-                 if (!_isNc)
 
-                 {
 
-                     IsILKOK = true;
 
-                     return;
 
-                 }
 
-                 if (ILKDiSensor.Value)
 
-                 {
 
-                     IsILKOK = true;
 
-                 }
 
-                 else if (!ILKDiSensor.Value)
 
-                 {
 
-                     if (!IsILKNot)
 
-                     {
 
-                         IsILKOK = false;
 
-                     }
 
-                 }
 
-             }
 
-             //ILK判断
 
-             if (_ilkDelayTimer.IsRunning)
 
-             {
 
-                 if (_ilkDelayTimer.ElapsedMilliseconds >= _ilkDelayTimeInMS)
 
-                 {
 
-                     _ilkDelayTimer.Stop();
 
-                     _ilkDelayTimer.Reset();
 
-                     if (_doOpen != null)
 
-                     {
 
-                         if (!ILKDiSensor.Value)
 
-                         {
 
-                             InterlockAlarm.Set($"interlock delay ends. The value of the {Name} valve is incorrect.");
 
-                            // EV.PostWarningLog(Module,  $"interlock delay ends. The value of the {Name} valve is incorrect.");
 
-                             return;
 
-                         }
 
-                     }
 
-                 }
 
-                 else
 
-                 {
 
-                     return;
 
-                 }
 
-             }
 
-         }
 
-         public bool TurnValve(bool isOn, out string reason)
 
-         {
 
-             bool bValue = _isNc ? isOn : !isOn;
 
-             var stringOnOff = isOn ? "On" : "Off";
 
-             //EV.PostInfoLog(Module, $"Turn {Display} {stringOnOff}");
 
-             _writeLog = $"Turn {Display} {stringOnOff}";
 
-             reason = "";
 
-             _interlockDelayTimeInMS = 0;
 
-             _ilkDelayTimeInMS = 0;
 
-             //开始计时ILK
 
-             if (_interlockNoneOrExist != null && _interlockILKTime != null && _interlockNoneOrExist.BoolValue && bValue)
 
-             {
 
-                 var timeParas = _interlockILKTime.StringValue.Split(':');//00:00.0
 
-                 if (timeParas.Length > 1)
 
-                 {
 
-                     float.TryParse(timeParas[0], out float min);
 
-                     float.TryParse(timeParas[1], out float sec);
 
-                     _ilkDelayTimeInMS = min * 60 * 1000 + sec * 1000;
 
-                 }
 
-                 _ilkDelayTimer.Restart();
 
-                 _operation = bValue;
 
-                 if (_ilkDelayTimeInMS > 0)
 
-                 {
 
-                     _writeLog += $"ilk delay time={_ilkDelayTimeInMS} ms";
 
-                     _timer.Start(2000 + _ilkDelayTimeInMS);
 
-                 }
 
-             }
 
-             if (_interlockNoneOrExist != null && _interlockDelayOnTime != null && _interlockNoneOrExist.BoolValue && bValue)
 
-             {
 
-                 var timeParas = _interlockDelayOnTime.StringValue.Split(':');//00:00.0
 
-                 if (timeParas.Length > 1)
 
-                 {
 
-                     float.TryParse(timeParas[0], out float min);
 
-                     float.TryParse(timeParas[1], out float sec);
 
-                     _interlockDelayTimeInMS = min * 60 * 1000 + sec * 1000;
 
-                 }
 
-                 _interlockDelayTimer.Restart();
 
-                 _operation = bValue;
 
-                 if (_interlockDelayTimeInMS > 0)
 
-                 {
 
-                     _writeLog += $" delay time={_interlockDelayTimeInMS} ms";
 
-                     _timer.Start(2000 + _interlockDelayTimeInMS);
 
-                 }
 
-                 else
 
-                 {
 
-                      _timer.Start(2000);         //2 seconds to monitor
 
-                 }
 
-             }
 
-             else if (_interlockNoneOrExist != null && _interlockDelayOffTime != null && _interlockNoneOrExist.BoolValue && !bValue)
 
-             {
 
-                 var timeParas = _interlockDelayOffTime.StringValue.Split(':');//00:00.0
 
-                
 
-                 if (timeParas.Length > 1)
 
-                 {
 
-                     float.TryParse(timeParas[0], out float min);
 
-                     float.TryParse(timeParas[1], out float sec);
 
-                     _interlockDelayTimeInMS = min * 60 * 1000 + sec * 1000;
 
-                 }
 
-                 _interlockDelayTimer.Restart();
 
-                 _operation = bValue;
 
-                 if (_interlockDelayTimeInMS > 0)
 
-                 {
 
-                     _writeLog += $" delay time={_interlockDelayTimeInMS} ms";
 
-                     _timer.Start(2000 + _interlockDelayTimeInMS);
 
-                 }
 
-                 else
 
-                 {
 
-                      _timer.Start(2000);         //2 seconds to monitor
 
-                 }
 
-             }
 
-             else
 
-             {
 
-                 //正常开阀逻辑
 
-                 if (_doOpen != null)
 
-                 {
 
-                     if (!_doOpen.Check(bValue, out reason))
 
-                     {
 
-                         if (!_isNc && !IsILKNot)
 
-                         {
 
-                             IsILKOK = false;
 
-                         }
 
-                         _doOpen.SetValue(bValue, out reason, false);
 
-                         //EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, $"{(isOn ? "Open" : "Close")}", ":Failed for interlock " + reason); ;
 
-                         return false;
 
-                     }
 
-                 }
 
-                 if (_doClose != null)
 
-                 {
 
-                     if (!_doClose.Check(!bValue, out reason))
 
-                     {
 
-                         _doClose.SetValue(bValue, out reason, false);
 
-                         //EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, $"{(isOn ? "Open" : "Close")}", ":Failed for interlock " + reason); ;
 
-                         return false;
 
-                     }
 
-                 }
 
-                 SetPoint = isOn;
 
-                 _operation = isOn;
 
-                 _timer.Start(2000);         //2 seconds to monitor
 
-             }
 
-             return true;
 
-         }
 
-         public void Reset()
 
-         {
 
-             eventTrigger.RST = true;
 
-         }
 
-     }
 
- }
 
 
  |