using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Xml;
using Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.IOCore;
using Aitex.Core.RT.IOCore.Interlock;
using Aitex.Core.RT.OperationCenter;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;
using MECF.Framework.RT.Core.IoProviders;

namespace MECF.Framework.Common.IOCore
{
	public class IoManager : Singleton<IoManager>, IIoBuffer
	{
		private readonly Dictionary<string, DIAccessor> _diMap = new();
		private readonly Dictionary<string, DOAccessor> _doMap = new();
		private readonly Dictionary<string, AIAccessor> _aiMap = new();
		private readonly Dictionary<string, AOAccessor> _aoMap = new();
		private readonly Dictionary<string, IoDataCachePerOffset<bool[]>> _diBuffer = new();
		private readonly Dictionary<string, IoDataCachePerOffset<bool[]>> _doBuffer = new();
		private readonly Dictionary<string, IoDataCachePerOffset<float[]>> _aiBuffer = new();
		private readonly Dictionary<string, IoDataCachePerOffset<float[]>> _aoBuffer = new();
		private readonly Dictionary<string, IoDataCachePerOffset<Type>> _aiBufferType = new();
		private readonly Dictionary<string, IoDataCachePerOffset<Type>> _aoBufferType = new();
		private readonly Dictionary<string, List<DIAccessor>> _diListPerPlc = new();
		private readonly Dictionary<string, List<DOAccessor>> _doListPerPlc = new();
		private readonly Dictionary<string, List<AIAccessor>> _aiListPerPlc = new();
		private readonly Dictionary<string, List<AOAccessor>> _aoListPerPlc = new();
		private readonly Dictionary<string, List<NotifiableIoItem>> _ioItemList = new();
		//private PeriodicJob _monitorThread;

		/// <summary>
		/// 是否使用在模拟器中。
		/// </summary>
        protected bool IsSimulator;

        public IReadOnlyDictionary<string, DIAccessor> DIMap => _diMap;

        public IReadOnlyDictionary<string, DOAccessor> DOMap => _doMap;

        public IReadOnlyDictionary<string, AIAccessor> AIMap => _aiMap;

        public IReadOnlyDictionary<string, AOAccessor> AOMap => _aoMap;

        public void Initialize()
		{

        }

		internal List<Tuple<int, int, string>> GetIONameList(string group, IOType ioType)
		{
			return null;
		}

		public bool CanSetDo(string doName, bool onOff, out string reason)
		{
			return Singleton<InterlockManager>.Instance.CanSetDo(doName, onOff, out reason);
		}

		public List<DIAccessor> GetDIList(string source)
        {
            return _diListPerPlc.TryGetValue(source, out var value) ? value : null;
        }

		public List<DOAccessor> GetDOList(string source)
		{
			return _doListPerPlc.TryGetValue(source, out var value) ? value : null;
		}

		public List<AIAccessor> GetAIList(string source)
		{
			return _aiListPerPlc.TryGetValue(source, out var value) ? value : null;
		}

		public List<AOAccessor> GetAOList(string source)
		{
			return _aoListPerPlc.TryGetValue(source, out var value) ? value : null;
		}

		public IoManager()
		{
			OP.Subscribe("System.SetDoValue", InvokeSetDo);
			OP.Subscribe("System.SetAoValue", InvokeSetAo);
			OP.Subscribe("System.SetAoValueFloat", InvokeSetAoFloat);
			OP.Subscribe("System.SetDoValueWithPrivoder", InvokeSetDoWithProvider);
			OP.Subscribe("System.SetAoValueWithPrivoder", InvokeSetAoWithPrivoder);
			OP.Subscribe("System.SetAiBuffer", InvokeSetAiBuffer);
			OP.Subscribe("System.SetDiBuffer", InvokeSetDiBuffer);
		}

