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; 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, } 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" }, }; 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}" } }; 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 int Pressure { get; private set; } public int 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 = 500; 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 }; public PendulumValve(ModuleName mod) { Name = VenusDevice.PendulumValve.ToString(); Module = mod.ToString(); var _PortNum = SC.GetStringValue($"{mod}.PendulumValve.Port"); _serial = new AsyncSerialPort(_PortNum, 9600, 7, System.IO.Ports.Parity.Even, System.IO.Ports.StopBits.One, EOF); IsOpen = false; } public bool Initialize() { 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) { SendCommand(_querys[_queryFlag++ % 3]); _queryWatch.Restart(); } } public void Reset() { } public void Terminate() { _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": { int pressure; if (int.TryParse(data[1], out pressure)) Pressure = pressure; } break; case "A": { int position; if (int.TryParse(data[1], out position)) Position = position; } break; case "i": { _tryParseInqueryData(obj); } break; case "C": case "O": case "H": 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)) { return _serial.Write(_noneParaCommandOp[op] + EOF); } 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 _sendCmd(string cmd) { 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); 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}"); } } break; default: break; } return true; } public bool SetPosition(int postion) { if(_CheckStatus()) { IsOpen = postion > 0; return SendCommand(Operation.SetPosition, postion); } return false; } public bool SetPressure(int pressure) { if(_CheckStatus()) { IsOpen = true; return SendCommand(Operation.SetPressure, pressure); } return false; } public bool Hold() { return SendCommand(Operation.Hold); } public bool TurnValve(bool on) { if(_CheckStatus(on)) { IsOpen = on; return SendCommand(on ? Operation.OpenValve : Operation.CloseValve); } return false; } 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 > 500) { _noRepeatAlarm($"Foreline Pressure:{_chamber.ForelinePressure} is too high, can not turn on pendulum valve."); return false; } return true; } } }