using Aitex.Core.RT.IOCore; using Aitex.Core.RT.Log; using Aitex.Core.RT.SCCore; using MECF.Framework.Common.Equipment; using MECF.Framework.Common.SCCore; using MECF.Framework.Common.Twincat; using MECF.Framework.RT.Core.IoProviders; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Threading; using System.Xml; namespace Venus_RT.Devices { public static class TwinCatMananger { private static Dictionary _mod2twio = new Dictionary(); public static void AddTwinCatWithModule(ModuleName mod, TwincatIO twio) { Debug.Assert(!_mod2twio.ContainsKey(mod)); _mod2twio.Add(mod, twio); } public static TwincatIO GetTwinCatByModuleName(ModuleName mod) { Debug.Assert(_mod2twio.ContainsKey(mod)); return _mod2twio[mod]; } } public class TwincatIO : IoProvider { #region 内部变量 private string _prefix = "MAIN"; private string _structName = "PcComm"; /// /// 写入队列 /// private ConcurrentQueue<(string name, object value)> _writeQueue = new ConcurrentQueue<(string, object)>(); /// /// 写入线程 /// private Thread _writeThread = null; /// /// 是否正在运行 /// private bool _isRunning = false; /// /// Ado通信对象 /// private TwincatAdoManager _adoManager = null; #endregion /// /// 初始化 /// /// /// /// /// /// /// /// public override void Initialize(string module, string name, List lstBuffers, IIoBuffer buffer, XmlElement nodeParameter, string ioMappingPathFile, string ioModule) { Module = module; Name = name; _adoManager = new TwincatAdoManager(); TwincatAdoObjectManager.Instance.InitializeModuleAdoManager(module, _adoManager); if (SC.ContainsItem("System.TwincatPrefix")) { _prefix = SC.GetStringValue("System.TwincatPrefix"); } _source = module + "." + name; _buffer = buffer; _nodeParameter = nodeParameter; _blockSections = lstBuffers; SetIoMap(_source, 0, ioMappingPathFile, ioModule); string configMapPath = ioMappingPathFile.Substring(0, ioMappingPathFile.LastIndexOf('\\'))+"\\TwincatConfig.xml"; //if (configMapPath != null) //{ // TwincatConfigManager.Instance.SetConfigMap(configMapPath, ioModule); //} SetRecipe(_source,ioModule); SetParameter(nodeParameter); State = IoProviderStateEnum.Uninitialized; _writeThread = new Thread(new ThreadStart(WriteThreadCallBack)); _writeThread.IsBackground = true; _isRunning = true; _writeThread.Start(); TwinCatMananger.AddTwinCatWithModule(ModuleHelper.Converter(module), this); } protected override void SetParameter(XmlElement nodeParameter) { string strIp = nodeParameter.GetAttribute("ip"); string strPort = nodeParameter.GetAttribute("port"); int port = int.Parse(strPort); string ip = strIp; _adoManager.Initialize(ip, port); _adoManager.Start(); } private void SetIoMap(string provider, int blockOffset, string xmlPathFile, string module = "") { XmlDocument xml = new XmlDocument(); xml.Load(xmlPathFile); XmlNodeList lstDi = xml.SelectNodes("IO_DEFINE/Dig_In/DI_ITEM"); XmlNodeList lstDo = xml.SelectNodes("IO_DEFINE/Dig_Out/DO_ITEM"); XmlNodeList lstAo = xml.SelectNodes("IO_DEFINE/Ana_Out/AO_ITEM"); XmlNodeList lstAi = xml.SelectNodes("IO_DEFINE/Ana_In/AI_ITEM"); _buffer.SetBufferBlock(provider, lstDo.Count, lstDi.Count, lstAi.Count, lstAo.Count); var scConfig = SC.GetConfigItem("System.IsIgnoreSaveDB"); var isIgnoreSaveDB = scConfig == null ? false : scConfig.BoolValue; var dibuffer = _buffer.GetDiBuffer(provider); List diList = new List(); foreach (var diItem in lstDi) { XmlElement element = diItem as XmlElement; if (element == null) continue; string index = element.GetAttribute("Index"); string name = element.GetAttribute("Name"); string address = element.GetAttribute("Addr"); string description = element.GetAttribute("Description"); if (string.IsNullOrEmpty(name)) continue; name = name.Trim(); index = index.Trim(); string moduleName = string.IsNullOrEmpty(module) ? name : $"{module}.{name}"; int intIndex; if (!int.TryParse(index, out intIndex)) continue; if (!dibuffer.ContainsKey(blockOffset)) { throw new Exception("Not defined DI buffer from IO provider, " + provider); } DIAccessor diAccessor = new DIAccessor(moduleName, intIndex, dibuffer[blockOffset], dibuffer[blockOffset]); diAccessor.IoTableIndex = intIndex; diAccessor.Addr = address; diAccessor.Provider = provider; diAccessor.BlockOffset = blockOffset; diAccessor.Description = description; diList.Add(diAccessor); _adoManager.Subscribe(GetTwincatVariableName(module, name), 1, UpdateInputVariable); } _buffer.SetIoMap(provider, blockOffset, diList); var dobuffer = _buffer.GetDoBuffer(provider); List doList = new List(); foreach (var doItem in lstDo) { XmlElement element = doItem as XmlElement; if (element == null) continue; string index = element.GetAttribute("Index"); string name = element.GetAttribute("Name"); string address = element.GetAttribute("Addr"); string description = element.GetAttribute("Description"); if (string.IsNullOrEmpty(name)) continue; name = name.Trim(); index = index.Trim(); //main.pma.av_102 string moduleName = string.IsNullOrEmpty(module) ? name : $"{module}.{name}"; int intIndex; if (!int.TryParse(index, out intIndex)) continue; if (!dobuffer.ContainsKey(blockOffset)) { throw new Exception("Not defined DO buffer from IO provider, " + provider); } DOAccessor doAccessor = new DOAccessor(moduleName, intIndex, dobuffer[blockOffset]); doAccessor.IoTableIndex = intIndex; doAccessor.Addr = address; doAccessor.Provider = provider; doAccessor.BlockOffset = blockOffset; doAccessor.Description = description; doList.Add(doAccessor); _adoManager.Subscribe(GetTwincatVariableName(module, name), 1, UpdateInputVariable); _adoManager.SubscribeOutput(GetTwincatVariableName(module, name)); } _buffer.SetIoMap(provider, blockOffset, doList); var aobuffer = _buffer.GetAoBuffer(provider); List aoList = new List(); foreach (var aoItem in lstAo) { XmlElement element = aoItem as XmlElement; if (element == null) continue; string index = element.GetAttribute("Index"); string name = element.GetAttribute("Name"); string address = element.GetAttribute("Addr"); string description = element.GetAttribute("Description"); if (string.IsNullOrEmpty(name)) continue; name = name.Trim(); index = index.Trim(); string moduleName = string.IsNullOrEmpty(module) ? name : $"{module}.{name}"; int intIndex; if (!int.TryParse(index, out intIndex)) continue; if (!aobuffer.ContainsKey(blockOffset)) { throw new Exception("Not defined AO buffer from IO provider, " + provider); } AOAccessor aoAccessor = new AOAccessor(moduleName, intIndex * 2, aobuffer[blockOffset]); aoAccessor.IoTableIndex = intIndex; aoAccessor.Addr = address; aoAccessor.Provider = provider; aoAccessor.BlockOffset = blockOffset; aoAccessor.Description = description; aoList.Add(aoAccessor); _adoManager.Subscribe(GetTwincatVariableName(module, name), 4, UpdateInputVariable);//float在Twincat中32位,数据长度为4 _adoManager.SubscribeOutput(GetTwincatVariableName(module, name)); } _buffer.SetIoMap(provider, blockOffset, aoList); var aibuffer = _buffer.GetAiBuffer(provider); List aiList = new List(); foreach (var aiItem in lstAi) { XmlElement element = aiItem as XmlElement; if (element == null) continue; string index = element.GetAttribute("Index"); string name = element.GetAttribute("Name"); string address = element.GetAttribute("Addr"); string description = element.GetAttribute("Description"); if (string.IsNullOrEmpty(name)) continue; name = name.Trim(); index = index.Trim(); string moduleName = string.IsNullOrEmpty(module) ? name : $"{module}.{name}"; int intIndex; if (!int.TryParse(index, out intIndex)) continue; if (!aibuffer.ContainsKey(blockOffset)) { throw new Exception("Not defined AI buffer from IO provider, " + provider); } AIAccessor aiAccessor = new AIAccessor(moduleName, intIndex * 2, aibuffer[blockOffset]); aiAccessor.IoTableIndex = intIndex; aiAccessor.Addr = address; aiAccessor.Provider = provider; aiAccessor.BlockOffset = blockOffset; aiAccessor.Description = description; aiList.Add(aiAccessor); _adoManager.Subscribe(GetTwincatVariableName(module, name), 4, UpdateInputVariable);//float在Twincat中32位,数据长度为4 } _buffer.SetIoMap(provider, blockOffset, aiList); } /// /// 获取Twincat变量名称 /// /// /// private string GetTwincatVariableName(string module, string name) { if (!string.IsNullOrEmpty(_prefix)) { return $"{_prefix}.{_structName}.{name}"; } else { return name; } } /// /// 输入变量数值发生变化 /// /// /// private void UpdateInputVariable(string variableName, object value) { if (!string.IsNullOrEmpty(_prefix)) { _buffer.SetBufferValue(_source, $"{variableName.Replace($"{_prefix}.", "")}", value); } else { _buffer.SetBufferValue(_source, $"{Module}.{variableName}", value); } } protected override void Close() { _isRunning = false; while (_writeQueue.Count != 0) { try { _writeQueue.TryDequeue(out var data); } catch { } } if (_writeThread != null) { try { _writeThread.Abort(); } catch { } _writeThread = null; } } public override void Open() { } protected override short[] ReadAi(int offset, int size) { return null; } protected override bool[] ReadDi(int offset, int size) { return null; } protected override void WriteAo(int offset, short[] buffer) { } protected override void WriteDo(int offset, bool[] buffer) { } public override void SetValue(string variableName, object value) { if (_isRunning) { var data = (variableName, value); _writeQueue.Enqueue(data); } else { LOG.Write(eEvent.ERR_TWINCAT, Module, $"{Module}.{Name} stopped"); } } /// /// 写入线程 /// private void WriteThreadCallBack() { while (_isRunning) { if (_writeQueue.Count == 0) { Thread.Sleep(5); continue; } if (_writeQueue.TryDequeue(out var data)) { string variableName = data.name.Replace($"{Module}.", ""); string twincatName = GetTwincatVariableName(Module, variableName); var result = _adoManager.WriteValue(twincatName, data.value); if (!result.success && result.status == 1)//写入异常 { _writeQueue.Enqueue(data); } } Thread.Sleep(1); } } public class TwincatFeedbackBuffer{ //缓存process过程变量 private readonly Dictionary _feedbackSignal = new Dictionary(); // 添加信号到缓存中 public void AddSignal(string name, object value) { _feedbackSignal[name] = value; } // 获取信号值,返回一个类型为 object 的值 public object GetSignal(string name) { if (_feedbackSignal.TryGetValue(name, out var value)) { return value; } else { throw new KeyNotFoundException($"Signal with name '{name}' not found."); } } } private void UpdateRecipeFeedBackVariable(string variableName, object value) { //byte[] bytes = (byte[])value; //int byteNumber = 0; //ProcessRecipeFeedBack processRecipeFeedBack = TwincatFeedBackManager.Instance.GetModuleFeebback(Module); //Type _feedBackItemType = processRecipeFeedBack.GetType(); //var _feedBackItems = _feedBackItemType.GetFields(BindingFlags.Public | BindingFlags.Instance); //foreach (var _feedBackItem in _feedBackItems) //{ // _feedBackItem.SetValue(processRecipeFeedBack, GetPropertyValue(_feedBackItem, bytes, ref byteNumber)); //} } private object GetPropertyValue(FieldInfo propertyInfo,byte[] bytes,ref int bytesNumber) { switch(propertyInfo.FieldType.Name.ToLower()) { case "boolean": return bytes[bytesNumber++] == 1 ? true : false; case "int16": short shortValue= BitConverter.ToInt16(bytes, bytesNumber); bytesNumber += 2; return shortValue; case "int32": int intValue= BitConverter.ToInt32(bytes, bytesNumber); bytesNumber += 4; return intValue; case "float": float floatValue=BitConverter.ToSingle(bytes, bytesNumber); bytesNumber += 4; return floatValue; case "double": double doubleValue = BitConverter.ToDouble(bytes, bytesNumber); bytesNumber += 8; return doubleValue; default: return null; } } private void SetRecipe(string source,string module = "") { //TwincatFeedBackManager.Instance.Initialize(module); //Array recipeTypes = Enum.GetValues(typeof(KeplerRecipeType)); //var _processRecipeFeedBack = TwincatFeedBackManager.Instance.GetModuleFeebback(module); //int length = Marshal.SizeOf(_processRecipeFeedBack); //string twincatFeedBackItemName = GetTwincatVariableName(module, "processFeedBack"); //_adoManager.Subscribe(twincatFeedBackItemName, length, UpdateRecipeFeedBackVariable); //foreach (var type in recipeTypes) //{ // string twincatName = GetTwincatVariableName(module, type.ToString()); // _adoManager.SubscribeOutput(twincatName); //} } private int GetDataTypeLength(string type) { switch(type.ToLower()) { case "boolean": return 1; case "int16": return 2; default: return 0; } } //写入recipe //public override bool WriteRecipe(ModuleName moduleName,RecipeType currentRecipeType,T data) //{ // string twincatName = GetTwincatVariableName(Module, currentRecipeType.ToString()); // var result = _adoManager.WriteStruct(twincatName, data); // return result.success; //} } }