2023-09-07 11:10:17 +08:00
|
|
|
|
using Aitex.Core.RT.Event;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
using Aitex.Core.RT.Log;
|
|
|
|
|
using Aitex.Core.RT.SCCore;
|
2023-09-07 11:10:17 +08:00
|
|
|
|
using Aitex.Core.Util;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
using MECF.Framework.Common.Communications;
|
|
|
|
|
using MECF.Framework.Common.Event;
|
|
|
|
|
using MECF.Framework.Common.PLC;
|
|
|
|
|
using MECF.Framework.RT.Core.IoProviders.Siemens;
|
2023-05-09 14:14:18 +08:00
|
|
|
|
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.OffsetManage;
|
2023-09-07 11:10:17 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Xml;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
|
|
|
|
namespace Aitex.Core.RT.Device.Devices
|
|
|
|
|
{
|
|
|
|
|
public class TcAds : BaseDevice, IConnectable, IConnectionContext, IDevice, IConnection, IAdsPlc
|
|
|
|
|
{
|
|
|
|
|
#region IConnectionContext
|
|
|
|
|
|
|
|
|
|
public int RetryConnectIntervalMs => 1000;
|
2023-09-14 17:33:18 +08:00
|
|
|
|
|
|
|
|
|
//这个属性是底层库重连次数,超过重连次数,以后不在重新连接
|
2023-04-13 11:51:03 +08:00
|
|
|
|
public int MaxRetryConnectCount => 5;
|
|
|
|
|
public bool EnableCheckConnection => true;
|
|
|
|
|
public virtual string Address { get; }
|
|
|
|
|
public bool IsAscii { get; }
|
|
|
|
|
public string NewLine { get; }
|
|
|
|
|
public bool EnableLog { get; }
|
|
|
|
|
|
2023-09-07 11:10:17 +08:00
|
|
|
|
private int MaxFailCount = 3;
|
|
|
|
|
|
|
|
|
|
private int readPLCFailCount;
|
|
|
|
|
|
|
|
|
|
private R_TRIG trig_readPLCFailCount = new R_TRIG();
|
|
|
|
|
|
|
|
|
|
private int writePLCFailCount;
|
|
|
|
|
|
|
|
|
|
private R_TRIG trig_writePLCFailCount = new R_TRIG();
|
|
|
|
|
|
|
|
|
|
private R_TRIG trig_Reconnection = new R_TRIG();
|
|
|
|
|
|
|
|
|
|
private PeriodicJob _thread;
|
|
|
|
|
|
|
|
|
|
#endregion IConnectionContext
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
|
|
|
|
#region IConnectable
|
2023-09-07 11:10:17 +08:00
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
public event Action<string> OnCommunicationError;
|
2023-09-07 11:10:17 +08:00
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
public event Action<string> OnAsciiDataReceived;
|
2023-09-07 11:10:17 +08:00
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
public event Action<byte[]> OnBinaryDataReceived;
|
|
|
|
|
|
2023-09-07 11:10:17 +08:00
|
|
|
|
#endregion IConnectable
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
|
|
|
|
public event Action OnDisconnected;
|
|
|
|
|
|
|
|
|
|
bool IAdsPlc.CheckIsConnected()
|
|
|
|
|
{
|
|
|
|
|
return Connection.IsConnected;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IConnection.IsConnected
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return Connection.IsConnected;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IConnection.Connect()
|
|
|
|
|
{
|
|
|
|
|
Connect(out _);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IConnection.Disconnect()
|
|
|
|
|
{
|
|
|
|
|
Disconnect(out _);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public FsmConnection Connection { get; set; } = new FsmConnection();
|
|
|
|
|
|
|
|
|
|
//public bool IsCommunicationError
|
|
|
|
|
//{
|
|
|
|
|
// get
|
|
|
|
|
// {
|
|
|
|
|
// return _isCommunicationError;
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
//private bool _isCommunicationError;
|
2023-09-07 11:10:17 +08:00
|
|
|
|
private OffsetManager offset;
|
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
private OperateResult connect;
|
|
|
|
|
public SiemensS7Net siemensTcpNet = null;
|
|
|
|
|
private SiemensPLCS siemensPLCSelected = SiemensPLCS.S1500;
|
|
|
|
|
private string _plcType;
|
|
|
|
|
|
|
|
|
|
private Dictionary<string, int> _nameHandleMap = new Dictionary<string, int>();
|
|
|
|
|
|
|
|
|
|
private bool _isTargetConnected;
|
|
|
|
|
|
|
|
|
|
public AlarmEventItem AlarmConnectFailed { get; set; }
|
|
|
|
|
public AlarmEventItem AlarmCommunicationError { get; set; }
|
2023-09-07 11:10:17 +08:00
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
public event Action OnConnected;
|
|
|
|
|
|
|
|
|
|
public TcAds(string module, XmlElement node, string ioModule = "")
|
|
|
|
|
{
|
|
|
|
|
var attrModule = node.GetAttribute("module");
|
|
|
|
|
Module = string.IsNullOrEmpty(attrModule) ? module : attrModule;
|
|
|
|
|
|
|
|
|
|
Name = node.GetAttribute("id");
|
|
|
|
|
|
|
|
|
|
Address = SC.GetStringValue($"PM.{Module}.SiemensIP");
|
|
|
|
|
_plcType = SC.GetStringValue($"PM.{Module}.SiemensType");
|
2023-05-09 14:14:18 +08:00
|
|
|
|
|
2023-12-13 13:40:42 +08:00
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Initialize()
|
|
|
|
|
{
|
2023-12-12 17:08:47 +08:00
|
|
|
|
if (!SC.GetValue<bool>($"System.SetUp.Is{Module}Installed"))
|
|
|
|
|
return true;
|
|
|
|
|
|
2023-12-13 13:40:42 +08:00
|
|
|
|
offset = new OffsetManager(Module);
|
2023-04-13 11:51:03 +08:00
|
|
|
|
AlarmConnectFailed = SubscribeAlarm($"{Module}.{Name}.ConnectionError", $"Can not connect with {Address}", null);
|
|
|
|
|
AlarmCommunicationError = SubscribeAlarm($"{Module}.{Name}.CommunicationError", $"Can not Communication {Address}", null);
|
|
|
|
|
|
|
|
|
|
Connection = new FsmConnection();
|
|
|
|
|
Connection.Initialize(100, this, this);
|
|
|
|
|
|
|
|
|
|
Connection.OnConnected += Connection_OnConnected;
|
|
|
|
|
Connection.OnDisconnected += Connection_OnDisconnected;
|
|
|
|
|
Connection.OnError += Connection_OnError;
|
|
|
|
|
|
|
|
|
|
ConnectionManager.Instance.Subscribe($"{Module}.{Name}", this);
|
|
|
|
|
|
2023-09-07 11:10:17 +08:00
|
|
|
|
_thread = new PeriodicJob(2000, OnTimer, "PLC Reconnection Thread", false);
|
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IConnectable.CheckIsConnected()
|
|
|
|
|
{
|
|
|
|
|
return siemensTcpNet != null && siemensTcpNet.IsConnected;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Connect(out string reason)
|
|
|
|
|
{
|
|
|
|
|
var ipAddr = ParseAdsIPAddr(Address);
|
|
|
|
|
siemensPLCSelected = (SiemensPLCS)Enum.Parse(typeof(SiemensPLCS), _plcType);
|
|
|
|
|
siemensTcpNet = new SiemensS7Net(siemensPLCSelected);
|
|
|
|
|
siemensTcpNet.IpAddress = ipAddr.Item1;// _ipAddress;
|
|
|
|
|
siemensTcpNet.Port = ipAddr.Item2; // int.Parse(_port);
|
|
|
|
|
siemensTcpNet.ConnectTimeOut = 1;
|
|
|
|
|
siemensTcpNet.ReceiveTimeOut = 1000;
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (siemensPLCSelected != SiemensPLCS.S200Smart &&
|
|
|
|
|
siemensPLCSelected != SiemensPLCS.S200)
|
|
|
|
|
{
|
|
|
|
|
siemensTcpNet.Rack = 0;
|
|
|
|
|
siemensTcpNet.Slot = 0;
|
|
|
|
|
|
|
|
|
|
siemensTcpNet.ConnectionType = 1;
|
|
|
|
|
siemensTcpNet.LocalTSAP = 258;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
connect = siemensTcpNet.ConnectServer();
|
|
|
|
|
if (!siemensTcpNet.IsConnected)
|
|
|
|
|
{
|
|
|
|
|
//_isCommunicationError = true;
|
|
|
|
|
//IsConnect = true;
|
|
|
|
|
//EV.PostInfoLog(Module, "PLC Connection Successful!");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//_isCommunicationError = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//return connect.IsSuccess;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_isTargetConnected = false;
|
|
|
|
|
//_isCommunicationError = true;
|
|
|
|
|
reason = ex.Message;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_isTargetConnected = true;
|
|
|
|
|
reason = string.Empty;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Disconnect(out string reason)
|
|
|
|
|
{
|
|
|
|
|
reason = string.Empty;
|
|
|
|
|
siemensTcpNet.ConnectClose();
|
|
|
|
|
siemensTcpNet = null;
|
|
|
|
|
connect = null;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Connection_OnError(string obj)
|
|
|
|
|
{
|
|
|
|
|
AlarmConnectFailed.Set();
|
2023-09-07 11:10:17 +08:00
|
|
|
|
_thread.Start();
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Connection_OnDisconnected()
|
|
|
|
|
{
|
|
|
|
|
EV.PostInfoLog(Module, $"Disconnect from PLC {Address}");
|
2023-09-07 11:10:17 +08:00
|
|
|
|
_thread.Start();
|
2023-04-13 11:51:03 +08:00
|
|
|
|
ResetAlarm();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Connection_OnConnected()
|
|
|
|
|
{
|
|
|
|
|
EV.PostInfoLog(Module, $"Connected with PLC {Address}");
|
2023-12-13 13:40:42 +08:00
|
|
|
|
EV.PostInfoLog(Module, $"Delay 5s can do offset");
|
|
|
|
|
Task.Delay(5000).ContinueWith(x => offset.Set());
|
2023-09-07 11:10:17 +08:00
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
ResetAlarm();
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-07 11:10:17 +08:00
|
|
|
|
protected override void HandleMonitor()
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
2023-09-07 11:10:17 +08:00
|
|
|
|
//Reconnection();
|
|
|
|
|
}
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-09-07 11:10:17 +08:00
|
|
|
|
public bool OnTimer()
|
|
|
|
|
{
|
|
|
|
|
Reconnection();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2023-09-14 17:33:18 +08:00
|
|
|
|
/// 事件触发线程启动,PLC断线延时2秒重连,连接后才会退出线程
|
2023-09-07 11:10:17 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
protected void Reconnection()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (siemensTcpNet == null || siemensTcpNet.IsConnected)//连接被主动切断,或者已连接直接退出
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
trig_Reconnection.CLK = true;
|
|
|
|
|
if (trig_Reconnection.Q)//首次进去提示用户
|
|
|
|
|
EV.PostWarningLog(Module, "Disconnect from PLC,Stat reconnection");
|
|
|
|
|
|
|
|
|
|
connect = siemensTcpNet.ConnectServer();
|
|
|
|
|
if (siemensTcpNet.IsConnected)
|
|
|
|
|
{
|
|
|
|
|
trig_Reconnection.RST = true;
|
|
|
|
|
Connection.InvokeReset();
|
|
|
|
|
EV.PostInfoLog(Module, "PLC reconnection successful!");
|
|
|
|
|
_thread.Stop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{ }
|
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Terminate()
|
|
|
|
|
{
|
|
|
|
|
Connection.Terminate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Tuple<string, int> ParseAdsIPAddr(string addr)
|
|
|
|
|
{
|
|
|
|
|
if (addr == null)
|
|
|
|
|
throw (new Exception("PM.SiemensIP is not exist in sc.data!"));
|
|
|
|
|
|
|
|
|
|
Regex reg = new Regex(@"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?):(\d{1,5})$");
|
|
|
|
|
if (!reg.IsMatch(addr))
|
|
|
|
|
throw (new Exception($@"{Module}.SiemensIP '{addr}' is invalid!"));
|
|
|
|
|
|
|
|
|
|
var item = addr.Split(':');
|
|
|
|
|
return Tuple.Create(item[0], int.Parse(item[1]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Reset()
|
|
|
|
|
{
|
|
|
|
|
ResetAlarm();
|
2023-09-07 11:10:17 +08:00
|
|
|
|
trig_Reconnection.RST = true;//复位重连trig信号
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-09-07 11:10:17 +08:00
|
|
|
|
//复位读写异常trig信号
|
|
|
|
|
trig_readPLCFailCount.RST = true;
|
|
|
|
|
trig_writePLCFailCount.CLK = true;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
Connection.InvokeReset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Read(string variableName, out object data, string type, int length, out string reason)
|
|
|
|
|
{
|
|
|
|
|
data = null;
|
|
|
|
|
reason = string.Empty;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool WriteArrayElement(string variableName, int index, object data, out string reason)
|
|
|
|
|
{
|
|
|
|
|
reason = "";
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool SendBinaryData(byte[] data)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool SendAsciiData(string data)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool BulkReadRenderResult(string Adrress, ushort length, out byte[] data)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2023-09-07 11:10:17 +08:00
|
|
|
|
if (siemensTcpNet == null || !siemensTcpNet.IsConnected)
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
|
|
|
|
data = null;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-09-07 11:10:17 +08:00
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
OperateResult<byte[]> read = siemensTcpNet.Read(Adrress, length);// ushort.Parse(lengthTextBox.Text));
|
|
|
|
|
if (read.IsSuccess)
|
|
|
|
|
{
|
|
|
|
|
data = read.Content;
|
2023-09-07 11:10:17 +08:00
|
|
|
|
|
|
|
|
|
//复位读错误次数
|
|
|
|
|
trig_readPLCFailCount.RST = true;
|
|
|
|
|
readPLCFailCount = 0;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//if (!_isCommunicationError)
|
2023-09-13 08:56:00 +08:00
|
|
|
|
LOG.Write($"PLC BulkReadRenderResult Communication error with Siemens PLC , {read.Message}");
|
2023-04-13 11:51:03 +08:00
|
|
|
|
//_isCommunicationError = true;
|
|
|
|
|
data = null;
|
2023-09-07 11:10:17 +08:00
|
|
|
|
|
|
|
|
|
//新增读错误次数判断
|
|
|
|
|
if (readPLCFailCount < MaxFailCount)
|
|
|
|
|
{
|
|
|
|
|
readPLCFailCount++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
trig_readPLCFailCount.CLK = true;
|
|
|
|
|
if (trig_readPLCFailCount.Q)
|
|
|
|
|
{
|
|
|
|
|
EV.PostWarningLog(Module, $"PLC BulkReadRenderResult error with Siemens PLC , {read.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
// EV.PostAlarmLog("", "Read Failed:" + ex.Message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data = null;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool BulkWriteByteRenderResult(string Adrress, byte[] data)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2023-09-07 11:10:17 +08:00
|
|
|
|
if (siemensTcpNet == null || !siemensTcpNet.IsConnected)
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
|
|
|
|
data = null;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
OperateResult write = siemensTcpNet.Write(Adrress, data);
|
|
|
|
|
if (!write.IsSuccess)
|
|
|
|
|
{
|
|
|
|
|
//if (!_isCommunicationError)
|
2023-07-13 12:28:37 +08:00
|
|
|
|
//EV.PostInfoLog("PLC", $"Communication error with Siemens PLC , {write.Message}");
|
2023-09-13 08:56:00 +08:00
|
|
|
|
LOG.Write($"PLC BulkWriteByteRenderResult Communication error with Siemens PLC , {write.Message}");
|
2023-04-13 11:51:03 +08:00
|
|
|
|
//_isCommunicationError = true;
|
2023-09-07 11:10:17 +08:00
|
|
|
|
|
|
|
|
|
//新增写错误次数判断
|
|
|
|
|
if (writePLCFailCount < MaxFailCount)
|
|
|
|
|
{
|
|
|
|
|
writePLCFailCount++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
trig_writePLCFailCount.CLK = true;
|
|
|
|
|
if (trig_writePLCFailCount.Q)
|
|
|
|
|
{
|
|
|
|
|
EV.PostWarningLog(Module, $"PLC BulkWriteByteRenderResult error with Siemens PLC , {write.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-09-13 08:56:00 +08:00
|
|
|
|
return false;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
2023-09-07 11:10:17 +08:00
|
|
|
|
//复位写错误次数
|
|
|
|
|
trig_writePLCFailCount.RST = true;
|
|
|
|
|
writePLCFailCount = 0;
|
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
return write.IsSuccess;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
// EV.PostAlarmLog("", "Read Failed:" + ex.Message);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-09-07 11:10:17 +08:00
|
|
|
|
}
|