using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using Aitex.Core.RT.Log; using Aitex.Core.RT.Event; namespace Aitex.Core.RT.IOCore { public class Interlock { string _errMessage; bool _isErr = false; object _locker = new object(); Dictionary>> _interlocks = new Dictionary>>(); List _safeDoList = new List(); List> _diMap; List> _doMap; public Interlock() { } public void InitSafeDO(List> safedos) { lock (_locker) { _safeDoList.Clear(); for (int i = 0; i < safedos.Count; i++) { _safeDoList.Add(new SafePlcInterlockChecker(safedos[i].Item1, safedos[i].Item2)); } } } public Tuple FindDI(int iodefineIndex) { return _diMap.Find(item => item.Item1 == iodefineIndex); } public Tuple FindDO(int iodefineIndex) { return _doMap.Find(item => item.Item1 == iodefineIndex); } public bool Init(string cfgFile, List> diMap, List> doMap, out string reason) { _interlocks.Clear(); _errMessage = ""; _isErr = false; _diMap = diMap; _doMap = doMap; if (_isErr) _isErr = true; try { //read interlock table from config file //example: ADO100:ADI100,ADI50,ADO55,!DI34,!TDI34&!TDO33,DO345 using (StreamReader sr = new StreamReader(cfgFile)) { string strLine = sr.ReadLine(); while (strLine != null) { strLine = strLine.TrimStart(' ', '\t'); if (string.IsNullOrEmpty(strLine) || strLine[0] == '#') { strLine = sr.ReadLine(); continue; } string[] str = strLine.Split(new char[] { ':', ',', ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); IntlkNode key; ParseIO(str[0], out key); System.Diagnostics.Debug.Assert(!key.IoTypeIsDI); List> rules = new List>(); for (int i = 1; i < str.Length; i++) { List singleRule = new List(); string[] slist = str[i].Split(new char[] { '&' }, StringSplitOptions.RemoveEmptyEntries); foreach (var s1 in slist) { IntlkNode nd; ParseIO(s1, out nd); singleRule.Add(nd); } rules.Add(singleRule); } if (_interlocks.Keys.Contains(key)) { _errMessage = string.Format("{0} is defined in more than one place.", str[0]); _isErr = true; //Notifier.PostEvent(EventEnum.InterlockFileBad, Reactor.PmName, _errMessage); reason = _errMessage; return false; } _interlocks.Add(key, rules); strLine = sr.ReadLine(); } } //verify PmInterlock.cfg file foreach (var interlock in _interlocks) { var ruleHead = interlock.Key; if ((ruleHead.IoTypeIsDI && FindDI(ruleHead.Index)==null) || (!ruleHead.IoTypeIsDI && FindDO(ruleHead.Index)==null)) { _errMessage = string.Format("{0}-{1} not exist.", ruleHead.IoTypeIsDI ? "DI" : "DO", ruleHead.Index); _isErr = true; //Notifier.PostEvent(EventEnum.InterlockFileBad, Reactor.PmName, _errMessage); reason = _errMessage; return false; } foreach (var rule in interlock.Value) { foreach (var r1 in rule) { if ((r1.IoTypeIsDI && FindDI(r1.Index) == null) || (!r1.IoTypeIsDI && FindDO(r1.Index) == null)) { _errMessage = string.Format("{0}-{1} not exist", r1.IoTypeIsDI ? "DI" : "DO", r1.Index); _isErr = true; //Notifier.PostEvent(EventEnum.InterlockFileBad, Reactor.PmName, _errMessage); reason = _errMessage; return false; } } } } } catch (Exception ex) { _errMessage = ex.Message; _isErr = true; reason = _errMessage; LOG.Write(ex, string.Format("erro reading interlock config file {0}", cfgFile)); return false; } reason = _errMessage; LOG.Write(string.Format("get {0} interlock items", _interlocks.Count)); return true; } /// /// IO interlock monitoring /// public void Monitor() { //检查安全逻辑是否动作,如果有动作则撤销当前的DO设定值 lock (_locker) { foreach (var safeItem in _safeDoList) { safeItem.Monitor(); } } //监测当前软件DO点是否存在互锁情况 foreach (var interlock in _interlocks) { try { var failReasonList = new List>(); bool isTrigger = false; foreach (var rule in interlock.Value) //OR遍历 { bool ret = true; foreach (var item in rule) //AND遍历 { if (item.IoTypeIsDI && item.IsReversed) { ret &= (!IO.DI[FindDI(item.Index).Item3].RawData); } else if (item.IoTypeIsDI && !item.IsReversed) { ret &= IO.DI[FindDI(item.Index).Item3].RawData; } else if (!item.IoTypeIsDI && item.IsReversed) { ret &= (!IO.DO[FindDO(item.Index).Item3].Value); } else { ret &= IO.DO[FindDO(item.Index).Item3].Value; } } if (ret) { var andList = new List(rule); failReasonList.Add(andList); isTrigger = true; } } if (isTrigger) { if (IO.DO[FindDO(interlock.Key.Index).Item3].Value != interlock.Key.IsReversed) { string reason = string.Empty; reason = string.Format("DO-{0}({1}) can not match {2} interlock condition,force to {3}。", interlock.Key.Index, _doMap[interlock.Key.Index], failReasonList.Count, interlock.Key.IsReversed ? "ON" : "OFF"); for (int i = 0; i < failReasonList.Count; i++) { reason += string.Format("{0}{1}. ", i == 0 ? "" : "\n", i + 1); for (int j = 0; j < failReasonList[i].Count; j++) { var it = failReasonList[i][j]; reason += string.Format("{0}{1}-{2}({3})[{4}]", j == 0 ? "" : " & ", it.IoTypeIsDI ? "DI" : "DO", it.Index, it.IoTypeIsDI ? _diMap[it.Index] : _doMap[it.Index], (it.IoTypeIsDI ? IO.DI[FindDI(it.Index).Item3].Value : IO.DO[FindDO(it.Index).Item3].Value) ? "ON" : "OFF"); } } //将DO进行强制操作 string reason1; IO.DO[FindDO(interlock.Key.Index).Item3].SetValue(interlock.Key.IsReversed, out reason1); //将互锁消息发送给用户界面 EV.PostMessage("IO", EventEnum.SwInterlock, "IO", reason); } } } catch (Exception ex) { LOG.Write(ex); } } } public bool CanSetDo(string doName, bool onOff, out string reason) { foreach (var item in _doMap) { if (item.Item3 == doName) return CanSetDo(item.Item1, onOff, out reason); } throw new ApplicationException("Can not find DO:"+doName); } public bool CanSetDo(int doIndex, bool onOff, out string reason) { reason = string.Empty; bool isTrigger = false; var failReasonList = new List>(); foreach (var interlock in _interlocks) { if (!(interlock.Key.Index == doIndex && interlock.Key.IoTypeIsDI == false)) { continue; } if (onOff == interlock.Key.IsReversed) { //return true; continue; } foreach (var rule in interlock.Value) { bool ret = true; foreach (var item in rule) { if (item.IoTypeIsDI && item.IsReversed) { ret &= (!IO.DI[FindDI(item.Index).Item3].RawData); } else if (item.IoTypeIsDI && !item.IsReversed) { ret &= IO.DI[FindDI(item.Index).Item3].RawData; } else if (!item.IoTypeIsDI && item.IsReversed) { ret &= (!IO.DO[FindDO(item.Index).Item3].Value); } else { ret &= IO.DO[FindDO(item.Index).Item3].Value; } } if (ret) { var andList = new List(rule); failReasonList.Add(andList); isTrigger = true; } } } if (isTrigger) { //1. DI-1(Earthquake)[OFF] & DI-12(Smoke)[ON] //2. DO-105(ServiceMode)[ON] for (int i = 0; i < failReasonList.Count; i++) { reason += string.Format("{0}{1}. ", i == 0 ? "" : "\n", i + 1); for (int j = 0; j < failReasonList[i].Count; j++) { var it = failReasonList[i][j]; reason += string.Format("{0}{1}-{2}({3})[{4}]", j == 0 ? "" : " & ", it.IoTypeIsDI ? "DI" : "DO", it.Index, it.IoTypeIsDI ? _diMap[it.Index] : _doMap[it.Index], (it.IoTypeIsDI ? IO.DI[FindDI(it.Index).Item3].Value : IO.DO[FindDO(it.Index).Item3].Value) ? "ON" : "OFF"); } } } return !isTrigger; } void ParseIO(string ioName, out IntlkNode interlockNode) { interlockNode = new IntlkNode(); ioName = ioName.Trim(); if (ioName[0] == '!') { ioName = ioName.Remove(0, 1); interlockNode.IsReversed = true; } else { interlockNode.IsReversed = false; } switch (ioName.Substring(0, 2)) { case "DO": interlockNode.IoTypeIsDI = false; break; case "DI": interlockNode.IoTypeIsDI = true; break; default: throw new Exception("interlock file found error, the io name should be DO or DI leading" + ioName); } ioName = ioName.Remove(0, 2); interlockNode.Index = Convert.ToInt32(ioName); if (interlockNode.Index < 0 && interlockNode.Index >= 1000) { throw new Exception(string.Format("{0} index out of range", ioName)); } } } }