		private bool InvokeSetDo(string arg1, object[] args)
		{
			var text = (string)args[0];
			var flag = (bool)args[1];
			if (!CanSetDo(text, flag, out var reason))
			{
				EV.PostWarningLog("System", $"Can not set DO {text} to {flag}, {reason}");
				return false;
			}
			var iO = GetIO<DOAccessor>(text);
			if (iO == null)
			{
				EV.PostWarningLog("System", $"Can not set DO {text} to {flag}, not defined do");
				return false;
			}
			if (!iO.SetValue(flag, out reason))
			{
				EV.PostWarningLog("System", $"Can not set DO {text} to {flag}, {reason}");
				return false;
			}
			EV.PostInfoLog("System", $"Change DO {text} to {flag}");
			return true;
		}

		private bool InvokeSetAo(string arg1, object[] args)
		{
			var text = (string)args[0];
			var num = (short)args[1];
			var iO = GetIO<AOAccessor>(text);
			if (iO == null)
			{
				EV.PostWarningLog("System", $"Can not set AO {text} to {num}, not defined do");
				return false;
			}
			iO.Value = num;
			EV.PostInfoLog("System", $"Change AO {text} to {num}");
			return true;
		}

		private bool InvokeSetAoFloat(string arg1, object[] args)
		{
			var text = (string)args[0];
			var num = (float)args[1];
			var iO = GetIO<AOAccessor>(text);
			if (iO == null)
			{
				EV.PostWarningLog("System", $"Can not set AO {text} to {num}, not defined");
				return false;
			}
			iO.Value = num;
			EV.PostInfoLog("System", $"Change AO {text} to {num}");
			return true;
		}

		private bool InvokeSetDoWithProvider(string arg1, object[] args)
		{
			var text = (string)args[0];
			var num = (int)args[1];
			var name = (string)args[2];
			var flag = (bool)args[3];
			if (!CanSetDo(name, flag, out var reason))
			{
				EV.PostWarningLog("System", $"Can not set DO {text}.{name} to {flag}, {reason}");
				return false;
			}
			var dOList = GetDOList(text);
			if (dOList == null)
			{
				EV.PostWarningLog("System", $"Can not set DO {text}.{name} to {flag}, {reason}");
				return false;
			}
			var dOAccessor = dOList.FirstOrDefault((DOAccessor x) => x.Name == name);
			if (dOAccessor == null)
			{
				EV.PostWarningLog("System", $"Can not set DO {text}.{name} to {flag}, {reason}");
				return false;
			}
			if (!dOAccessor.SetValue(flag, out reason))
			{
				EV.PostWarningLog("System", $"Can not set DO {text}.{name} to {flag}, {reason}");
				return false;
			}
			EV.PostInfoLog("System", $"Change DO {text}.{name} to {flag}");
			return true;
		}

		private bool InvokeSetAiBuffer(string arg1, object[] args)
		{
			var source = (string)args[0];
			var offset = (int)args[1];
			var buffer = (float[])args[2];
			SetAiBuffer(source, offset, buffer);
			return true;
		}

		private bool InvokeSetDiBuffer(string arg1, object[] args)
		{
			var source = (string)args[0];
			var offset = (int)args[1];
			var buffer = (bool[])args[2];
			SetDiBuffer(source, offset, buffer);
			return true;
		}

		private bool InvokeSetAoWithPrivoder(string arg1, object[] args)
		{
			var text = (string)args[0];
			var num = (int)args[1];
			var name = (string)args[2];
			var num2 = (float)args[3];
			var text2 = "";
			var aOList = GetAOList(text);
			if (aOList == null)
			{
				EV.PostWarningLog("System", $"Can not set AO {text}.{name} to {num2}, {text2}");
				return false;
			}
			var aOAccessor = aOList.FirstOrDefault((AOAccessor x) => x.Name == name);
			if (aOAccessor == null)
			{
				EV.PostWarningLog("System", $"Can not set AO {text}.{name} to {num2}, {text2}");
				return false;
			}
			aOAccessor.Value = (short)num2;
			EV.PostInfoLog("System", $"Change DO {text}.{name} to {num2}");
			return true;
		}

