|
@@ -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
|
|
|
+ }
|
|
|
+}
|