Browse Source

add twincat releated

chenzk 3 weeks ago
parent
commit
2880ac4a85

+ 13 - 0
Beckoff/BeckhoffCfg.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FestoDebugger.Beckoff
+{
+    public class BeckhoffCfg
+    {
+        public BeckhoffController Controller { get; set; }
+    }
+}

+ 30 - 0
Beckoff/BeckhoffController.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Schema;
+using System.Xml.Serialization;
+
+namespace FestoDebugger.Beckoff
+{
+    public class BeckhoffController
+    {
+        [XmlAttribute(AttributeName = "Name", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string Name { get; set; }
+
+        [XmlAttribute(AttributeName = "IPAddress", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string IPAddress { get; set; }
+
+        [XmlAttribute(AttributeName = "PortAddress", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string PortAddress { get; set; }
+
+        [XmlElement(Type = typeof(BeckhoffInput), ElementName = "Input", IsNullable = false, Form = XmlSchemaForm.Qualified)]
+        public List<BeckhoffInput> Inputs { get; set; }
+
+        [XmlElement(Type = typeof(BeckhoffOutput), ElementName = "Output", IsNullable = false, Form = XmlSchemaForm.Qualified)]
+        public List<BeckhoffOutput> Outputs { get; set; }
+
+
+    }
+}

+ 15 - 0
Beckoff/BeckhoffDIAccessor.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FestoDebugger.Beckoff
+{
+    public class BeckhoffDIAccessor : BeckhoffIOAccessor<bool>
+    {
+        public BeckhoffDIAccessor(string name, string address, string scaling) : base(name, address, scaling, "bool")
+        {
+        }
+    }
+}

+ 27 - 0
Beckoff/BeckhoffDOAccessor.cs

@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FestoDebugger.Beckoff
+{
+    public class BeckhoffDOAccessor : BeckhoffIOAccessor<bool>
+    {
+        #region 内部变量
+        private bool _bitOperated = false;
+        private int _bit = 0;
+        #endregion
+
+        #region 属性
+        public bool BitOperated { get { return _bitOperated; } }
+
+        public int Bit { get { return _bit; } }
+        #endregion
+        public BeckhoffDOAccessor(string name, string address, string scaling, bool bitOperated, int bit) : base(name, address, scaling, "bool")
+        {
+            _bitOperated = bitOperated;
+            _bit = bit;
+        }
+    }
+}

+ 29 - 0
Beckoff/BeckhoffDelegate.cs

@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FestoDebugger.Beckoff
+{
+    public class BeckhoffDelegate
+    {
+        /// <summary>
+        /// Twincat断开连接
+        /// </summary>
+        public delegate void OnNotifyTwincateDisConnect();
+        /// <summary>
+        /// 变量数值发生变化委托声明
+        /// </summary>
+        /// <param name="variableName"></param>
+        /// <param name="value"></param>
+        public delegate void OnUpdateModuleVariableValue(string variableName, object value);
+        /// <summary>
+        /// 模块变量数值发生变化委托声明
+        /// </summary>
+        /// <param name="moduleName"></param>
+        /// <param name="variableName"></param>
+        /// <param name="value"></param>
+        public delegate void OnModuleUpdateVariableValue(string moduleName, string variableName, object value);
+    }
+}

+ 31 - 0
Beckoff/BeckhoffIOAccessor.cs

@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FestoDebugger.Beckoff
+{
+    public class BeckhoffIOAccessor<T>
+    {
+        private string _name;
+        private string _address;
+        private string _scaling;
+        private string _dataType;
+        public T Value { get; set; }
+
+        public string Name { get { return _name; } set { _name = value; } }
+
+        public string Address { get { return _address; } set { _address = value; } }
+
+        public string dataType { get { return _dataType; } set { _dataType = value; } }
+
+        public BeckhoffIOAccessor(string name, string address, string scaling, string dataType)
+        {
+            this._name = name;
+            this._address = address;
+            this._scaling = scaling;
+            this._dataType = dataType;
+        }
+    }
+}

+ 170 - 0
Beckoff/BeckhoffIOManager.cs

@@ -0,0 +1,170 @@
+using FestoDebugger.Common;
+using FestoDebugger.Service;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FestoDebugger.Beckoff
+{
+    public class BeckhoffIOManager : Singleton<BeckhoffIOManager>
+    {
+        #region 内部常量
+        private const string DI_ITEMLIST = "System.DIItemList";
+        private const string DO_ITEMLIST = "System.DOItemList";
+        private const string DIGITAL = "Digital";
+        #endregion
+
+        #region 内部变量
+        private Dictionary<string, BeckhoffDIAccessor> _diMap = new Dictionary<string, BeckhoffDIAccessor>();
+        private Dictionary<string, BeckhoffDOAccessor> _doMap = new Dictionary<string, BeckhoffDOAccessor>();
+
+        private Dictionary<string, List<NotifiableIoItem>> _ioItemList = new Dictionary<string, List<NotifiableIoItem>>();
+        private int _size = 0;
+        /// <summary>
+        /// 模块变量更新委托字典(key-模块.变量,value-变量委托事件
+        /// </summary>
+        private Dictionary<string, BeckhoffDelegate.OnUpdateModuleVariableValue> _moduleVariableActionDic = new Dictionary<string, BeckhoffDelegate.OnUpdateModuleVariableValue>();
+        /// <summary>
+        /// 变量数值字典(key-io变量名称,value-数值)
+        /// </summary>
+        private Dictionary<string, object> _nameVariableValueDic = new Dictionary<string, object>();
+        /// <summary>
+        /// do对应地址(多个do对应同一址)
+        /// </summary>
+        private List<string> _doAddress = new List<string>();
+        #endregion
+
+        #region 属性
+        /// <summary>
+        /// IO数据长度
+        /// </summary>
+        public int Size { get { return _size; } }
+        #endregion
+        /// <summary>
+        /// 初始化
+        /// </summary>
+        /// <param name="cfg"></param>
+        public void Initialize(BeckhoffCfg cfg)
+        {
+            InitialAccessors(cfg);
+        }
+        /// <summary>
+        /// 初始化
+        /// </summary>
+        /// <param name="cfg"></param>
+        private void InitialAccessors(BeckhoffCfg cfg)
+        {
+            foreach (BeckhoffInput item in cfg.Controller.Inputs)
+            {
+                string key = $"{item.Name}";
+                if (item.Type == DIGITAL)
+                {
+                    if (!_diMap.ContainsKey(key))
+                    {
+                        BeckhoffDIAccessor accessor = new BeckhoffDIAccessor(key, item.Address, item.Scaling);
+                        _diMap[key] = accessor;
+                        _ioItemList[DI_ITEMLIST].Add(new NotifiableIoItem()
+                        {
+                            Address = accessor.Address,
+                            Name = key
+                        });
+                        BeckhoffItemManager.Instance.InitBeckhoffItem(key, item.Address, item.DataType, "di", 1, false, 0, item.Invert);
+                        _size++;
+                    }
+                }
+            }
+            foreach (BeckhoffOutput item in cfg.Controller.Outputs)
+            {
+                string key = $"{item.Name}";
+                if (item.Type == DIGITAL)
+                {
+                    if (!_doMap.ContainsKey(key))
+                    {
+                        BeckhoffDOAccessor accessor = new BeckhoffDOAccessor(key, item.Address, item.Scaling, item.BitOperated, item.Bit);
+                        _doMap[key] = accessor;
+                        _ioItemList[DO_ITEMLIST].Add(new NotifiableIoItem()
+                        {
+                            Address = accessor.Address,
+                            Name = key
+                        });
+                        BeckhoffItemManager.Instance.InitBeckhoffItem(key, item.Address, item.DataType, "do", 1, item.BitOperated, item.Bit, item.Invert);
+                        BeckhoffItemManager.Instance.InitWriteBeckoffItem(key, item.Address, item.DataType, "do", 1, item.BitOperated, item.Bit, item.Invert);
+                        _size++;
+      
+                    }
+                }
+            }
+        }
+        /// <summary>
+        /// 写DO变量数值
+        /// </summary>
+        /// <param name="arg1"></param>
+        /// <param name="args"></param>
+        /// <returns></returns>
+        private bool WriteDoOperation(string arg1, object[] args)
+        {
+            string name = (string)args[0];
+            bool setpoint = (bool)args[1];
+            BeckhoffItem item = BeckhoffItemManager.Instance.GetWriteBeckhoffItem(name);
+            return TwincatAdoManager.Instance.WriteValue(name, setpoint);
+        }
+        
+
+        #region 获取Accessor和Item
+        public BeckhoffDIAccessor GetDIAccessor(string name)
+        {
+            return _diMap.ContainsKey(name) ? _diMap[name] : null;
+        }
+        public BeckhoffDOAccessor GetDOAccessor(string name)
+        {
+            return _doMap.ContainsKey(name) ? _doMap[name] : null;
+        }
+
+        #endregion
+
+        /// <summary>
+        /// 注册变量数值发生变化回调
+        /// </summary>
+        /// <param name="moduleName"></param>
+        /// <param name="variable"></param>
+        /// <param name="onUpdateModuleVariableValue"></param>
+        public void SubscribeModuleVariable(string moduleName, string variable, BeckhoffDelegate.OnUpdateModuleVariableValue onUpdateModuleVariableValue)
+        {
+            string name = $"{moduleName}.{variable}";
+            _moduleVariableActionDic[name] = onUpdateModuleVariableValue;
+            //根据模块变量名称获取相应的IO变量名称
+            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName(name);
+            if (!string.IsNullOrEmpty(ioName))
+            {
+                if (_nameVariableValueDic.ContainsKey(ioName))
+                {
+                    _moduleVariableActionDic[name].Invoke(variable, _nameVariableValueDic[ioName]);
+                }
+            }
+        }
+        /// <summary>
+        /// 更新IO数值
+        /// </summary>
+        /// <param name="name"></param>
+        /// <param name="value"></param>
+        public void UpdateIoValue(string name, object value)
+        {
+            _nameVariableValueDic[name] = value;
+            //根据io变量名称获取模块变量名称
+            string innerModuleName = BeckhoffModuleIOManager.Instance.GetInnerModuleNameByIOName(name);
+            if (!string.IsNullOrEmpty(innerModuleName))
+            {
+                if (_moduleVariableActionDic.ContainsKey(innerModuleName))
+                {
+                    string[] strAry = innerModuleName.Split('.');
+                    if (strAry.Length != 0)
+                    {
+                        _moduleVariableActionDic[innerModuleName].Invoke(strAry[strAry.Length - 1], value);
+                    }
+                }
+            }
+        }
+    }
+}

+ 37 - 0
Beckoff/BeckhoffInput.cs

@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Schema;
+using System.Xml.Serialization;
+
+namespace FestoDebugger.Beckoff
+{
+    public class BeckhoffInput
+    {
+        [XmlAttribute(AttributeName = "Name", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string Name { get; set; }
+
+        [XmlAttribute(AttributeName = "Address", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string Address { get; set; }
+
+        [XmlAttribute(AttributeName = "Invert", Form = XmlSchemaForm.Unqualified, DataType = "boolean")]
+        public bool Invert { get; set; }
+
+        [XmlAttribute(AttributeName = "Type", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string Type { get; set; }
+
+        [XmlAttribute(AttributeName = "DataType", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string DataType { get; set; }
+
+        [XmlAttribute(AttributeName = "Scaling", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string Scaling { get; set; }
+
+        [XmlAttribute(AttributeName = "Clip", Form = XmlSchemaForm.Unqualified, DataType = "boolean")]
+        public bool Clip { get; set; }
+
+        [XmlAttribute(AttributeName = "ModuleName", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string ModuleName { get; set; }
+    }
+}

+ 53 - 0
Beckoff/BeckhoffItem.cs

@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FestoDebugger.Beckoff
+{
+    public class BeckhoffItem
+    {
+        public string Name { get; }
+
+        public string Address { get; }
+        public string DataType { get; }
+
+        public string IoType { get; }
+
+        public int Size { get; }
+
+        public string Scaling { get; }
+
+        public bool BitOperated { get; }
+
+        public int Bit { get; }
+
+        public bool Invert { get; }
+
+        public BeckhoffItem(string name, string address, string dataType, string scaling, string ioType, int size, bool bitOperated, int bit, bool invert)
+        {
+            Name = name;
+            DataType = dataType;
+            IoType = ioType;
+            Size = size;
+            Address = address;
+            Scaling = scaling;
+            BitOperated = bitOperated;
+            Bit = bit;
+            Invert = invert;
+        }
+
+        public BeckhoffItem(string name, string address, string dataType, string ioType, int size, bool bitOperated, int bit, bool invert)
+        {
+            Name = name;
+            DataType = dataType;
+            IoType = ioType;
+            Size = size;
+            Address = address;
+            BitOperated = bitOperated;
+            Bit = bit;
+            Invert = invert;
+        }
+    }
+}

+ 44 - 0
Beckoff/BeckhoffItemManager.cs

@@ -0,0 +1,44 @@
+using FestoDebugger.Service;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FestoDebugger.Beckoff
+{
+    public class BeckhoffItemManager : Singleton<BeckhoffItemManager>
+    {
+
+        private Dictionary<string, BeckhoffItem> _ioItemMap = new Dictionary<string, BeckhoffItem>();
+
+        private Dictionary<string, BeckhoffItem> _writeItemMap = new Dictionary<string, BeckhoffItem>();
+
+        public BeckhoffItem GetIOBeckhoffItem(string name)
+        {
+            return _ioItemMap.ContainsKey(name) ? _ioItemMap[name] : null;
+        }
+
+        public BeckhoffItem GetWriteBeckhoffItem(string name)
+        {
+            return _writeItemMap.ContainsKey(name) ? _writeItemMap[name] : null;
+        }
+        public void InitBeckhoffItem(string name, string address, string dataType, string ioType, int size, bool bitOperated, int bit, bool invert)
+        {
+            _ioItemMap[name] = new BeckhoffItem(name, address, dataType, ioType, size, bitOperated, bit, invert);
+        }
+
+        public void InitWriteBeckoffItem(string name, string address, string dataType, string ioType, int size, bool bitOperated, int bit, bool invert)
+        {
+            _writeItemMap[name] = new BeckhoffItem(name, address, dataType, ioType, size, bitOperated, bit, invert);
+        }
+        public List<BeckhoffItem> GetIOItems()
+        {
+            return _ioItemMap.Values.ToList();
+        }
+        public List<BeckhoffItem> GetWriteItems()
+        {
+            return _writeItemMap.Values.ToList();
+        }
+    }
+}

+ 49 - 0
Beckoff/BeckhoffManager.cs

@@ -0,0 +1,49 @@
+using FestoDebugger.Service;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FestoDebugger.Beckoff
+{
+    public class BeckhoffManager : Singleton<BeckhoffManager>
+    {
+        #region 内部常量
+        private const string DI_ITEMLIST = "System.DIItemList";
+        private const string DO_ITEMLIST = "System.DOItemList";
+        private const string AI_ITEMLIST = "System.AIItemList";
+        private const string AO_ITEMLIST = "System.AOItemList";
+        private const string DIGITAL = "Digital";
+        private const string ANALOG = "Analog";
+        #endregion
+        #region 内部变量
+        BeckhoffCfg _cfg = null;
+        #endregion
+
+        #region 属性
+        public BeckhoffCfg BeckhoffCfg
+        {
+            get { return _cfg; }
+            set { _cfg = value; }
+        }
+        #endregion
+        /// <summary>
+        /// 初始化
+        /// </summary>
+        public void Initialize()
+        {
+            string xmlPath = "";
+    
+            xmlPath = Directory.GetParent(Directory.GetCurrentDirectory()).FullName + "Config\\Beckhoffcfg.xml";
+            
+            BeckhoffModuleIOManager.Instance.Initialize(Directory.GetParent(Directory.GetCurrentDirectory()).FullName + "Config\\ModuleIoCfg.xml");
+            _cfg = CustomXmlSerializer.Deserialize<BeckhoffCfg>(new FileInfo(xmlPath));
+            if (_cfg != null)
+            {
+                BeckhoffIOManager.Instance.Initialize(_cfg);
+            }
+        }
+    }
+}

+ 19 - 0
Beckoff/BeckhoffModule.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Schema;
+using System.Xml.Serialization;
+
+namespace FestoDebugger.Beckoff
+{
+    public class BeckhoffModule
+    {
+        [XmlAttribute(AttributeName = "Name", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string Name { get; set; }
+
+        [XmlElement(Type = typeof(BeckhoffModuleIOInfo), ElementName = "IO", IsNullable = false, Form = XmlSchemaForm.Qualified)]
+        public List<BeckhoffModuleIOInfo> IOs { get; set; }
+    }
+}

+ 16 - 0
Beckoff/BeckhoffModuleIOCfg.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Schema;
+using System.Xml.Serialization;
+
+namespace FestoDebugger.Beckoff
+{
+    public class BeckhoffModuleIOCfg
+    {
+        [XmlElement(Type = typeof(BeckhoffModule), ElementName = "Module", IsNullable = false, Form = XmlSchemaForm.Qualified)]
+        public List<BeckhoffModule> Modules { get; set; }
+    }
+}

+ 19 - 0
Beckoff/BeckhoffModuleIOInfo.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Schema;
+using System.Xml.Serialization;
+
+namespace FestoDebugger.Beckoff
+{
+    public class BeckhoffModuleIOInfo
+    {
+        [XmlAttribute(AttributeName = "Name", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string Name { get; set; }
+
+        [XmlAttribute(AttributeName = "IOName", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string IOName { get; set; }
+    }
+}

+ 62 - 0
Beckoff/BeckhoffModuleIOManager.cs

@@ -0,0 +1,62 @@
+using FestoDebugger.Service;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FestoDebugger.Beckoff
+{
+    public class BeckhoffModuleIOManager : Singleton<BeckhoffModuleIOManager>
+    {
+        #region 内部变量
+        private BeckhoffModuleIOCfg _cfg;
+        /// <summary>
+        /// 模块IO字典(key-模块名称,value-beckhoff 变量名称)
+        /// </summary>
+        private Dictionary<string, string> _moduleNameIODic = new Dictionary<string, string>();
+        /// <summary>
+        /// Beckhoff (IO-模块名称字典) 
+        /// </summary>
+        private Dictionary<string, string> _ioModuleNameDic = new Dictionary<string, string>();
+        #endregion
+        /// <summary>
+        /// 初始化
+        /// </summary>
+        /// <param name="xmlPath"></param>
+        public void Initialize(string xmlPath)
+        {
+            _cfg = CustomXmlSerializer.Deserialize<BeckhoffModuleIOCfg>(new FileInfo(xmlPath));
+            if (_cfg != null)
+            {
+                foreach (BeckhoffModule item in _cfg.Modules)
+                {
+                    foreach (BeckhoffModuleIOInfo io in item.IOs)
+                    {
+                        _moduleNameIODic[io.Name] = io.IOName;
+                        _ioModuleNameDic[io.IOName] = io.Name;
+                    }
+                }
+            }
+        }
+        /// <summary>
+        /// 通过获取模块名称获取IO名称
+        /// </summary>
+        /// <param name="key"></param>
+        /// <returns></returns>
+        public string GetIoNameByInnerModuleName(string key)
+        {
+            return _moduleNameIODic.ContainsKey(key) ? _moduleNameIODic[key] : "";
+        }
+        /// <summary>
+        /// 通过IO名称获取模块名称
+        /// </summary>
+        /// <param name="key"></param>
+        /// <returns></returns>
+        public string GetInnerModuleNameByIOName(string key)
+        {
+            return _ioModuleNameDic.ContainsKey(key) ? _ioModuleNameDic[key] : "";
+        }
+    }
+}

+ 43 - 0
Beckoff/BeckhoffOutput.cs

@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Schema;
+using System.Xml.Serialization;
+
+namespace FestoDebugger.Beckoff
+{
+    public class BeckhoffOutput
+    {
+        [XmlAttribute(AttributeName = "Name", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string Name { get; set; }
+
+        [XmlAttribute(AttributeName = "Address", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string Address { get; set; }
+
+        [XmlAttribute(AttributeName = "Type", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string Type { get; set; }
+
+        [XmlAttribute(AttributeName = "DataType", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string DataType { get; set; }
+
+        [XmlAttribute(AttributeName = "BitOperated", Form = XmlSchemaForm.Unqualified, DataType = "boolean")]
+        public bool BitOperated { get; set; }
+
+        [XmlAttribute(AttributeName = "Bit", Form = XmlSchemaForm.Unqualified, DataType = "int")]
+        public int Bit { get; set; }
+
+        [XmlAttribute(AttributeName = "Invert", Form = XmlSchemaForm.Unqualified, DataType = "boolean")]
+        public bool Invert { get; set; }
+
+        [XmlAttribute(AttributeName = "Scaling", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string Scaling { get; set; }
+
+        [XmlAttribute(AttributeName = "Clip", Form = XmlSchemaForm.Unqualified, DataType = "boolean")]
+        public bool Clip { get; set; }
+
+        [XmlAttribute(AttributeName = "ModuleName", Form = XmlSchemaForm.Unqualified, DataType = "string")]
+        public string ModuleName { get; set; }
+    }
+}

+ 420 - 0
Beckoff/TwincatAdoManager.cs

@@ -0,0 +1,420 @@
+using FestoDebugger.Service;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TwinCAT.Ads;
+
+namespace FestoDebugger.Beckoff
+{
+    public class TwincatAdoManager : Singleton<TwincatAdoManager>
+    {
+        #region 内部变量
+        private TcAdsClient _adsClient = null;
+        private string _ipAddress = "";
+        private int _port = 851;
+        private BeckhoffCfg _cfg;
+        private Dictionary<int, BeckhoffItem> _handleItemDic = new Dictionary<int, BeckhoffItem>();
+        private Dictionary<string, int> _notificationNameHandleDic = new Dictionary<string, int>();
+        private Dictionary<string, int> _writeNameHandleDic = new Dictionary<string, int>();
+        private AdsStream _adsStream;
+        private BinaryReader _reader;
+        private bool _isConnected = false;
+        /// <summary>
+        /// BitOperated地址数值字典(key-BitOperated为true地址,value-数值)
+        /// </summary>
+        private Dictionary<string, byte> _bitOperatedAddressValueDic = new Dictionary<string, byte>();
+        /// <summary>
+        /// BitOperated锁
+        /// </summary>
+        private object _bitOperatedLocker = new object();
+        #endregion
+
+        #region 属性
+        public bool IsConnect
+        {
+            get { return _isConnected; }
+            set { _isConnected = value; }
+        }
+        /// <summary>
+        /// Twincat断开连接
+        /// </summary>
+        public event BeckhoffDelegate.OnNotifyTwincateDisConnect OnTwincateDisConnect;
+        #endregion
+
+        /// <summary>
+        /// 初始化
+        /// </summary>
+        public void Initialize()
+        {
+            Init(BeckhoffManager.Instance.BeckhoffCfg, 100);
+        }
+        /// <summary>
+        /// 初始化
+        /// </summary>
+        /// <param name="ipAddress"></param>
+        /// <param name="port"></param>
+        private void Init(BeckhoffCfg beckhoffCfg, int size)
+        {
+            _adsClient = new TcAdsClient();
+            _adsClient.ConnectionStateChanged += AdsClient_ConnectionStateChanged;
+            _cfg = beckhoffCfg;
+            _ipAddress = beckhoffCfg.Controller.IPAddress;
+            _port = int.Parse(beckhoffCfg.Controller.PortAddress);
+            _adsStream = new AdsStream(size);
+            _reader = new BinaryReader(_adsStream);
+            _adsClient.AdsNotification += AdsClient_AdsNotification;
+            _adsClient.AdsNotificationError += AdsClient_AdsNotificationError;
+            Start();
+        }
+
+        private void AdsClient_AdsNotificationError(object sender, AdsNotificationErrorEventArgs e)
+        {
+        }
+
+        /// <summary>
+        /// 连接状态变化
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void AdsClient_ConnectionStateChanged(object sender, TwinCAT.ConnectionStateChangedEventArgs e)
+        {
+            if (e.NewState == TwinCAT.ConnectionState.Connected)
+            {
+                try
+                {
+                    StateInfo stateInfo = _adsClient.ReadState();
+                    CreateWriteHandle();
+                    SubscribeNotification();
+                    _isConnected = true;
+                }
+                catch (Exception ex)
+                {
+                    if (OnTwincateDisConnect != null)
+                    {
+                        OnTwincateDisConnect();
+                    }
+                    _isConnected = false;
+                  
+                    return;
+                }
+            }
+            else
+            {
+                _isConnected = false;
+            }
+        }
+
+        /// <summary>
+        /// 启动
+        /// </summary>
+        public void Start()
+        {
+            try
+            {
+                _adsClient.Connect(_ipAddress, _port);
+            }
+            catch
+            {
+
+            }
+        }
+        /// <summary>
+        /// 订阅变量
+        /// </summary>
+        private void SubscribeNotification()
+        {
+            _notificationNameHandleDic.Clear();
+            _handleItemDic.Clear();
+            int count = 0;
+            List<BeckhoffItem> beckhoffItems = BeckhoffItemManager.Instance.GetIOItems();
+            foreach (BeckhoffItem item in beckhoffItems)
+            {
+                try
+                {
+                    int handle = _adsClient.AddDeviceNotification(item.Address, _adsStream, count, item.Size, AdsTransMode.OnChange, 0, 0, item);
+                    _notificationNameHandleDic[item.Name] = handle;
+                    _handleItemDic[handle] = item;
+                    count += item.Size;
+                }
+                catch (Exception ex)
+                {
+                   
+                }
+            }
+        }
+        /// <summary>
+        /// 创建写入句柄
+        /// </summary>
+        private void CreateWriteHandle()
+        {
+            _writeNameHandleDic.Clear();
+            foreach (BeckhoffItem item in BeckhoffItemManager.Instance.GetWriteItems())
+            {
+                try
+                {
+                    int handle = _adsClient.CreateVariableHandle(item.Address);
+                    _writeNameHandleDic[item.Name] = handle;
+                }
+                catch
+                {
+                    
+                }
+            }
+        }
+        /// <summary>
+        /// 写入数值
+        /// </summary>
+        /// <param name="name"></param>
+        /// <param name="value"></param>
+        /// <returns></returns>
+        public bool WriteValue(string name, object value)
+        {
+            if (_writeNameHandleDic.ContainsKey(name))
+            {
+                if (!_isConnected)
+                {
+                   
+                    return false;
+                }
+                int handle = _writeNameHandleDic[name];
+                BeckhoffItem item = BeckhoffItemManager.Instance.GetIOBeckhoffItem(name);
+                object convertedValue = value;
+
+                try
+                {
+                    if (item != null && item.BitOperated)
+                    {
+                        if (item.Invert && convertedValue is bool)
+                        {
+                            convertedValue = !(bool)convertedValue;
+                        }
+                        lock (_bitOperatedLocker)
+                        {
+                            convertedValue = GetBeckoffItemValue(item, convertedValue);
+                            _adsClient.WriteAny(handle, convertedValue);
+                            _bitOperatedAddressValueDic[item.Address] = (byte)convertedValue;
+                        }
+                    }
+                    else
+                    {
+                        _adsClient.WriteAny(handle, convertedValue);
+                    }
+                    return true;
+                }
+                catch (Exception ex)
+                {
+                    return false;
+                }
+            }
+            else
+            {
+            }
+            return false;
+        }
+        /// <summary>
+        /// 获取Beckhoff项数值(用于解决BitOperated为true)
+        /// </summary>
+        /// <param name="name"></param>
+        /// <param name="value"></param>
+        /// <returns></returns>
+        private object GetBeckoffItemValue(BeckhoffItem item, object value)
+        {
+            if (_bitOperatedAddressValueDic.ContainsKey(item.Address))
+            {
+                byte addressValue = _bitOperatedAddressValueDic[item.Address];
+                bool[] boolArray = Converter.GetboolArrayByByte(addressValue);
+                bool boolValue = (bool)value;
+                if (item.Bit < boolArray.Length)
+                {
+                    boolArray[item.Bit] = boolValue;
+                }
+                byte byt = Converter.GetByteValueByBoolArray(boolArray);
+                return byt;
+            }
+            else
+            {
+                return value;
+            }
+        }
+        /// <summary>
+        /// 变量数值发生变化事件
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void AdsClient_AdsNotification(object sender, AdsNotificationEventArgs e)
+        {
+            try
+            {
+                BeckhoffItem item = null;
+                if (_handleItemDic.ContainsKey(e.NotificationHandle))
+                {
+                    item = _handleItemDic[e.NotificationHandle];
+                }
+                else
+                {
+                    item = (BeckhoffItem)e.UserData;
+                }
+                e.DataStream.Position = e.Offset;
+                if (item != null)
+                {
+                    switch (item.IoType)
+                    {
+                        case "di":
+                            BeckhoffDIAccessor diAccessor = BeckhoffIOManager.Instance.GetDIAccessor(item.Name);
+                            if (diAccessor != null)
+                            {
+                                diAccessor.Value = _reader.ReadBoolean();
+                                if (item.Invert)
+                                {
+                                    BeckhoffIOManager.Instance.UpdateIoValue(item.Name, !diAccessor.Value);
+                                }
+                                else
+                                {
+                                    BeckhoffIOManager.Instance.UpdateIoValue(item.Name, diAccessor.Value);
+                                }
+                            }
+                            break;
+                        case "do":
+                            BeckhoffDOAccessor doAccessor = BeckhoffIOManager.Instance.GetDOAccessor(item.Name);
+                            if (doAccessor != null)
+                            {
+                                if (item.BitOperated)
+                                {
+                                    byte byt = _reader.ReadByte();
+                                    lock (_bitOperatedLocker)
+                                    {
+                                        _bitOperatedAddressValueDic[doAccessor.Address] = byt;
+                                    }
+                                    doAccessor.Value = ((byt >> item.Bit) & 0x01) == 0x01;
+                                }
+                                else
+                                {
+                                    doAccessor.Value = _reader.ReadBoolean();
+                                }
+                                if (item.Invert)
+                                {
+                                    BeckhoffIOManager.Instance.UpdateIoValue(item.Name, !doAccessor.Value);
+                                }
+                                else
+                                {
+                                    BeckhoffIOManager.Instance.UpdateIoValue(item.Name, doAccessor.Value);
+                                }
+                            }
+                            break;
+                       
+
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+            }
+        }
+        /// <summary>
+        /// 获取模拟数量数值 
+        /// </summary>
+        /// <param name="dataType"></param>
+        /// <returns></returns>
+        private double GetAnalogValue(string dataType)
+        {
+            return double.Parse(GetDataTypeValue(dataType).ToString());
+        }
+        /// <summary>
+        /// 获取数据类型数值
+        /// </summary>
+        /// <param name="dataType"></param>
+        /// <returns></returns>
+        private object GetDataTypeValue(string dataType)
+        {
+            switch (dataType)
+            {
+                case "byte":
+                    return _reader.ReadByte();
+                case "sint":
+                    return _reader.ReadSByte();
+                case "usint":
+                    return _reader.ReadByte();
+                case "short":
+                    return _reader.ReadInt16();
+                case "int":
+                    return _reader.ReadInt16();
+                case "uint":
+                    return _reader.ReadUInt16();
+                case "dint":
+                    return _reader.ReadInt32();
+                case "udint":
+                    return _reader.ReadUInt32();
+                case "long":
+                    return _reader.ReadInt64();
+                case "float":
+                    return _reader.ReadSingle();
+                case "ulong":
+                    return _reader.ReadUInt64();
+                case "double":
+                    return _reader.ReadDouble();
+                case "bool":
+                    return _reader.ReadBoolean();
+                default:
+                    return _reader.ReadDouble();
+            }
+        }
+
+        /// <summary>
+        /// 停止
+        /// </summary>
+        public void Stop()
+        {
+            _adsClient.Dispose();
+        }
+
+        #region 支持写入结构体
+        /* T必须字段必须为连续地址,
+         *  [StructLayout(LayoutKind.Sequential)]
+            public class Tag
+            {
+                [MarshalAs(UnmanagedType.I1)]
+                public bool DO_PVN21; 
+                [MarshalAs(UnmanagedType.I1)]
+                public bool DO_PV11;
+                [MarshalAs(UnmanagedType.R4)]
+                public float AI_Chamber_Pressure_10t;
+            }
+         * */
+        /// <summary>
+        /// 写入类
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="name"></param>
+        /// <param name="data"></param>
+        /// <returns></returns>
+        public (bool success, int status) WriteStruct<T>(string name, T data)
+        {
+            if (_writeNameHandleDic.ContainsKey(name))
+            {
+                if (!_isConnected)
+                {
+                    return (false, 0);
+                }
+                try
+                {
+                    int handle = _writeNameHandleDic[name];
+
+                    _adsClient.WriteAny(handle, data);
+                    return (true, 0);
+                }
+                catch (Exception ex)
+                {
+                    return (false, 1);
+                }
+            }
+            else
+            {
+                return (false, 0);
+            }
+        }
+        #endregion
+    }
+}

+ 63 - 0
Common/DelegateCommand.cs

@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Controls;
+using System.Windows.Input;
+
+namespace FestoDebugger.Common
+{
+    public interface IDelegateCommand : ICommand
+    {
+        void RaiseCanExecuteChanged();
+    }
+
+    public class DelegateCommand<T> : IDelegateCommand
+    {
+        readonly Action<T> _execute;
+        readonly Predicate<T> _canExecute;
+        private Action<object, SelectionChangedEventArgs> selectionAction;
+
+        public event EventHandler CanExecuteChanged;
+
+        public DelegateCommand(Action<T> execute)
+            : this(execute, null)
+        {
+        }
+
+        public DelegateCommand(Action<T> execute, Predicate<T> canExecute)
+        {
+            if (execute == null)
+                throw new ArgumentNullException("execute");
+
+            _execute = execute;
+            _canExecute = canExecute;
+        }
+
+        public DelegateCommand(Action<object, SelectionChangedEventArgs> selectionAction)
+        {
+            this.selectionAction = selectionAction;
+        }
+
+        public bool CanExecute(object parameter)
+        {
+            return _canExecute == null ? true : _canExecute((T)parameter);
+        }
+
+
+        // The CanExecuteChanged is automatically registered by command binding, we can assume that it has some execution logic 
+        // to update the button's enabled\disabled state(though we cannot see). So raises this event will cause the button's state be updated.
+        public void RaiseCanExecuteChanged()
+        {
+            if (CanExecuteChanged != null)
+                CanExecuteChanged(this, EventArgs.Empty);
+        }
+
+        public void Execute(object parameter)
+        {
+            _execute((T)parameter);
+        }
+
+    }
+}

+ 66 - 0
Common/NotifiableIoItem.cs

@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FestoDebugger.Common
+{
+    public class NotifiableIoItem : NotifiableItem
+    {
+        [DataMember]
+        public string Name { get; set; }
+
+        [DataMember]
+        public string Description { get; set; }
+
+        [DataMember]
+        public int Index { get; set; }
+
+        [DataMember]
+        public bool BoolValue { get; set; }
+
+        [DataMember]
+        public short ShortValue { get; set; }
+
+        [DataMember]
+        public double DoubleValue { get; set; }
+
+        [DataMember]
+        public string Address { get; set; }
+
+        [DataMember]
+        public string Provider { get; set; }
+
+        [DataMember]
+        public int BlockOffset { get; set; }
+
+        [DataMember]
+        public int BlockIndex { get; set; }
+
+
+        [DataMember]
+        public bool HoldValue { get; set; }
+
+        [DataMember]
+        public bool CanEdit { get; set; }
+
+        public NotifiableIoItem Clone()
+        {
+            return new NotifiableIoItem()
+            {
+                Address = Address,
+                BlockIndex = BlockIndex,
+                BlockOffset = BlockOffset,
+                BoolValue = BoolValue,
+                Description = Description,
+                HoldValue = HoldValue,
+                Index = Index,
+                Name = Name,
+                Provider = Provider,
+                ShortValue = ShortValue,
+            };
+        }
+    }
+}

+ 59 - 0
Common/NotifiableItem.cs

@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+
+namespace FestoDebugger.Common
+{
+    [Serializable]
+    [DataContract]
+    public class NotifiableItem : INotifyPropertyChanged
+    {
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        public void InvokePropertyChanged(string propertyName)
+        {
+            if (PropertyChanged != null)
+            {
+                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+            }
+        }
+
+        public void InvokePropertyChanged()
+        {
+            PropertyInfo[] ps = this.GetType().GetProperties();
+            foreach (PropertyInfo p in ps)
+            {
+                InvokePropertyChanged(p.Name);
+
+                if (p.PropertyType == typeof(ICommand))
+                {
+                    DelegateCommand<string> cmd = p.GetValue(this, null) as DelegateCommand<string>;
+                    if (cmd != null)
+                        cmd.RaiseCanExecuteChanged();
+
+                }
+            }
+
+            FieldInfo[] fi = this.GetType().GetFields();
+            foreach (FieldInfo p in fi)
+            {
+                InvokePropertyChanged(p.Name);
+
+                if (p.FieldType == typeof(ICommand))
+                {
+                    DelegateCommand<string> cmd = p.GetValue(this) as DelegateCommand<string>;
+                    if (cmd != null)
+                        cmd.RaiseCanExecuteChanged();
+
+                }
+            }
+        }
+
+    }
+}

+ 15 - 0
Config/Beckhoffcfg.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<BeckhoffCfg>
+	<Controller Name="MASTER" IPAddress="10.4.6.75.1.1" PortAddress="851">
+
+
+		<Input Name="r_SRD1_SHUTTER_OPEN" Address="MAIN.SRD_DI_16CHANNEL_EL1819_N303_6" Invert="false" Type="Digital" DataType="bool"/>
+		<Input Name="r_SRD1_SHUTTER_CLOSED" Address="MAIN.SRD_DI_16CHANNEL_EL1819_N303_7" Invert="false" Type="Digital" DataType="bool"/>
+
+		<Output Name="c_QDRD1_LO_PRESSURE" Address="MAIN.MAIN_IO_DO_16CHANNEL_EL2809_N24_11" Type="Digital" DataType="bool"/>
+		<Output Name="c_QDRD2_LO_PRESSURE" Address="MAIN.MAIN_IO_DO_16CHANNEL_EL2809_N24_12" Type="Digital" DataType="bool"/>
+
+		<Output Name="c_QDRD7_LO_PRESSURE" Address="MAIN.MAIN_IO_DO_16CHANNEL_EL2809_N24_17" Type="Digital" DataType="bool"/>
+		<Output Name="c_QDRD8_LO_PRESSURE" Address="MAIN.MAIN_IO_DO_16CHANNEL_EL2809_N24_18" Type="Digital" DataType="bool"/>
+	</Controller>
+</BeckhoffCfg>

+ 36 - 0
Config/ModuleIoCfg.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<BeckhoffModuleIOCfg xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+	<Module Name="SRD1">
+		
+		<IO Name="SRD1.DoorClosed" IOName="r_SRD1_SHUTTER_CLOSED"/>
+		<IO Name="SRD1.DoorOpened" IOName="r_SRD1_SHUTTER_OPEN"/>
+
+	</Module>
+
+	<Module Name="Rinse1">
+
+		<IO Name="Rinse1.N2Valve" IOName="c_QDRD1_LO_PRESSURE"/>
+	</Module>
+
+
+	<Module Name="Rinse2">
+	
+
+		<IO Name="Rinse2.N2Valve" IOName="c_QDRD2_LO_PRESSURE"/>
+	</Module>
+
+	<Module Name="Rinse7">
+
+
+		<IO Name="Rinse7.N2Valve" IOName="c_QDRD7_LO_PRESSURE"/>
+	</Module>
+
+	<Module Name="Rinse8">
+
+>
+		<IO Name="Rinse8.N2Valve" IOName="c_QDRD8_LO_PRESSURE"/>
+	</Module>
+
+
+
+</BeckhoffModuleIOCfg>

+ 0 - 4
FestoDebugger.csproj

@@ -8,8 +8,4 @@
     <UseWPF>true</UseWPF>
   </PropertyGroup>
 
-  <ItemGroup>
-    <Folder Include="Service\" />
-  </ItemGroup>
-
 </Project>

+ 92 - 0
Service/Converter.cs

@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FestoDebugger.Service
+{
+    public static class Converter
+    {
+        ///// <summary>
+        ///// Unit converter from physical to logical in linear formula
+        ///// </summary>
+        ///// <returns></returns>
+        public static double Phy2Logic(double physicalValue, double logicalMin, double logicalMax, double physicalMin, double physicalMax)
+        {
+            var ret = (logicalMax - logicalMin) * (physicalValue - physicalMin) / (physicalMax - physicalMin) + logicalMin;
+            //if (ret < logicalMin) ret = logicalMin;
+            //if (ret > logicalMax) ret = logicalMax;
+            if (double.IsNaN(ret)) throw new DivideByZeroException("Phy2Logic除0操作");
+            return ret;
+        }
+
+        ///// <summary>
+        ///// Unit converter from logical to physical in linear formula
+        ///// </summary>
+        ///// <returns></returns>
+        public static double Logic2Phy(double logicalValue, double logicalMin, double logicalMax, double physicalMin, double physicalMax)
+        {
+            var ret = (physicalMax - physicalMin) * (logicalValue - logicalMin) / (logicalMax - logicalMin) + physicalMin;
+            if (ret < physicalMin) ret = physicalMin;
+            if (ret > physicalMax) ret = physicalMax;
+            if (double.IsNaN(ret))
+                throw new DivideByZeroException("Logic2Phy除0操作");
+            return ret;
+        }
+
+        /// <summary>
+        /// 只按占用的字节转换,不检查范围
+        /// </summary>
+        /// <param name="high"></param>
+        /// <param name="low"></param>
+        /// <returns></returns>
+        public static float TwoUInt16ToFloat(UInt16 high, UInt16 low)
+        {
+            Int32 sum = (high << 16) + (low & 0xFFFF);
+            byte[] bs = BitConverter.GetBytes(sum);
+            return BitConverter.ToSingle(bs, 0);
+        }
+
+        public static int TwoInt16ToInt32(Int16 high, Int16 low)
+        {
+            UInt32 tmp = (ushort)low + (UInt32)(((ushort)high) << 16);
+            return tmp > Int32.MaxValue ? (int)((~tmp + 1) * -1) : (int)tmp;////unsigned to signed
+        }
+
+
+        public static float TwoInt16ToFloat(Int16 high, Int16 low)
+        {
+            Int32 sum = (high << 16) + (low & 0xFFFF);
+            byte[] bs = BitConverter.GetBytes(sum);
+            return BitConverter.ToSingle(bs, 0);
+        }
+
+        public static bool[] GetboolArrayByByte(byte byt)
+        {
+            bool[] ret = new bool[8];
+            ret[0] = ((byte)(byt & 0x01) == 0x01);
+            ret[1] = ((byte)(byt >> 1 & 0x01) == 0x01);
+            ret[2] = ((byte)(byt >> 2 & 0x01) == 0x01);
+            ret[3] = ((byte)(byt >> 3 & 0x01) == 0x01);
+            ret[4] = ((byte)(byt >> 4 & 0x01) == 0x01);
+            ret[5] = ((byte)(byt >> 5 & 0x01) == 0x01);
+            ret[6] = ((byte)(byt >> 6 & 0x01) == 0x01);
+            ret[7] = ((byte)(byt >> 7 & 0x01) == 0x01);
+            return ret;
+        }
+
+        public static byte GetByteValueByBoolArray(bool[] byt)
+        {
+            byte value = 0;
+            for (int i = 0; i < byt.Length; i++)
+            {
+                if (byt[i])
+                {
+                    value += (byte)(1 << i);
+                }
+            }
+            return value;
+        }
+    }
+}

+ 78 - 0
Service/CustomXmlSerializer.cs

@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.IO;
+using System.Xml.Serialization;
+using System.Xml;
+namespace FestoDebugger.Service
+{
+  
+        public class CustomXmlSerializer
+        {
+            static public void Serialize<T>(T t, string fileName)
+            {
+                XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
+                xmlWriterSettings.Indent = true;
+                using (FileStream fileWriter = new FileStream(fileName, FileMode.Create))
+                using (XmlWriter writer = XmlTextWriter.Create(fileWriter, xmlWriterSettings))
+                {
+                    XmlSerializer serializer = new XmlSerializer(t.GetType());
+
+                    serializer.Serialize(writer, t);
+                    writer.Flush();
+                }
+            }
+            static public string Serialize<T>(T t)
+            {
+                StringBuilder buffer = new StringBuilder();
+                using (StringWriter writer = new StringWriter(buffer))
+                {
+                    XmlSerializer serializer = new XmlSerializer(typeof(T));
+                    serializer.Serialize(writer, t);
+                }
+
+                return buffer.ToString();
+            }
+
+            static public T Deserialize<T>(string xmlString)
+            {
+                if (string.IsNullOrWhiteSpace(xmlString))
+                    throw new ArgumentNullException("xmlString");
+
+                using (StringReader reader = new StringReader(xmlString))
+                {
+                    XmlSerializer serializer = new XmlSerializer(typeof(T));
+                    return (T)serializer.Deserialize(reader);
+                }
+            }
+
+            static public T Deserialize<T>(Stream streamReader)
+            {
+                if (streamReader == null)
+                    throw new ArgumentNullException("streamReader");
+                if (streamReader.Length <= 0)
+                    throw new EndOfStreamException("The stream is blank");
+
+                using (XmlReader reader = XmlTextReader.Create(streamReader))
+                {
+                    XmlSerializer deserializer = new XmlSerializer(typeof(T));
+                    return (T)deserializer.Deserialize(reader);
+                }
+            }
+
+            static public T Deserialize<T>(FileInfo fi)
+            {
+                if (fi == null)
+                    throw new ArgumentNullException("fi");
+                if (!fi.Exists)
+                    throw new FileNotFoundException(fi.FullName);
+
+                using (FileStream fileReader = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read))
+                    return Deserialize<T>(fileReader);
+            }
+
+        }
+    
+}

+ 6 - 144
ViewModels/MainViewModel.cs

@@ -1,4 +1,5 @@
-using FestoDebugger.Models;
+using FestoDebugger.Beckoff;
+using FestoDebugger.Models;
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
@@ -17,61 +18,15 @@ namespace FestoDebugger.ViewModels
     public class MainViewModel : ViewModelBase
     {
         #region 内部变量
-        private string _ipAddress = "192.168.1.1";
 
-        private string _port = "502";
-
-        private string _connectionStatus = "未连接";
-        
-        private bool _isConnecting;
-        
-        private bool _isConnected;
 
         private ObservableCollection<SignalModuleData> _signalModuleDatas;
 
         #endregion
 
         #region 属性
-        public string IpAddress
-        {
-            get => _ipAddress;
-            set
-            {
-                if (_ipAddress != value)
-                {
-                    _ipAddress = value;
-                    OnPropertyChanged(nameof(IpAddress));
-                }
-            }
-        }
-
-        public string Port
-        {
-            get => _port;
-            set
-            {
-                if (_port != value)
-                {
-                    _port = value;
-                    OnPropertyChanged(nameof(Port));
-                }
-            }
-        }
 
 
-        public string ConnectionStatus
-        {
-            get => _connectionStatus;
-            set
-            {
-                if (_connectionStatus != value)
-                {
-                    _connectionStatus = value;
-                    OnPropertyChanged(nameof(ConnectionStatus));
-                }
-            }
-        }
-
         public ObservableCollection<SignalModuleData> SignalModuleDatas
         {
             get => _signalModuleDatas;
@@ -80,18 +35,10 @@ namespace FestoDebugger.ViewModels
 
         #endregion
 
-        public bool IsNotConnecting => !_isConnecting;
-
-        public string ConnectButtonText => _isConnected ? "断开连接" : "连接设备";
-
-        public Brush StatusColor => _isConnected ? Brushes.Green : Brushes.Red;
-
-        public ICommand ConnectCommand { get; private set; }
-
         public MainViewModel()
         {
+            LoadSignalConfig();
 
-            ConnectCommand = new RelayCommand(async () => await ToggleConnection());
             SignalModuleDatas = new ObservableCollection<SignalModuleData>();
             for (int i = 1; i < 11; i++)
             {
@@ -109,96 +56,11 @@ namespace FestoDebugger.ViewModels
         /// </summary>
         private void LoadSignalConfig()
         {
-
-        }
-
-        private async Task ToggleConnection()
-        {
-            if (_isConnected)
-            {
-                Disconnect();
-                return;
-            }
-
-            await Connect();
-        }
-
-        private async Task Connect()
-        {
-            if (!IPAddress.TryParse(IpAddress, out var ipAddress) || !int.TryParse(Port, out var port))
-            {
-                ConnectionStatus = "IP或端口格式错误";
-                return;
-            }
-
-            _isConnecting = true;
-            OnPropertyChanged(nameof(IsNotConnecting));
-            ConnectionStatus = "连接中...";
-
-            try
-            {
-                using var client = new TcpClient();
-                var connectTask = client.ConnectAsync(ipAddress, port);
-
-                if (await Task.WhenAny(connectTask, Task.Delay(5000)) == connectTask)
-                {
-                    if (client.Connected)
-                    {
-                        _isConnected = true;
-                        ConnectionStatus = "已连接";
-                    }
-                    else
-                    {
-                        ConnectionStatus = "连接失败";
-                    }
-                }
-                else
-                {
-                    ConnectionStatus = "连接超时";
-                }
-            }
-            catch (Exception ex)
-            {
-                ConnectionStatus = $"错误: {ex.Message}";
-            }
-            finally
-            {
-                _isConnecting = false;
-                OnPropertyChanged(nameof(IsNotConnecting));
-                OnPropertyChanged(nameof(ConnectButtonText));
-                OnPropertyChanged(nameof(StatusColor));
-            }
+            BeckhoffManager.Instance.Initialize();
         }
 
-        private void Disconnect()
-        {
-            _isConnected = false;
-            ConnectionStatus = "已断开";
-            OnPropertyChanged(nameof(ConnectButtonText));
-            OnPropertyChanged(nameof(StatusColor));
-        }
-
-
     }
+}
 
-    public class RelayCommand : ICommand
-        {
-            private readonly Func<Task> _execute;
-            private readonly Func<bool> _canExecute;
-
-            public event EventHandler CanExecuteChanged;
-
-            public RelayCommand(Func<Task> execute, Func<bool> canExecute = null)
-            {
-                _execute = execute ?? throw new ArgumentNullException(nameof(execute));
-                _canExecute = canExecute;
-            }
-
-            public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true;
-
-            public async void Execute(object parameter) => await _execute();
-
-            public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
-        }
-    }
+  
 

+ 1 - 49
Views/MainWindow.xaml

@@ -13,59 +13,11 @@
         <local1:MainViewModel />
     </Window.DataContext>
     <Canvas>
-        
-        <Grid Height="40" Width="700" Margin="10,10,0,0" Panel.ZIndex="1">
-            <Grid.RowDefinitions>
-                <RowDefinition Height="30"/>
-            </Grid.RowDefinitions>
-            <Grid.ColumnDefinitions>
-                <ColumnDefinition Width="50"/>
-                <ColumnDefinition Width="100"/>
-                <ColumnDefinition Width="50"/>
-                <ColumnDefinition Width="50"/>
-                <ColumnDefinition Width="100"/>
-                <ColumnDefinition Width="100"/>
-            </Grid.ColumnDefinitions>
-
-            <!-- IP地址输入 -->
-            <TextBlock Text="IP地址:" 
-                   Grid.Row="0" Grid.Column="0"
-                   VerticalAlignment="Center"/>
-            <TextBox x:Name="IpTextBox"
-                 Grid.Row="0" Grid.Column="1"
-                 Text="{Binding IpAddress, UpdateSourceTrigger=PropertyChanged}"
-                 VerticalContentAlignment="Center" TextChanged="IpTextBox_TextChanged"/>
-
-            <!-- 端口输入 -->
-            <TextBlock Text="端口:" 
-                   Grid.Row="0" Grid.Column="2"
-                   Margin="10,0,0,0"
-                   VerticalAlignment="Center"/>
-            <TextBox x:Name="PortTextBox"
-                 Grid.Row="0" Grid.Column="3"
-                 Margin="0,0,0,0"
-                 Text="{Binding Port, UpdateSourceTrigger=PropertyChanged}"
-                 VerticalContentAlignment="Center"/>
-
-            <!-- 连接按钮 -->
-            <Button Content="Connect" Width="70"
-                Margin="10,0,0,0"
-                Grid.Row="0" Grid.Column="4"
-                Command="{Binding ConnectCommand}"
-                IsEnabled="{Binding IsNotConnecting}"/>
-
-            <TextBlock Text="{Binding ConnectionStatus}"
-                   Grid.Row="0" Grid.Column="5" 
-                   Margin="10,10,0,0"
-                   Foreground="{Binding StatusColor}"
-                   HorizontalAlignment="Center"/>
-
-        </Grid>
         <ScrollViewer Height="450" Width="800">
         <ItemsControl ItemsSource="{Binding SignalModuleDatas}">
             <ItemsControl.ItemsPanel>
                 <ItemsPanelTemplate>
-                    <WrapPanel Orientation="Horizontal" Margin="20,50,20,0"/>
+                    <WrapPanel Orientation="Horizontal" Margin="20,20,20,0"/>
                 </ItemsPanelTemplate>
             </ItemsControl.ItemsPanel>
             <ItemsControl.ItemTemplate>