		public T GetIO<T>(string name) where T : class
		{
			if (typeof(T) == typeof(DIAccessor) && _diMap.TryGetValue(name, out var value))
			{
				return value as T;
			}
			if (typeof(T) == typeof(DOAccessor) && _doMap.TryGetValue(name, out var value1))
			{
				return value1 as T;
			}
			if (typeof(T) == typeof(AIAccessor) && _aiMap.TryGetValue(name, out var value2))
			{
				return value2 as T;
			}
			if (typeof(T) == typeof(AOAccessor) && _aoMap.TryGetValue(name, out var value3))
			{
				return value3 as T;
			}
			return null;
		}

		public Dictionary<int, bool[]> GetDiBuffer(string source)
		{
			if (_diBuffer.TryGetValue(source, out var buffer))
			{
				return buffer;
			}
			return null;
		}

		public Dictionary<int, bool[]> GetDoBuffer(string source)
		{
			if (_doBuffer.TryGetValue(source, out var buffer))
			{
				return buffer;
			}
			return null;
		}
		
		public Dictionary<int, float[]> GetAiBuffer(string source)
		{
			return _aiBuffer.TryGetValue(source, out var f) ? f : null;
		}

		public Dictionary<int, float[]> GetAoBuffer(string source)
		{
			return _aoBuffer.TryGetValue(source, out var buffer) ? buffer : null;
		}

		public void SetDiBuffer(string provider, int offset, bool[] data)
		{
			if (_diBuffer.ContainsKey(provider) && _diBuffer[provider].ContainsKey(offset))
			{
				for (var i = 0; data != null && i < data.Length && i < _diBuffer[provider][offset].Length; i++)
				{
					_diBuffer[provider][offset][i] = data[i];
				}
			}
		}

		public void SetDoBuffer(string provider, int offset, bool[] data)
		{
			if (_doBuffer.ContainsKey(provider) && _doBuffer[provider].ContainsKey(offset))
			{
				for (var i = 0; data != null && i < data.Length && i < _doBuffer[provider][offset].Length; i++)
				{
					_doBuffer[provider][offset][i] = data[i];
				}
			}
		}

		public void SetAiBuffer(string provider, int offset, float[] data, int skipSize = 0)
		{
			if (_aiBuffer.ContainsKey(provider) && _aiBuffer[provider].ContainsKey(offset))
			{
				for (var i = 0; data != null && i < data.Length && i < _aiBuffer[provider][offset].Length; i++)
				{
					_aiBuffer[provider][offset][i + skipSize] = data[i];
				}
			}
		}
		

		public void SetAoBuffer(string provider, int offset, float[] data, int bufferStartIndex = 0)
		{
			if (_aoBuffer.ContainsKey(provider) && _aoBuffer[provider].ContainsKey(offset))
			{
				for (var i = 0; data != null && i < data.Length && i < _aoBuffer[provider][offset].Length; i++)
				{
					_aoBuffer[provider][offset][i] = data[i];
				}
			}
		}

