Interlock.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.IO;
  6. using Aitex.Core.RT.Log;
  7. using Aitex.Core.RT.Event;
  8. namespace Aitex.Core.RT.IOCore
  9. {
  10. public class Interlock
  11. {
  12. string _errMessage;
  13. bool _isErr = false;
  14. object _locker = new object();
  15. Dictionary<IntlkNode, List<List<IntlkNode>>> _interlocks = new Dictionary<IntlkNode, List<List<IntlkNode>>>();
  16. List<SafePlcInterlockChecker> _safeDoList = new List<SafePlcInterlockChecker>();
  17. List<Tuple<int, int, string>> _diMap;
  18. List<Tuple<int, int, string>> _doMap;
  19. public Interlock()
  20. {
  21. }
  22. public void InitSafeDO(List<Tuple<DOAccessor, DIAccessor>> safedos)
  23. {
  24. lock (_locker)
  25. {
  26. _safeDoList.Clear();
  27. for (int i = 0; i < safedos.Count; i++)
  28. {
  29. _safeDoList.Add(new SafePlcInterlockChecker(safedos[i].Item1, safedos[i].Item2));
  30. }
  31. }
  32. }
  33. public Tuple<int, int, string> FindDI(int iodefineIndex)
  34. {
  35. return _diMap.Find(item => item.Item1 == iodefineIndex);
  36. }
  37. public Tuple<int, int, string> FindDO(int iodefineIndex)
  38. {
  39. return _doMap.Find(item => item.Item1 == iodefineIndex);
  40. }
  41. public bool Init(string cfgFile, List<Tuple<int, int, string>> diMap, List<Tuple<int, int, string>> doMap, out string reason)
  42. {
  43. _interlocks.Clear();
  44. _errMessage = "";
  45. _isErr = false;
  46. _diMap = diMap;
  47. _doMap = doMap;
  48. if (_isErr)
  49. _isErr = true;
  50. try
  51. {
  52. //read interlock table from config file
  53. //example: ADO100:ADI100,ADI50,ADO55,!DI34,!TDI34&!TDO33,DO345
  54. using (StreamReader sr = new StreamReader(cfgFile))
  55. {
  56. string strLine = sr.ReadLine();
  57. while (strLine != null)
  58. {
  59. strLine = strLine.TrimStart(' ', '\t');
  60. if (string.IsNullOrEmpty(strLine) || strLine[0] == '#')
  61. {
  62. strLine = sr.ReadLine();
  63. continue;
  64. }
  65. string[] str = strLine.Split(new char[] { ':', ',', ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
  66. IntlkNode key;
  67. ParseIO(str[0], out key);
  68. System.Diagnostics.Debug.Assert(!key.IoTypeIsDI);
  69. List<List<IntlkNode>> rules = new List<List<IntlkNode>>();
  70. for (int i = 1; i < str.Length; i++)
  71. {
  72. List<IntlkNode> singleRule = new List<IntlkNode>();
  73. string[] slist = str[i].Split(new char[] { '&' }, StringSplitOptions.RemoveEmptyEntries);
  74. foreach (var s1 in slist)
  75. {
  76. IntlkNode nd;
  77. ParseIO(s1, out nd);
  78. singleRule.Add(nd);
  79. }
  80. rules.Add(singleRule);
  81. }
  82. if (_interlocks.Keys.Contains(key))
  83. {
  84. _errMessage = string.Format("{0} is defined in more than one place.", str[0]);
  85. _isErr = true;
  86. //Notifier.PostEvent(EventEnum.InterlockFileBad, Reactor.PmName, _errMessage);
  87. reason = _errMessage;
  88. return false;
  89. }
  90. _interlocks.Add(key, rules);
  91. strLine = sr.ReadLine();
  92. }
  93. }
  94. //verify PmInterlock.cfg file
  95. foreach (var interlock in _interlocks)
  96. {
  97. var ruleHead = interlock.Key;
  98. if ((ruleHead.IoTypeIsDI && FindDI(ruleHead.Index)==null) ||
  99. (!ruleHead.IoTypeIsDI && FindDO(ruleHead.Index)==null))
  100. {
  101. _errMessage = string.Format("{0}-{1} not exist.", ruleHead.IoTypeIsDI ? "DI" : "DO", ruleHead.Index);
  102. _isErr = true;
  103. //Notifier.PostEvent(EventEnum.InterlockFileBad, Reactor.PmName, _errMessage);
  104. reason = _errMessage;
  105. return false;
  106. }
  107. foreach (var rule in interlock.Value)
  108. {
  109. foreach (var r1 in rule)
  110. {
  111. if ((r1.IoTypeIsDI && FindDI(r1.Index) == null) ||
  112. (!r1.IoTypeIsDI && FindDO(r1.Index) == null))
  113. {
  114. _errMessage = string.Format("{0}-{1} not exist", r1.IoTypeIsDI ? "DI" : "DO", r1.Index);
  115. _isErr = true;
  116. //Notifier.PostEvent(EventEnum.InterlockFileBad, Reactor.PmName, _errMessage);
  117. reason = _errMessage;
  118. return false;
  119. }
  120. }
  121. }
  122. }
  123. }
  124. catch (Exception ex)
  125. {
  126. _errMessage = ex.Message;
  127. _isErr = true;
  128. reason = _errMessage;
  129. LOG.Write(ex, string.Format("erro reading interlock config file {0}", cfgFile));
  130. return false;
  131. }
  132. reason = _errMessage;
  133. LOG.Write(string.Format("get {0} interlock items", _interlocks.Count));
  134. return true;
  135. }
  136. /// <summary>
  137. /// IO interlock monitoring
  138. /// </summary>
  139. public void Monitor()
  140. {
  141. //检查安全逻辑是否动作,如果有动作则撤销当前的DO设定值
  142. lock (_locker)
  143. {
  144. foreach (var safeItem in _safeDoList)
  145. {
  146. safeItem.Monitor();
  147. }
  148. }
  149. //监测当前软件DO点是否存在互锁情况
  150. foreach (var interlock in _interlocks)
  151. {
  152. try
  153. {
  154. var failReasonList = new List<List<IntlkNode>>();
  155. bool isTrigger = false;
  156. foreach (var rule in interlock.Value) //OR遍历
  157. {
  158. bool ret = true;
  159. foreach (var item in rule) //AND遍历
  160. {
  161. if (item.IoTypeIsDI && item.IsReversed)
  162. {
  163. ret &= (!IO.DI[FindDI(item.Index).Item3].RawData);
  164. }
  165. else if (item.IoTypeIsDI && !item.IsReversed)
  166. {
  167. ret &= IO.DI[FindDI(item.Index).Item3].RawData;
  168. }
  169. else if (!item.IoTypeIsDI && item.IsReversed)
  170. {
  171. ret &= (!IO.DO[FindDO(item.Index).Item3].Value);
  172. }
  173. else
  174. {
  175. ret &= IO.DO[FindDO(item.Index).Item3].Value;
  176. }
  177. }
  178. if (ret)
  179. {
  180. var andList = new List<IntlkNode>(rule);
  181. failReasonList.Add(andList);
  182. isTrigger = true;
  183. }
  184. }
  185. if (isTrigger)
  186. {
  187. if (IO.DO[FindDO(interlock.Key.Index).Item3].Value != interlock.Key.IsReversed)
  188. {
  189. string reason = string.Empty;
  190. reason = string.Format("DO-{0}({1}) can not match {2} interlock condition,force to {3}。",
  191. interlock.Key.Index,
  192. _doMap[interlock.Key.Index],
  193. failReasonList.Count,
  194. interlock.Key.IsReversed ? "ON" : "OFF");
  195. for (int i = 0; i < failReasonList.Count; i++)
  196. {
  197. reason += string.Format("{0}{1}. ", i == 0 ? "" : "\n", i + 1);
  198. for (int j = 0; j < failReasonList[i].Count; j++)
  199. {
  200. var it = failReasonList[i][j];
  201. reason += string.Format("{0}{1}-{2}({3})[{4}]",
  202. j == 0 ? "" : " & ",
  203. it.IoTypeIsDI ? "DI" : "DO",
  204. it.Index,
  205. it.IoTypeIsDI ? _diMap[it.Index] : _doMap[it.Index],
  206. (it.IoTypeIsDI ? IO.DI[FindDI(it.Index).Item3].Value : IO.DO[FindDO(it.Index).Item3].Value) ? "ON" : "OFF");
  207. }
  208. }
  209. //将DO进行强制操作
  210. string reason1;
  211. IO.DO[FindDO(interlock.Key.Index).Item3].SetValue(interlock.Key.IsReversed, out reason1);
  212. //将互锁消息发送给用户界面
  213. EV.PostMessage("IO", EventEnum.SwInterlock, "IO", reason);
  214. }
  215. }
  216. }
  217. catch (Exception ex)
  218. {
  219. LOG.Write(ex);
  220. }
  221. }
  222. }
  223. public bool CanSetDo(string doName, bool onOff, out string reason)
  224. {
  225. foreach (var item in _doMap)
  226. {
  227. if (item.Item3 == doName)
  228. return CanSetDo(item.Item1, onOff, out reason);
  229. }
  230. throw new ApplicationException("Can not find DO:"+doName);
  231. }
  232. public bool CanSetDo(int doIndex, bool onOff, out string reason)
  233. {
  234. reason = string.Empty;
  235. bool isTrigger = false;
  236. var failReasonList = new List<List<IntlkNode>>();
  237. foreach (var interlock in _interlocks)
  238. {
  239. if (!(interlock.Key.Index == doIndex &&
  240. interlock.Key.IoTypeIsDI == false))
  241. {
  242. continue;
  243. }
  244. if (onOff == interlock.Key.IsReversed)
  245. {
  246. //return true;
  247. continue;
  248. }
  249. foreach (var rule in interlock.Value)
  250. {
  251. bool ret = true;
  252. foreach (var item in rule)
  253. {
  254. if (item.IoTypeIsDI && item.IsReversed)
  255. {
  256. ret &= (!IO.DI[FindDI(item.Index).Item3].RawData);
  257. }
  258. else if (item.IoTypeIsDI && !item.IsReversed)
  259. {
  260. ret &= IO.DI[FindDI(item.Index).Item3].RawData;
  261. }
  262. else if (!item.IoTypeIsDI && item.IsReversed)
  263. {
  264. ret &= (!IO.DO[FindDO(item.Index).Item3].Value);
  265. }
  266. else
  267. {
  268. ret &= IO.DO[FindDO(item.Index).Item3].Value;
  269. }
  270. }
  271. if (ret)
  272. {
  273. var andList = new List<IntlkNode>(rule);
  274. failReasonList.Add(andList);
  275. isTrigger = true;
  276. }
  277. }
  278. }
  279. if (isTrigger)
  280. {
  281. //1. DI-1(Earthquake)[OFF] & DI-12(Smoke)[ON]
  282. //2. DO-105(ServiceMode)[ON]
  283. for (int i = 0; i < failReasonList.Count; i++)
  284. {
  285. reason += string.Format("{0}{1}. ", i == 0 ? "" : "\n", i + 1);
  286. for (int j = 0; j < failReasonList[i].Count; j++)
  287. {
  288. var it = failReasonList[i][j];
  289. reason += string.Format("{0}{1}-{2}({3})[{4}]",
  290. j == 0 ? "" : " & ",
  291. it.IoTypeIsDI ? "DI" : "DO",
  292. it.Index,
  293. it.IoTypeIsDI ? _diMap[it.Index] : _doMap[it.Index],
  294. (it.IoTypeIsDI ? IO.DI[FindDI(it.Index).Item3].Value : IO.DO[FindDO(it.Index).Item3].Value) ? "ON" : "OFF");
  295. }
  296. }
  297. }
  298. return !isTrigger;
  299. }
  300. void ParseIO(string ioName, out IntlkNode interlockNode)
  301. {
  302. interlockNode = new IntlkNode();
  303. ioName = ioName.Trim();
  304. if (ioName[0] == '!')
  305. {
  306. ioName = ioName.Remove(0, 1);
  307. interlockNode.IsReversed = true;
  308. }
  309. else
  310. {
  311. interlockNode.IsReversed = false;
  312. }
  313. switch (ioName.Substring(0, 2))
  314. {
  315. case "DO":
  316. interlockNode.IoTypeIsDI = false;
  317. break;
  318. case "DI":
  319. interlockNode.IoTypeIsDI = true;
  320. break;
  321. default:
  322. throw new Exception("interlock file found error, the io name should be DO or DI leading" + ioName);
  323. }
  324. ioName = ioName.Remove(0, 2);
  325. interlockNode.Index = Convert.ToInt32(ioName);
  326. if (interlockNode.Index < 0 && interlockNode.Index >= 1000)
  327. {
  328. throw new Exception(string.Format("{0} index out of range", ioName));
  329. }
  330. }
  331. }
  332. }