using System; using System.Collections.Generic; using System.Diagnostics; using Venus_RT.Modules; using MECF.Framework.Common.Communications; using MECF.Framework.Common.Equipment; using Venus_Core; using Aitex.Core.RT.SCCore; using Aitex.Core.RT.Event; using Aitex.Core.RT.Device; using Aitex.Core.RT.Log; using System.Threading.Tasks; using System.Collections.Concurrent; using Aitex.Core.Common.DeviceData; using MECF.Framework.Common.CommonData.DeviceData; using Aitex.Core.RT.DataCenter; using Venus_RT.Modules.PMs; using System.Linq; namespace Venus_RT.Devices { class PendulumValve : IDevice { public enum Operation { SetPosition, GetPositionSP, Hold, OpenValve, CloseValve, SetPressure, GetPressureSP, GetAssembly, GetPosition, GetPressure, GetSensor1Data, GetSensor2Data, GetPressureCtrlStatus, GetDeviceStatus, GetWarnings, GetSensorOffset, GetSensor1Offset, GetSensor2Offset, GetLearnStatus, GetLearnPressureLimit, GetErrorStatus, GetFatalErrorStatus, GetThrottleCycleCounter, GetIsolationCycleCounter, GetPowerUpCounter, GetHardwareConfiguration, GetFirmwareConfiguration, GetIdentification, SetAccessMode, SetInterfaceConfiguration, SetValveConfiguration, SetSensorConfiguration, SetRangeConfiguration, SetZero, SetPressureAlignment, SetLearn, DownloadLearnData, UploadLearnData, SetPIDConfiguration, GetPIDConfiguration, SetValveSpeed, GetValveSpeed, Reset, Invalid, SetP, SetI, GetP, GetI } private readonly Dictionary _noneParaCommandOp = new Dictionary { {Operation.GetPositionSP, "i:38" }, {Operation.Hold, "H:" }, {Operation.CloseValve, "C:" }, {Operation.OpenValve, "O:" }, {Operation.GetPressureSP, "i:38" }, {Operation.GetAssembly, "i:76" }, {Operation.GetPosition, "A:" }, {Operation.GetPressure, "P:" }, {Operation.GetSensor1Data, "i:64" }, {Operation.GetSensor2Data, "i:65" }, {Operation.GetPressureCtrlStatus, "i:36" }, {Operation.GetDeviceStatus, "i:30" }, {Operation.GetWarnings, "i:35" }, {Operation.GetSensorOffset, "i:62" }, {Operation.GetSensor1Offset, "i:60" }, {Operation.GetSensor2Offset, "i:61" }, {Operation.GetLearnStatus, "i:32" }, {Operation.GetLearnPressureLimit, "i:34" }, {Operation.GetErrorStatus, "i:52" }, {Operation.GetFatalErrorStatus, "i:50" }, {Operation.GetThrottleCycleCounter, "i:70" }, {Operation.GetIsolationCycleCounter, "i:71" }, {Operation.GetPowerUpCounter, "i:72" }, {Operation.GetHardwareConfiguration, "i:80" }, {Operation.GetFirmwareConfiguration, "i:82" }, {Operation.GetIdentification, "i:83" }, {Operation.GetP, "i:02B04" }, {Operation.GetI, "i:02B05" }, }; private readonly Dictionary _singleParaCommandOp = new Dictionary { {Operation.SetPosition, "R:{0:D6}" }, {Operation.SetPressure, "S:{0:D8}" }, {Operation.SetAccessMode, "c:01{0:D2}" }, {Operation.SetInterfaceConfiguration, "s:20{0:D8}" }, {Operation.SetValveConfiguration, "s:04{0:D2}000000" }, {Operation.SetSensorConfiguration, "s:01{0:D8}" }, {Operation.SetRangeConfiguration, "s:21{0:D8}" }, {Operation.SetPressureAlignment, "c:6002{0:D8}" }, {Operation.SetLearn, "L:{0:D8}" }, {Operation.UploadLearnData, "u:{0:D3}" }, {Operation.SetPIDConfiguration, "s:02{0:D8}" }, {Operation.SetValveSpeed, "V:00{0:D4}" }, {Operation.SetP, "s:02B04{0}" }, {Operation.SetI, "s:02B05{0}" } }; private readonly Dictionary _twoParaCommandOp = new Dictionary { {Operation.DownloadLearnData, "d:{0:D3}{1:D8}" } }; private readonly Dictionary _deviceError = new Dictionary { {"000001", "Parity error" }, {"000002", "Input buffer overflow (to many characters) " }, {"000003", "Framing error (data length, number of stop bits)" }, {"000010", " or missing" }, {"000011", ": missing" }, {"000012", "Invalid number of characters (between : and [CR][LF])" }, {"000020", "Unknown command" }, {"000021", "Unknown command" }, {"000022", "Invalid value" }, {"000023", "Invalid value" }, {"000030", "Value out of range" }, {"000041", "Command not applicable for hardware configuration" }, {"000060", "ZERO disabled" }, {"000080", "Command not accepted due to local operation" }, {"000082", "Command not accepted due to synchronization, CLOSED or OPEN by digital input, safety mode or fatal error" }, }; public string Module { get; set; } public string Name { get; set; } public float Pressure { get; private set; } public float Position { get; private set; } public string Status { get; private set; } public bool IsOpen { get; private set; } private readonly string EOF = "\r\n"; private readonly int _readInterval = 100; private readonly int _position_unit = 100; private readonly float _pressure_ful_range = 500; private readonly int _foreLinePressureLimit = 750; private readonly int _chamberPressureLimit = 600; private readonly int _turboPumpSpeedLimit = 100; private int _queryFlag = 0; private readonly AsyncSerialPort _serial; private Stopwatch _queryWatch = new Stopwatch(); private string _lastAlarmString = string.Empty; private Operation[] _querys = new Operation[] { Operation.GetPressure, Operation.GetPosition, Operation.GetDeviceStatus, Operation.GetP, Operation.GetI }; BlockingCollection blockingCollection = new BlockingCollection(); private JetChamber m_JetChamber; private PressureType m_PressureType; public float PValue; public float IValue; private float m_PressureSetPoint; private float m_PositionSetPoint; public AITPendulumValveData DeviceData { get { AITPendulumValveData deviceData = new AITPendulumValveData { DeviceName = Name, Module = Module, Pressure = Pressure, IsOpen = IsOpen || Position > 0, Position = IsOpen ? Position : 0, PValue = PValue, IValue = IValue, PressureSetPoint= IsOpen ? m_PressureSetPoint : 0, PositionSetPoint= IsOpen ? m_PositionSetPoint : 0 }; return deviceData; } } public PendulumValve(ModuleName mod,PressureType pressureType) { Name = VenusDevice.PendulumValve.ToString(); Module = mod.ToString(); var _PortNum = SC.GetStringValue($"{mod}.PendulumValve.Port"); _serial = new AsyncSerialPort(_PortNum, 9600, 8, System.IO.Ports.Parity.None, System.IO.Ports.StopBits.One, EOF); _pressure_ful_range = SC.GetValue($"{mod}.PendulumValve.PressureFullRange"); _foreLinePressureLimit = SC.GetValue($"{mod}.PendulumValve.ForelinePressureLimit"); _chamberPressureLimit = SC.GetValue($"{mod}.PendulumValve.ChamberPressureLimit"); _turboPumpSpeedLimit = SC.GetValue($"{mod}.PendulumValve.TurboPumpSpeedLimit"); IsOpen = false; Task.Run(() => { foreach (var data in blockingCollection.GetConsumingEnumerable()) { _serial?.Write(data); System.Threading.Thread.Sleep(100); } }); m_PressureType = pressureType; m_JetChamber = (JetChamber)SC.GetValue($"{mod}.ChamberType"); } public bool Initialize() { DATA.Subscribe($"{Module}.{Name}.DeviceData", () => DeviceData); if (!_serial.Open()) { _noRepeatAlarm("Pendulum Valve 串口无法打开"); return false; } _serial.OnDataChanged += OnPortDataChanged; _serial.OnErrorHappened += OnErrorOccurred; _queryWatch.Restart(); return true; } public void Monitor() { if (_queryWatch.ElapsedMilliseconds > _readInterval && _querys.Length>0) { SendCommand(_querys[_queryFlag++ % _querys.Length]); _queryWatch.Restart(); } //var isopen = _serial.IsOpen(); } public void Reset() { } public void Terminate() { if (IsOpen) { TurnValve(false); } _serial?.Close(); } private void OnErrorOccurred(string obj) { _noRepeatAlarm($"[{Module}] VAT Pendulum Valve error: [{obj}]"); } private void OnPortDataChanged(string obj) { if (string.IsNullOrEmpty(obj)) { _noRepeatAlarm("VAT Pendulum Valve receive empty message"); return; } try { var data = obj.TrimEnd().Split(':'); switch (data[0]) { case "P": { Double pressure; if (Double.TryParse(data[1], out pressure)) { if ((m_JetChamber == JetChamber.Kepler2200A || m_JetChamber == JetChamber.Kepler2200B)) { Pressure = Convert.ToSingle(ConvertPressureUnit.ConvertPaTomtorr(pressure * _pressure_ful_range / 1000000)); //Pressure = pressure * _pressure_ful_range / 1000000; } else if (m_JetChamber == JetChamber.VenusSE || m_JetChamber == JetChamber.VenusDE) { Pressure = Convert.ToSingle(pressure) / 100; } else { Pressure = Convert.ToSingle(pressure) * _pressure_ful_range / 1000000; } } } break; case "A": { float position; if (float.TryParse(data[1], out position)) Position = position / _position_unit; } break; case "i": { _tryParseInqueryData(obj); } break; case "C": IsOpen = false; break; case "O": IsOpen = true; break; case "H": case "R": case "S": break; case "E": { _noRepeatAlarm($"[{Module}] VAT Pendulum Valve device error: {_deviceError[data[1]]}"); } break; default: { _noRepeatAlarm($"VAT Pendulum Valve: unrecognized received data: {obj}"); break; } } } catch (Exception ex) { _noRepeatAlarm($"[{Module}] VAT Pendulum Valve error: [{ex.Message}], Data: {obj}"); } } private bool SendCommand(Operation op) { if (_noneParaCommandOp.ContainsKey(op)) { blockingCollection.Add(_noneParaCommandOp[op] + EOF); return true; } else { return false; } } private bool SendCommand(Operation op, int data) { if (_singleParaCommandOp.ContainsKey(op)) { var cmd = string.Format(_singleParaCommandOp[op], data) + EOF; return _sendCmd(cmd); } else { _noRepeatAlarm("This VAT Pendulum Valve command need 1 data"); return false; } } private bool SendCommand(Operation op, float data) { if (_singleParaCommandOp.ContainsKey(op)) { var cmd = string.Format(_singleParaCommandOp[op], data) + EOF; return _sendCmd(cmd); } else { _noRepeatAlarm("This VAT Pendulum Valve command need 1 data"); return false; } } private bool _sendCmd(string cmd) { blockingCollection.Add(cmd); return true; //return _serial.Write(cmd); } private void _noRepeatAlarm(string alarm) { if (_lastAlarmString != alarm) { _lastAlarmString = alarm; LOG.Write(eEvent.ERR_PENDULUM_VALVE, ModuleHelper.Converter(Module), alarm); EV.PostAlarmLog(Module, alarm); } } private bool _tryParseInqueryData(string data) { var cmdPrix = data.Substring(0, 4); if (cmdPrix == "i:02") { var pi = data.Substring(5, 2); if (pi == "04") { float s; float.TryParse(data.Substring(7, data.Length - 9), out s); PValue = s; } else if (pi == "05") { float s; float.TryParse(data.Substring(7, data.Length - 9), out s); IValue = s; } } else { Operation oper = Operation.Invalid; foreach (var item in _noneParaCommandOp) { if (item.Value == cmdPrix) { oper = item.Key; break; } } if (oper == Operation.Invalid) { return false; } switch (oper) { case Operation.GetDeviceStatus: { Status = data.Substring(4, 8); if (Status[1] == 'E') { // Fatal Error _noRepeatAlarm($"Device Status error:{Status}"); } else if (Status[3] == '1') { // Warning Present _noRepeatAlarm($"Device Warning Present:{Status}"); } else if (Status[1] == '3') { IsOpen = false; } else if (Status[1] == '4') { IsOpen = true; } } break; case Operation.GetPressureSP: case Operation.GetPositionSP: { string Pressure = data.Substring(4, 8); //LOG.Write(eEvent.EV_DEVICE_INFO, Module, $" PV Pressure SetPoint: {Pressure}"); } break; default: break; } } return true; } public bool SetPosition(float postion) { if (_CheckStatus()) { m_PositionSetPoint = postion; float setPosition = (float)Math.Round(postion, 1); return SendCommand(Operation.SetPosition, (int)(setPosition * _position_unit)); } return false; } public bool SetPressure(float pressure) { if (_CheckStatus()) { m_PressureSetPoint = pressure; if (m_PressureType == PressureType.Pa) { return SendCommand(Operation.SetPressure, Convert.ToInt32(pressure * 1000000 / ConvertPressureUnit.ConvertPaTomtorr(_pressure_ful_range))); } else { return SendCommand(Operation.SetPressure, Convert.ToInt32(pressure * 1000000 / _pressure_ful_range)); } } return false; } public bool Hold() { return SendCommand(Operation.Hold); } public bool TurnValve(bool on) { if (on == false) { return SendCommand(on ? Operation.OpenValve : Operation.CloseValve); } else { if (_CheckStatus(on)) { return SendCommand(on ? Operation.OpenValve : Operation.CloseValve); } } return false; } public bool SetPValue(float pValue) { return SendCommand(Operation.SetP, pValue); } public bool SetIValue(float pValue) { return SendCommand(Operation.SetI, pValue); } bool _CheckStatus(bool bTurnOn = false) { if (Status != null && Status[1] == 'E') { _noRepeatAlarm($"PendulumValve is error status, can do turn on/off operation."); return false; } var _chamber = DEVICE.GetDevice(Module); if (bTurnOn && !_chamber.IsTurboPumpAtSpeed) { _noRepeatAlarm($"Turbo Pump not at speed, can not turn on pendulum valve."); return false; } if (_chamber.ForelinePressure > _foreLinePressureLimit) { LOG.Write(eEvent.ERR_DEVICE_INFO, Module, $"Foreline Pressure:{_chamber.ForelinePressure} is higher than {_foreLinePressureLimit}{m_PressureType}, can not turn on pendulum valve."); return false; } if (_chamber.ChamberPressure > _chamberPressureLimit && _chamber.TurboPumpSpeed > _turboPumpSpeedLimit) { LOG.Write(eEvent.ERR_DEVICE_INFO, Module, $"Chamber Pressure:{_chamber.ChamberPressure} is higher than {_chamberPressureLimit}{m_PressureType} and Chamber.TurboPumpSpeed is higher than {_turboPumpSpeedLimit}, can not turn on pendulum valve."); return false; } if (_chamber.IsTurboPumpRunning) { if (!_chamber.IsISOOpen) { LOG.Write(eEvent.ERR_DEVICE_INFO, Module, $"Chamber TurboPumpingvalve is not open, can not turn on pendulum valve."); return false; } } return true; } public bool ReConnect() { return _serial.ReConnect(); } } }