        public void CreateBuffer(string provider, List<IoBlockItem> blocks)
        {
            foreach (var block in blocks)
            {
                switch (block.Type)
                {
                    case IoType.AI:
                        if (!_aiBuffer.ContainsKey(provider))
                        {
                            _aiBuffer[provider] = new IoDataCachePerOffset<float[]>(provider);
                            _aiBufferType[provider] = new IoDataCachePerOffset<Type>(provider);
                        }

                        Debug.Assert(!_aiBuffer[provider].ContainsKey(block.Index), $"Duplicate AI block index '{block.Index}'");
                        if (!_aiBuffer[provider].ContainsKey(block.Index))
                        {
                            _aiBuffer[provider][block.Index] = new float[block.Size];
                            _aiBufferType[provider][block.Index] = block.AIOType;
                        }

                        break;
                    case IoType.AO:
                        if (!_aoBuffer.ContainsKey(provider))
                        {
                            _aoBuffer[provider] = new IoDataCachePerOffset<float[]>(provider);
                            _aoBufferType[provider] = new IoDataCachePerOffset<Type>(provider);
                        }

                        Debug.Assert(!_aoBuffer[provider].ContainsKey(block.Index), $"Duplicate AO block index '{block.Index}'");
                        if (!_aoBuffer[provider].ContainsKey(block.Index))
                        {
                            _aoBuffer[provider][block.Index] = new float[block.Size];
                            _aoBufferType[provider][block.Index] = block.AIOType;
                        }

                        break;
                    case IoType.DI:
                        if (!_diBuffer.ContainsKey(provider))
                        {
                            _diBuffer[provider] = new IoDataCachePerOffset<bool[]>(provider);
                        }

                        Debug.Assert(!_diBuffer[provider].ContainsKey(block.Index), $"Duplicate DI block index '{block.Index}'");
                        if (!_diBuffer[provider].ContainsKey(block.Index))
                        {
                            _diBuffer[provider][block.Index] = new bool[block.Size];
                        }

                        break;
                    case IoType.DO:
                        if (!_doBuffer.ContainsKey(provider))
                        {
                            _doBuffer[provider] = new IoDataCachePerOffset<bool[]>(provider);
                        }

                        Debug.Assert(!_doBuffer[provider].ContainsKey(block.Index), $"Duplicate DO block index '{block.Index}'");
                        if (!_doBuffer[provider].ContainsKey(block.Index))
                        {
                            _doBuffer[provider][block.Index] = new bool[block.Size];
                        }

                        break;
                }
            }
        }

        private List<NotifiableIoItem> SubscribeDiData()
		{
			var list = new List<NotifiableIoItem>();
			foreach (var item2 in _diMap)
			{
				var item = new NotifiableIoItem
				{
					Address = item2.Value.Addr,
					Name = item2.Value.Name,
					Description = item2.Value.Description,
					Index = item2.Value.Index,
					BoolValue = item2.Value.Value,
					Provider = item2.Value.Provider,
					BlockOffset = item2.Value.BlockOffset,
					BlockIndex = item2.Value.Index,
					Visible = item2.Value.Visible
				};
				list.Add(item);
			}
			return list;
		}

		private List<NotifiableIoItem> SubscribeDoData()
		{
			var list = new List<NotifiableIoItem>();
			foreach (var item2 in _doMap)
			{
				var item = new NotifiableIoItem
				{
					Address = item2.Value.Addr,
					Name = item2.Value.Name,
					Description = item2.Value.Description,
					Index = item2.Value.Index,
					BoolValue = item2.Value.Value,
					Provider = item2.Value.Provider,
					BlockOffset = item2.Value.BlockOffset,
					BlockIndex = item2.Value.Index,
					Visible = item2.Value.Visible
				};
				list.Add(item);
			}
			return list;
		}

		private List<NotifiableIoItem> SubscribeAiData()
		{
			var list = new List<NotifiableIoItem>();
			foreach (var item2 in _aiMap)
			{
				var item = new NotifiableIoItem
				{
					Address = item2.Value.Addr,
					Name = item2.Value.Name,
					Description = item2.Value.Description,
					Index = item2.Value.Index,
					ShortValue = (short)item2.Value.Value,
					FloatValue = item2.Value.Value,
					Provider = item2.Value.Provider,
					BlockOffset = item2.Value.BlockOffset,
					BlockIndex = item2.Value.Index,
					Visible = item2.Value.Visible
				};
				list.Add(item);
			}
			return list;
		}

		private List<NotifiableIoItem> SubscribeAoData()
		{
			var list = new List<NotifiableIoItem>();
			foreach (var item2 in _aoMap)
			{
				var item = new NotifiableIoItem
				{
					Address = item2.Value.Addr,
					Name = item2.Value.Name,
					Description = item2.Value.Description,
					Index = item2.Value.Index,
					ShortValue = (short)item2.Value.Value,
					FloatValue = item2.Value.Value,
					Provider = item2.Value.Provider,
					BlockOffset = item2.Value.BlockOffset,
					BlockIndex = item2.Value.Index,
					Visible = item2.Value.Visible
				};
				list.Add(item);
			}
			return list;
		}

