TwincatIO.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. using Aitex.Core.RT.IOCore;
  2. using Aitex.Core.RT.Log;
  3. using Aitex.Core.RT.SCCore;
  4. using MECF.Framework.Common.Equipment;
  5. using MECF.Framework.Common.SCCore;
  6. using MECF.Framework.Common.Twincat;
  7. using MECF.Framework.RT.Core.IoProviders;
  8. using System;
  9. using System.Collections.Concurrent;
  10. using System.Collections.Generic;
  11. using System.Diagnostics;
  12. using System.Reflection;
  13. using System.Threading;
  14. using System.Xml;
  15. namespace Venus_RT.Devices
  16. {
  17. public static class TwinCatMananger
  18. {
  19. private static Dictionary<ModuleName, TwincatIO> _mod2twio = new Dictionary<ModuleName, TwincatIO>();
  20. public static void AddTwinCatWithModule(ModuleName mod, TwincatIO twio)
  21. {
  22. Debug.Assert(!_mod2twio.ContainsKey(mod));
  23. _mod2twio.Add(mod, twio);
  24. }
  25. public static TwincatIO GetTwinCatByModuleName(ModuleName mod)
  26. {
  27. Debug.Assert(_mod2twio.ContainsKey(mod));
  28. return _mod2twio[mod];
  29. }
  30. }
  31. public class TwincatIO : IoProvider
  32. {
  33. #region 内部变量
  34. private string _prefix = "MAIN";
  35. private string _structName = "PcComm";
  36. /// <summary>
  37. /// 写入队列
  38. /// </summary>
  39. private ConcurrentQueue<(string name, object value)> _writeQueue = new ConcurrentQueue<(string, object)>();
  40. /// <summary>
  41. /// 写入线程
  42. /// </summary>
  43. private Thread _writeThread = null;
  44. /// <summary>
  45. /// 是否正在运行
  46. /// </summary>
  47. private bool _isRunning = false;
  48. /// <summary>
  49. /// Ado通信对象
  50. /// </summary>
  51. private TwincatAdoManager _adoManager = null;
  52. #endregion
  53. /// <summary>
  54. /// 初始化
  55. /// </summary>
  56. /// <param name="module"></param>
  57. /// <param name="name"></param>
  58. /// <param name="lstBuffers"></param>
  59. /// <param name="buffer"></param>
  60. /// <param name="nodeParameter"></param>
  61. /// <param name="ioMappingPathFile"></param>
  62. /// <param name="ioModule"></param>
  63. public override void Initialize(string module, string name, List<IoBlockItem> lstBuffers, IIoBuffer buffer, XmlElement nodeParameter, string ioMappingPathFile, string ioModule)
  64. {
  65. Module = module;
  66. Name = name;
  67. _adoManager = new TwincatAdoManager();
  68. TwincatAdoObjectManager.Instance.InitializeModuleAdoManager(module, _adoManager);
  69. if (SC.ContainsItem("System.TwincatPrefix"))
  70. {
  71. _prefix = SC.GetStringValue("System.TwincatPrefix");
  72. }
  73. _source = module + "." + name;
  74. _buffer = buffer;
  75. _nodeParameter = nodeParameter;
  76. _blockSections = lstBuffers;
  77. SetIoMap(_source, 0, ioMappingPathFile, ioModule);
  78. string configMapPath = ioMappingPathFile.Substring(0, ioMappingPathFile.LastIndexOf('\\'))+"\\TwincatConfig.xml";
  79. //if (configMapPath != null)
  80. //{
  81. // TwincatConfigManager.Instance.SetConfigMap(configMapPath, ioModule);
  82. //}
  83. SetRecipe(_source,ioModule);
  84. SetParameter(nodeParameter);
  85. State = IoProviderStateEnum.Uninitialized;
  86. _writeThread = new Thread(new ThreadStart(WriteThreadCallBack));
  87. _writeThread.IsBackground = true;
  88. _isRunning = true;
  89. _writeThread.Start();
  90. TwinCatMananger.AddTwinCatWithModule(ModuleHelper.Converter(module), this);
  91. }
  92. protected override void SetParameter(XmlElement nodeParameter)
  93. {
  94. string strIp = nodeParameter.GetAttribute("ip");
  95. string strPort = nodeParameter.GetAttribute("port");
  96. int port = int.Parse(strPort);
  97. string ip = strIp;
  98. _adoManager.Initialize(ip, port);
  99. _adoManager.Start();
  100. }
  101. private void SetIoMap(string provider, int blockOffset, string xmlPathFile, string module = "")
  102. {
  103. XmlDocument xml = new XmlDocument();
  104. xml.Load(xmlPathFile);
  105. XmlNodeList lstDi = xml.SelectNodes("IO_DEFINE/Dig_In/DI_ITEM");
  106. XmlNodeList lstDo = xml.SelectNodes("IO_DEFINE/Dig_Out/DO_ITEM");
  107. XmlNodeList lstAo = xml.SelectNodes("IO_DEFINE/Ana_Out/AO_ITEM");
  108. XmlNodeList lstAi = xml.SelectNodes("IO_DEFINE/Ana_In/AI_ITEM");
  109. _buffer.SetBufferBlock(provider, lstDo.Count, lstDi.Count, lstAi.Count, lstAo.Count);
  110. var scConfig = SC.GetConfigItem("System.IsIgnoreSaveDB");
  111. var isIgnoreSaveDB = scConfig == null ? false : scConfig.BoolValue;
  112. var dibuffer = _buffer.GetDiBuffer(provider);
  113. List<DIAccessor> diList = new List<DIAccessor>();
  114. foreach (var diItem in lstDi)
  115. {
  116. XmlElement element = diItem as XmlElement;
  117. if (element == null)
  118. continue;
  119. string index = element.GetAttribute("Index");
  120. string name = element.GetAttribute("Name");
  121. string address = element.GetAttribute("Addr");
  122. string description = element.GetAttribute("Description");
  123. if (string.IsNullOrEmpty(name))
  124. continue;
  125. name = name.Trim();
  126. index = index.Trim();
  127. string moduleName = string.IsNullOrEmpty(module) ? name : $"{module}.{name}";
  128. int intIndex;
  129. if (!int.TryParse(index, out intIndex))
  130. continue;
  131. if (!dibuffer.ContainsKey(blockOffset))
  132. {
  133. throw new Exception("Not defined DI buffer from IO provider, " + provider);
  134. }
  135. DIAccessor diAccessor = new DIAccessor(moduleName, intIndex, dibuffer[blockOffset], dibuffer[blockOffset]);
  136. diAccessor.IoTableIndex = intIndex;
  137. diAccessor.Addr = address;
  138. diAccessor.Provider = provider;
  139. diAccessor.BlockOffset = blockOffset;
  140. diAccessor.Description = description;
  141. diList.Add(diAccessor);
  142. _adoManager.Subscribe(GetTwincatVariableName(module, name), 1, UpdateInputVariable);
  143. }
  144. _buffer.SetIoMap(provider, blockOffset, diList);
  145. var dobuffer = _buffer.GetDoBuffer(provider);
  146. List<DOAccessor> doList = new List<DOAccessor>();
  147. foreach (var doItem in lstDo)
  148. {
  149. XmlElement element = doItem as XmlElement;
  150. if (element == null)
  151. continue;
  152. string index = element.GetAttribute("Index");
  153. string name = element.GetAttribute("Name");
  154. string address = element.GetAttribute("Addr");
  155. string description = element.GetAttribute("Description");
  156. if (string.IsNullOrEmpty(name))
  157. continue;
  158. name = name.Trim();
  159. index = index.Trim();
  160. //main.pma.av_102
  161. string moduleName = string.IsNullOrEmpty(module) ? name : $"{module}.{name}";
  162. int intIndex;
  163. if (!int.TryParse(index, out intIndex))
  164. continue;
  165. if (!dobuffer.ContainsKey(blockOffset))
  166. {
  167. throw new Exception("Not defined DO buffer from IO provider, " + provider);
  168. }
  169. DOAccessor doAccessor = new DOAccessor(moduleName, intIndex, dobuffer[blockOffset]);
  170. doAccessor.IoTableIndex = intIndex;
  171. doAccessor.Addr = address;
  172. doAccessor.Provider = provider;
  173. doAccessor.BlockOffset = blockOffset;
  174. doAccessor.Description = description;
  175. doList.Add(doAccessor);
  176. _adoManager.Subscribe(GetTwincatVariableName(module, name), 1, UpdateInputVariable);
  177. _adoManager.SubscribeOutput(GetTwincatVariableName(module, name));
  178. }
  179. _buffer.SetIoMap(provider, blockOffset, doList);
  180. var aobuffer = _buffer.GetAoBuffer(provider);
  181. List<AOAccessor> aoList = new List<AOAccessor>();
  182. foreach (var aoItem in lstAo)
  183. {
  184. XmlElement element = aoItem as XmlElement;
  185. if (element == null)
  186. continue;
  187. string index = element.GetAttribute("Index");
  188. string name = element.GetAttribute("Name");
  189. string address = element.GetAttribute("Addr");
  190. string description = element.GetAttribute("Description");
  191. if (string.IsNullOrEmpty(name))
  192. continue;
  193. name = name.Trim();
  194. index = index.Trim();
  195. string moduleName = string.IsNullOrEmpty(module) ? name : $"{module}.{name}";
  196. int intIndex;
  197. if (!int.TryParse(index, out intIndex))
  198. continue;
  199. if (!aobuffer.ContainsKey(blockOffset))
  200. {
  201. throw new Exception("Not defined AO buffer from IO provider, " + provider);
  202. }
  203. AOAccessor aoAccessor = new AOAccessor(moduleName, intIndex * 2, aobuffer[blockOffset]);
  204. aoAccessor.IoTableIndex = intIndex;
  205. aoAccessor.Addr = address;
  206. aoAccessor.Provider = provider;
  207. aoAccessor.BlockOffset = blockOffset;
  208. aoAccessor.Description = description;
  209. aoList.Add(aoAccessor);
  210. _adoManager.Subscribe(GetTwincatVariableName(module, name), 4, UpdateInputVariable);//float在Twincat中32位,数据长度为4
  211. _adoManager.SubscribeOutput(GetTwincatVariableName(module, name));
  212. }
  213. _buffer.SetIoMap(provider, blockOffset, aoList);
  214. var aibuffer = _buffer.GetAiBuffer(provider);
  215. List<AIAccessor> aiList = new List<AIAccessor>();
  216. foreach (var aiItem in lstAi)
  217. {
  218. XmlElement element = aiItem as XmlElement;
  219. if (element == null)
  220. continue;
  221. string index = element.GetAttribute("Index");
  222. string name = element.GetAttribute("Name");
  223. string address = element.GetAttribute("Addr");
  224. string description = element.GetAttribute("Description");
  225. if (string.IsNullOrEmpty(name))
  226. continue;
  227. name = name.Trim();
  228. index = index.Trim();
  229. string moduleName = string.IsNullOrEmpty(module) ? name : $"{module}.{name}";
  230. int intIndex;
  231. if (!int.TryParse(index, out intIndex))
  232. continue;
  233. if (!aibuffer.ContainsKey(blockOffset))
  234. {
  235. throw new Exception("Not defined AI buffer from IO provider, " + provider);
  236. }
  237. AIAccessor aiAccessor = new AIAccessor(moduleName, intIndex * 2, aibuffer[blockOffset]);
  238. aiAccessor.IoTableIndex = intIndex;
  239. aiAccessor.Addr = address;
  240. aiAccessor.Provider = provider;
  241. aiAccessor.BlockOffset = blockOffset;
  242. aiAccessor.Description = description;
  243. aiList.Add(aiAccessor);
  244. _adoManager.Subscribe(GetTwincatVariableName(module, name), 4, UpdateInputVariable);//float在Twincat中32位,数据长度为4
  245. }
  246. _buffer.SetIoMap(provider, blockOffset, aiList);
  247. }
  248. /// <summary>
  249. /// 获取Twincat变量名称
  250. /// </summary>
  251. /// <param name="name"></param>
  252. /// <returns></returns>
  253. private string GetTwincatVariableName(string module, string name)
  254. {
  255. if (!string.IsNullOrEmpty(_prefix))
  256. {
  257. return $"{_prefix}.{_structName}.{name}";
  258. }
  259. else
  260. {
  261. return name;
  262. }
  263. }
  264. /// <summary>
  265. /// 输入变量数值发生变化
  266. /// </summary>
  267. /// <param name="variableName"></param>
  268. /// <param name="value"></param>
  269. private void UpdateInputVariable(string variableName, object value)
  270. {
  271. if (!string.IsNullOrEmpty(_prefix))
  272. {
  273. _buffer.SetBufferValue(_source, $"{variableName.Replace($"{_prefix}.", "")}", value);
  274. }
  275. else
  276. {
  277. _buffer.SetBufferValue(_source, $"{Module}.{variableName}", value);
  278. }
  279. }
  280. protected override void Close()
  281. {
  282. _isRunning = false;
  283. while (_writeQueue.Count != 0)
  284. {
  285. try
  286. {
  287. _writeQueue.TryDequeue(out var data);
  288. }
  289. catch
  290. {
  291. }
  292. }
  293. if (_writeThread != null)
  294. {
  295. try
  296. {
  297. _writeThread.Abort();
  298. }
  299. catch
  300. {
  301. }
  302. _writeThread = null;
  303. }
  304. }
  305. public override void Open()
  306. {
  307. }
  308. protected override short[] ReadAi(int offset, int size)
  309. {
  310. return null;
  311. }
  312. protected override bool[] ReadDi(int offset, int size)
  313. {
  314. return null;
  315. }
  316. protected override void WriteAo(int offset, short[] buffer)
  317. {
  318. }
  319. protected override void WriteDo(int offset, bool[] buffer)
  320. {
  321. }
  322. public override void SetValue(string variableName, object value)
  323. {
  324. if (_isRunning)
  325. {
  326. var data = (variableName, value);
  327. _writeQueue.Enqueue(data);
  328. }
  329. else
  330. {
  331. LOG.Write(eEvent.ERR_TWINCAT, Module, $"{Module}.{Name} stopped");
  332. }
  333. }
  334. /// <summary>
  335. /// 写入线程
  336. /// </summary>
  337. private void WriteThreadCallBack()
  338. {
  339. while (_isRunning)
  340. {
  341. if (_writeQueue.Count == 0)
  342. {
  343. Thread.Sleep(5);
  344. continue;
  345. }
  346. if (_writeQueue.TryDequeue(out var data))
  347. {
  348. string variableName = data.name.Replace($"{Module}.", "");
  349. string twincatName = GetTwincatVariableName(Module, variableName);
  350. var result = _adoManager.WriteValue(twincatName, data.value);
  351. if (!result.success && result.status == 1)//写入异常
  352. {
  353. _writeQueue.Enqueue(data);
  354. }
  355. }
  356. Thread.Sleep(1);
  357. }
  358. }
  359. public class TwincatFeedbackBuffer{
  360. //缓存process过程变量
  361. private readonly Dictionary<string, object> _feedbackSignal = new Dictionary<string, object>();
  362. // 添加信号到缓存中
  363. public void AddSignal(string name, object value)
  364. {
  365. _feedbackSignal[name] = value;
  366. }
  367. // 获取信号值,返回一个类型为 object 的值
  368. public object GetSignal(string name)
  369. {
  370. if (_feedbackSignal.TryGetValue(name, out var value))
  371. {
  372. return value;
  373. }
  374. else
  375. {
  376. throw new KeyNotFoundException($"Signal with name '{name}' not found.");
  377. }
  378. }
  379. }
  380. private void UpdateRecipeFeedBackVariable(string variableName, object value)
  381. {
  382. //byte[] bytes = (byte[])value;
  383. //int byteNumber = 0;
  384. //ProcessRecipeFeedBack processRecipeFeedBack = TwincatFeedBackManager.Instance.GetModuleFeebback(Module);
  385. //Type _feedBackItemType = processRecipeFeedBack.GetType();
  386. //var _feedBackItems = _feedBackItemType.GetFields(BindingFlags.Public | BindingFlags.Instance);
  387. //foreach (var _feedBackItem in _feedBackItems)
  388. //{
  389. // _feedBackItem.SetValue(processRecipeFeedBack, GetPropertyValue(_feedBackItem, bytes, ref byteNumber));
  390. //}
  391. }
  392. private object GetPropertyValue(FieldInfo propertyInfo,byte[] bytes,ref int bytesNumber)
  393. {
  394. switch(propertyInfo.FieldType.Name.ToLower())
  395. {
  396. case "boolean":
  397. return bytes[bytesNumber++] == 1 ? true : false;
  398. case "int16":
  399. short shortValue= BitConverter.ToInt16(bytes, bytesNumber);
  400. bytesNumber += 2;
  401. return shortValue;
  402. case "int32":
  403. int intValue= BitConverter.ToInt32(bytes, bytesNumber);
  404. bytesNumber += 4;
  405. return intValue;
  406. case "float":
  407. float floatValue=BitConverter.ToSingle(bytes, bytesNumber);
  408. bytesNumber += 4;
  409. return floatValue;
  410. case "double":
  411. double doubleValue = BitConverter.ToDouble(bytes, bytesNumber);
  412. bytesNumber += 8;
  413. return doubleValue;
  414. default:
  415. return null;
  416. }
  417. }
  418. private void SetRecipe(string source,string module = "")
  419. {
  420. //TwincatFeedBackManager.Instance.Initialize(module);
  421. //Array recipeTypes = Enum.GetValues(typeof(KeplerRecipeType));
  422. //var _processRecipeFeedBack = TwincatFeedBackManager.Instance.GetModuleFeebback(module);
  423. //int length = Marshal.SizeOf(_processRecipeFeedBack);
  424. //string twincatFeedBackItemName = GetTwincatVariableName(module, "processFeedBack");
  425. //_adoManager.Subscribe(twincatFeedBackItemName, length, UpdateRecipeFeedBackVariable);
  426. //foreach (var type in recipeTypes)
  427. //{
  428. // string twincatName = GetTwincatVariableName(module, type.ToString());
  429. // _adoManager.SubscribeOutput(twincatName);
  430. //}
  431. }
  432. private int GetDataTypeLength(string type)
  433. {
  434. switch(type.ToLower())
  435. {
  436. case "boolean":
  437. return 1;
  438. case "int16":
  439. return 2;
  440. default:
  441. return 0;
  442. }
  443. }
  444. //写入recipe
  445. //public override bool WriteRecipe<T>(ModuleName moduleName,RecipeType currentRecipeType,T data)
  446. //{
  447. // string twincatName = GetTwincatVariableName(Module, currentRecipeType.ToString());
  448. // var result = _adoManager.WriteStruct(twincatName, data);
  449. // return result.success;
  450. //}
  451. }
  452. }