		public void SetIoMap(string provider, int blockOffset, List<DIAccessor> ioList)
		{
			SubscribeIoItemList(provider);
			var flag = SC.GetConfigItem("System.IsIgnoreSaveDB")?.BoolValue ?? false;
			foreach (var accessor in ioList)
			{
				accessor.Provider = provider;
				accessor.BlockOffset = blockOffset;
				_diMap[accessor.Name] = accessor;
				if (!_diListPerPlc.ContainsKey(provider))
				{
					_diListPerPlc[provider] = new List<DIAccessor>();
				}
				_diListPerPlc[provider].Add(accessor);
				_ioItemList[provider + ".DIItemList"].Add(new NotifiableIoItem
				{
					Address = accessor.Addr,
					Name = accessor.Name,
					Description = accessor.Description,
					Index = accessor.Index,
					Provider = provider,
					BlockOffset = blockOffset,
					BlockIndex = accessor.BlockIndex,
					Visible = accessor.Visible
				});
				if (!flag)
				{
					DATA.Subscribe("IO." + accessor.Name, () => accessor.Value);
				}
			}
		}

		public void SetIoMap(string provider, int blockOffset, List<DOAccessor> ioList)
		{
			SubscribeIoItemList(provider);
			var flag = SC.GetConfigItem("System.IsIgnoreSaveDB")?.BoolValue ?? false;
			foreach (var accessor in ioList)
			{
				accessor.Provider = provider;
				accessor.BlockOffset = blockOffset;
				_doMap[accessor.Name] = accessor;
				if (!_doListPerPlc.ContainsKey(provider))
				{
					_doListPerPlc[provider] = new List<DOAccessor>();
				}
				_doListPerPlc[provider].Add(accessor);
				_ioItemList[provider + ".DOItemList"].Add(new NotifiableIoItem
				{
					Address = accessor.Addr,
					Name = accessor.Name,
					Description = accessor.Description,
					Index = accessor.Index,
					Provider = provider,
					BlockOffset = blockOffset,
					BlockIndex = accessor.BlockIndex,
					Visible = accessor.Visible
				});
				if (!flag)
				{
					DATA.Subscribe("IO." + accessor.Name, () => accessor.Value);
				}
			}
		}

		public void SetIoMap(string provider, int blockOffset, List<AIAccessor> ioList)
		{
			SubscribeIoItemList(provider);
			var flag = SC.GetConfigItem("System.IsIgnoreSaveDB")?.BoolValue ?? false;
			foreach (var accessor in ioList)
			{
				accessor.Provider = provider;
				accessor.BlockOffset = blockOffset;
				_aiMap[accessor.Name] = accessor;
				if (!_aiListPerPlc.ContainsKey(provider))
				{
					_aiListPerPlc[provider] = new List<AIAccessor>();
				}
				_aiListPerPlc[provider].Add(accessor);
				_ioItemList[provider + ".AIItemList"].Add(new NotifiableIoItem
				{
					Address = accessor.Addr,
					Name = accessor.Name,
					Description = accessor.Description,
					Index = accessor.Index,
					Provider = provider,
					BlockOffset = blockOffset,
					BlockIndex = accessor.BlockIndex,
					Visible = accessor.Visible
				});
				if (!flag)
				{
					DATA.Subscribe("IO." + accessor.Name, () => accessor.Value);
				}
			}
		}

		public void SetIoMap(string provider, int blockOffset, List<AOAccessor> ioList)
		{
			SubscribeIoItemList(provider);
			var flag = SC.GetConfigItem("System.IsIgnoreSaveDB")?.BoolValue ?? false;
			foreach (var accessor in ioList)
			{
				accessor.Provider = provider;
				accessor.BlockOffset = blockOffset;
				_aoMap[accessor.Name] = accessor;
				if (!_aoListPerPlc.ContainsKey(provider))
				{
					_aoListPerPlc[provider] = new List<AOAccessor>();
				}
				_aoListPerPlc[provider].Add(accessor);
				_ioItemList[provider + ".AOItemList"].Add(new NotifiableIoItem
				{
					Address = accessor.Addr,
					Name = accessor.Name,
					Description = accessor.Description,
					Index = accessor.Index,
					Provider = provider,
					BlockOffset = blockOffset,
					BlockIndex = accessor.BlockIndex,
					Visible = accessor.Visible
				});
				if (!flag)
				{
					DATA.Subscribe("IO." + accessor.Name, () => accessor.Value);
				}
			}
		}

        private void LoadIoDefinition<T>(IIoDataCache cache, XmlNodeList nodeList, string subNodeName, IDictionary ioMap, IDictionary dictIoPerPlc, string module = "")
        where T: IIOAccessor
        {
            var isIgnoreSaveDb = SC.GetConfigItem("System.IsIgnoreSaveDB")?.BoolValue ?? false;

            foreach (XmlElement block in nodeList)
            {
                var attrIndex = block.GetAttribute("Index");
                if (!int.TryParse(attrIndex, out var blockIndex))
                    continue;

                var xmlNodeDiDefs = block.SelectNodes(subNodeName);

                foreach (var node in xmlNodeDiDefs)
                {
                    if (node is not XmlElement ioNode)
                        continue;

                    var ioIndexText = ioNode.GetAttribute("Index");
                    var ioBuffOffsetText = ioNode.GetAttribute("BufferOffset");
                    if (string.IsNullOrEmpty(ioBuffOffsetText))
                        ioBuffOffsetText = ioIndexText;

                    var ioNameText = ioNode.GetAttribute("Name");
                    var ioAddrText = ioNode.GetAttribute("Addr");
                    var ioDescText = ioNode.GetAttribute("Description");
                    var visible = true;
                    if (ioNode.HasAttribute("Visible"))
                    {
                        if (!bool.TryParse(ioNode.GetAttribute("Visible"), out visible))
                            visible = true;
                    }

                    var disableImmediateCache = false;
                    if (ioNode.HasAttribute("DisImmCache"))
                    {
                        if (!bool.TryParse(ioNode.GetAttribute("DisImmCache"), out disableImmediateCache))
                            disableImmediateCache = false;
                    }

                    if (string.IsNullOrEmpty(ioNameText) || string.IsNullOrEmpty(ioIndexText) ||
                        string.IsNullOrEmpty(ioBuffOffsetText))
                        continue;

                    ioNameText = ioNameText.Trim();
                    ioIndexText = ioIndexText.Trim();
                    ioBuffOffsetText = ioBuffOffsetText.Trim();
                    var ioNameWithModule = (string.IsNullOrEmpty(module) ? ioNameText : (module + "." + ioNameText));
                    if (!int.TryParse(ioIndexText, out var ioIndex) ||
                        !int.TryParse(ioBuffOffsetText, out var ioBuffOffset))
                        continue;

                    if (cache == null)
                        throw new Exception($"Not defined IO Data cache for Accessor {typeof(T)}.");

                    var provider = cache.ProviderName;
                    var accessor = (IIOAccessor)Activator.CreateInstance(typeof(T), ioNameWithModule, ioIndex,
                        ioAddrText, IsSimulator, disableImmediateCache);
                    accessor.IoTableIndex = ioIndex;
                    accessor.Provider = provider;
                    accessor.BlockIndex = blockIndex;
                    accessor.BlockOffset = ioBuffOffset;
                    accessor.Description = ioDescText;
                    accessor.Visible = visible;
                    Debug.Assert(cache.Contains(blockIndex), $"{provider}未定义类型为{typeof(T).Name}，Index为{blockIndex}的Block。");
                    accessor.BindBuffer(cache[blockIndex]);

                    ioMap.Add(ioNameWithModule, accessor);
                    if (dictIoPerPlc.Contains(provider) == false)
                        dictIoPerPlc[provider] = new List<T>();

                    ((IList)dictIoPerPlc[provider]).Add(accessor);
                    _ioItemList[provider + $".{accessor.Type}ItemList"].Add(new NotifiableIoItem
                    {
                        Address = ioAddrText,
                        Name = ioNameWithModule,
                        Description = ioDescText,
                        Index = ioIndex,
                        Provider = provider,
                        BlockIndex = blockIndex,
                        Visible = visible
                    });

                    if (!isIgnoreSaveDb)
                        DATA.Subscribe("IO." + ioNameWithModule, () => accessor.NonTypedValue);
                }
            }
        }

        public void SetIoMap(string provider, string xmlPathFile, string module = "")
        {
            SubscribeIoItemList(provider);
            var xmlDocument = new XmlDocument();
            xmlDocument.Load(xmlPathFile);
            var xmlNodeDiBlocks = xmlDocument.SelectNodes("IO_DEFINE/Dig_In/Block");
            LoadIoDefinition<DIAccessor>(_diBuffer[provider], xmlNodeDiBlocks, "DI_ITEM", _diMap, _diListPerPlc,
                module);

            var xmlNodeDoBlocks = xmlDocument.SelectNodes("IO_DEFINE/Dig_Out/Block");
            LoadIoDefinition<DOAccessor>(_doBuffer[provider], xmlNodeDoBlocks, "DO_ITEM", _doMap, _doListPerPlc,
                module);

            var xmlNodeAoBlocks = xmlDocument.SelectNodes("IO_DEFINE/Ana_Out/Block");
            LoadIoDefinition<AOAccessor>(_aoBuffer[provider], xmlNodeAoBlocks, "AO_ITEM", _aoMap, _aoListPerPlc,
                module);

            var xmlNodeAiBlocks = xmlDocument.SelectNodes("IO_DEFINE/Ana_In/Block");
            LoadIoDefinition<AIAccessor>(_aiBuffer[provider], xmlNodeAiBlocks, "AI_ITEM", _aiMap, _aiListPerPlc,
                module);
        }

        public void MapIoBuffer(string provider, Dictionary<int, string> ioMappingPathFile)
		{
			foreach (var item in ioMappingPathFile)
			{
				SetIoMap(provider, item.Value);
			}
			DATA.Subscribe(provider, "DIList", SubscribeDiData);
			DATA.Subscribe(provider, "DOList", SubscribeDoData);
			DATA.Subscribe(provider, "AIList", SubscribeAiData);
			DATA.Subscribe(provider, "AOList", SubscribeAoData);
		}

		public void MapIoBufferByModule(string provider, string ioMappingPathFile, string module)
		{
			SetIoMap(provider, ioMappingPathFile, module);
			DATA.Subscribe(provider, "DIList", SubscribeDiData);
			DATA.Subscribe(provider, "DOList", SubscribeDoData);
			DATA.Subscribe(provider, "AIList", SubscribeAiData);
			DATA.Subscribe(provider, "AOList", SubscribeAoData);
		}

		private void SubscribeIoItemList(string provider)
		{
			var diKey = provider + ".DIItemList";
			if (!_ioItemList.ContainsKey(diKey))
			{
				_ioItemList[diKey] = [];
				DATA.Subscribe(diKey, () => _ioItemList[diKey]);
			}
			var doKey = provider + ".DOItemList";
			if (!_ioItemList.ContainsKey(doKey))
			{
				_ioItemList[doKey] = [];
				DATA.Subscribe(doKey, () => _ioItemList[doKey]);
			}
			var aiKey = provider + ".AIItemList";
			if (!_ioItemList.ContainsKey(aiKey))
			{
				_ioItemList[aiKey] = [];
				DATA.Subscribe(aiKey, () => _ioItemList[aiKey]);
			}
			var aoKey = provider + ".AOItemList";
			if (!_ioItemList.ContainsKey(aoKey))
			{
				_ioItemList[aoKey] = [];
				DATA.Subscribe(aoKey, () => _ioItemList[aoKey]);
			}
		}
	}
}
