387 lines
15 KiB
C#
387 lines
15 KiB
C#
using JDYD.IEC104;
|
||
using JDYD.IEC104.DatagramFormatters;
|
||
using System.Net;
|
||
using System.Net.Sockets;
|
||
using System.Threading.Tasks.Dataflow;
|
||
|
||
namespace Yunda.ISAS.DataMonitoringServer.DataAnalysis
|
||
{
|
||
// 定义一个委托
|
||
public delegate void NotifyCallAllCompleteDelegate(string message);
|
||
public class IEC104Client
|
||
{
|
||
Node _node = new Node();
|
||
bool _isMaster = false;
|
||
string _remoteAddr = "192.168.81.35";
|
||
int _remotePort = 2432;
|
||
int _localPort = 2432;
|
||
ActionBlock<IECData> _handleDataAction;
|
||
public event NotifyCallAllCompleteDelegate NotifyCallAllCompleteEvent;
|
||
public IEC104Client(string host, int port,ActionBlock<IECData> handleDataAction)
|
||
{
|
||
_remoteAddr = host;
|
||
_remotePort = port;
|
||
_handleDataAction = handleDataAction;
|
||
_node.NewDatagram += Node_NewDatagram;
|
||
_node.DatagramSending += Node_DatagramSending;
|
||
_node.ConnectionLost += Node_ConnectionLost;
|
||
|
||
}
|
||
|
||
|
||
|
||
private void Node_ConnectionLost(Node sender)
|
||
{
|
||
_handleDataAction.Post(new IECData("连接已断开"));
|
||
if (toDisconnect)
|
||
{
|
||
Connect();
|
||
}
|
||
}
|
||
|
||
private void Node_DatagramSending(APDU d, Node sender)
|
||
{
|
||
DispalyDatagram(d, false);
|
||
}
|
||
//LinkedList<DatagramInfo> DatagramToShow = new LinkedList<DatagramInfo>();
|
||
object locker = new object();
|
||
void DispalyDatagram(APDU apdu, bool rev = true)
|
||
{
|
||
if (apdu == null)
|
||
{
|
||
return;
|
||
}
|
||
try
|
||
{
|
||
var t = apdu.TransferTime;
|
||
_handleDataAction.Post(new IECData(string.Format("{0}-{1}-{2} {3:d2}:{4:d2}:{5:d2}.{6:d3} {7}",
|
||
t.Year, t.Month, t.Day, t.Hour, t.Minute, t.Second, t.Millisecond, rev ? "接收" : "发送")));
|
||
switch (apdu.Format)
|
||
{
|
||
case DatagramFormat.NumberedSupervisory:
|
||
_handleDataAction.Post(new IECData("S格式报文 " + "接收序号 " + apdu.RecevingNumber.ToString()));
|
||
|
||
break;
|
||
case DatagramFormat.UnnumberedControl:
|
||
_handleDataAction.Post(new IECData("U格式报文 " + apdu.ControlFunction.ToString()));
|
||
break;
|
||
case DatagramFormat.InformationTransmit:
|
||
|
||
string content = $"I格式报文:发送序号:{apdu.SendingNumber} 接收序号:{apdu.RecevingNumber} 报文类型: {apdu.Formatter?.Description}";
|
||
_handleDataAction.Post(new IECData(content));
|
||
content = $"I格式报文:服务地址:{apdu.ASDU.Address} 传送原因:{apdu.ASDU.Cause} P/N:{apdu.ASDU.PN} TEST:{apdu.ASDU.Test}";
|
||
_handleDataAction.Post(new IECData(content));
|
||
foreach (var message in apdu.ASDU.Messages)
|
||
{
|
||
IECData iECDataFloat = null;
|
||
IECData iECDataInt = null;
|
||
content = $"I格式报文:数据类型:{message.Type}";
|
||
_handleDataAction.Post(new IECData(content));
|
||
|
||
if (message.Type != ElementType.Empty)
|
||
{
|
||
switch (message.Type)
|
||
{
|
||
case ElementType.NVA:
|
||
break;
|
||
case ElementType.R:
|
||
iECDataFloat = new IECData((ushort)message.Address, message.R, apdu.ASDU.Cause, IECDataType.Telemetering);
|
||
break;
|
||
case ElementType.SVA:
|
||
break;
|
||
case ElementType.SCD:
|
||
iECDataInt = new IECData((ushort)message.Address, (byte)(message.SCD + 1), apdu.ASDU.Cause,IECDataType.Telesignal);
|
||
break;
|
||
case ElementType.DCD:
|
||
iECDataInt = new IECData((ushort)message.Address, (byte)(message.SCD), apdu.ASDU.Cause, IECDataType.Telesignal);
|
||
break;
|
||
case ElementType.DIQ:
|
||
iECDataInt = new IECData((ushort)message.Address, (byte)message.DIQ, apdu.ASDU.Cause, IECDataType.Telesignal);
|
||
break;
|
||
}
|
||
}
|
||
if (message.Extra != null)
|
||
{
|
||
if (apdu.Formatter != null && apdu.Formatter.GetType() == typeof(C_IC_NA_1))
|
||
{
|
||
content = $"I格式报文:附加信息:总召唤";
|
||
_handleDataAction.Post(new IECData(content));
|
||
if (apdu.ASDU.Cause == 10)
|
||
{
|
||
_handleDataAction.Post(new IECData() { DataType = IECDataType.CompleteAll });
|
||
}
|
||
}
|
||
}
|
||
DateTime time =default;
|
||
if (message.TimeStamp != null && message.TimeStamp.Length == 7)
|
||
{
|
||
var timeStr = string.Format("{0:d2}:{1:d2}:{2:d2}.{3:d3}", message.Hour, message.Minute, message.Second, message.Milisecond);
|
||
if (!DateTime.TryParse(timeStr, out time))
|
||
{
|
||
time = DateTime.Now;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
time = DateTime.Now;
|
||
}
|
||
if (iECDataFloat != null)
|
||
{
|
||
iECDataFloat.Time = time;
|
||
_handleDataAction.Post(iECDataFloat);
|
||
}
|
||
if (iECDataInt != null)
|
||
{
|
||
iECDataInt.Time = time;
|
||
_handleDataAction.Post(iECDataInt);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_handleDataAction.Post(new IECData(ex));
|
||
}
|
||
GC.Collect();
|
||
}
|
||
|
||
private void Node_NewDatagram(APDU d, Node sender)
|
||
{
|
||
DispalyDatagram(d, true);
|
||
}
|
||
Socket listenSocket;
|
||
Task _connectTask;
|
||
CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
|
||
public bool _isConnected = false;
|
||
public bool IsCallAllData=false;
|
||
/// <summary>
|
||
/// 连接
|
||
/// </summary>
|
||
public void Connect()
|
||
{
|
||
while (!_cancellationTokenSource.TryReset())
|
||
{
|
||
Task.Delay(100).Wait();
|
||
}
|
||
_isConnected = false;
|
||
toDisconnect = true;
|
||
_connectTask = Task.Factory.StartNew(async () =>
|
||
{
|
||
if (_node.Socket != null)
|
||
return;
|
||
|
||
try
|
||
{
|
||
_handleDataAction.Post(new IECData("开始连接..." ));
|
||
if (listenSocket != null)
|
||
{
|
||
listenSocket.Dispose();
|
||
listenSocket = null;
|
||
}
|
||
IPAddress ipAddress;
|
||
Socket s =null;
|
||
if (IPAddress.TryParse(_remoteAddr, out ipAddress))
|
||
{
|
||
if (ipAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
|
||
{
|
||
_handleDataAction.Post(new IECData("这是一个IPv4地址:" + _remoteAddr));
|
||
s = new Socket( SocketType.Stream, ProtocolType.Tcp);
|
||
}
|
||
else if (ipAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
|
||
{
|
||
_handleDataAction.Post(new IECData("这是一个IPv6地址:"+ _remoteAddr));
|
||
|
||
s = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
|
||
}
|
||
|
||
}
|
||
if (s == null)
|
||
{
|
||
throw new Exception("ip地址错误");
|
||
}
|
||
// 创建Socket
|
||
//Socket clientSocket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
|
||
|
||
_handleDataAction.Post(new IECData("正在尝试连接" + _remoteAddr + ",目标端口" + _remotePort));
|
||
await s.ConnectAsync(_remoteAddr, _remotePort, _cancellationTokenSource.Token);
|
||
_node.BindSocket(s, true);
|
||
var ip = _node.Socket.RemoteEndPoint as IPEndPoint;
|
||
if (ip != null)
|
||
{
|
||
var addr = ip.Address;
|
||
if (addr.IsIPv4MappedToIPv6)
|
||
addr = addr.MapToIPv4();
|
||
_handleDataAction.Post(new IECData("新连接已建立,对方位于" + addr.ToString() + ":" + ip.Port));
|
||
}
|
||
_node.StartReceive();
|
||
Task.Delay(1000).Wait();
|
||
//CallAll();
|
||
_isConnected = true;
|
||
_handleDataAction.Post(new IECData("连接成功"));
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_handleDataAction.Post(new IECData(new Exception("连接失败,对方地址" + _remoteAddr + ",目标端口" + _remotePort)));
|
||
if (toDisconnect)
|
||
{
|
||
Task.Delay(5000).Wait();
|
||
_handleDataAction.Post(new IECData("正在尝试重连" + _remoteAddr + ",目标端口" + _remotePort));
|
||
Connect();
|
||
}
|
||
if (_isConnected)
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
}, _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||
|
||
}
|
||
|
||
|
||
APDU metf_tosend;
|
||
/// <summary>
|
||
/// 发送遥控
|
||
/// </summary>
|
||
/// <param name="value"></param>
|
||
/// <param name="addr"></param>
|
||
public void SendValue(byte value,int addr,bool isDoublepPoint )
|
||
{
|
||
if (metf_tosend == null)
|
||
{
|
||
try
|
||
{
|
||
if (isDoublepPoint)
|
||
{
|
||
var f = (C_DC_NA_1)_node.FormatterManager.GetInstance(typeof(C_DC_NA_1));
|
||
if (metf_tosend == null)
|
||
metf_tosend = f.CreateAPDU(1);
|
||
var apdu = f.Create((ushort)addr, 1, value);
|
||
_node.SendAPDU(apdu);
|
||
metf_tosend = null;
|
||
}
|
||
else
|
||
{
|
||
var f = (C_SC_NA_1)_node.FormatterManager.GetInstance(typeof(C_SC_NA_1));
|
||
if (metf_tosend == null)
|
||
metf_tosend = f.CreateAPDU(1);
|
||
var apdu = f.Create((ushort)addr, 1, value);
|
||
_node.SendAPDU(apdu);
|
||
metf_tosend = null;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_handleDataAction.Post(new IECData(ex));
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
bool toDisconnect = false;
|
||
public void Close()
|
||
{
|
||
toDisconnect = false;
|
||
_node.CloseConnection();
|
||
_cancellationTokenSource.Cancel();
|
||
}
|
||
public void CallSingle(int addr)
|
||
{
|
||
try
|
||
{
|
||
var f = (C_RD_NA_1)_node.FormatterManager.GetInstance(typeof(C_RD_NA_1));
|
||
var apdu = f.Create(1, (uint)addr);
|
||
_node.SendAPDU(apdu);
|
||
}
|
||
catch (Exception ex) {
|
||
_handleDataAction.Post(new IECData(ex));
|
||
|
||
}
|
||
}
|
||
|
||
public void CallAll()
|
||
{
|
||
try
|
||
{
|
||
var f = (C_IC_NA_1)_node.FormatterManager.GetInstance(typeof(C_IC_NA_1));
|
||
var apdu = f.Create(1, 0);
|
||
_node.SendAPDU(apdu);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_handleDataAction.Post(new IECData(ex));
|
||
|
||
}
|
||
}
|
||
|
||
}
|
||
/// <summary>
|
||
/// 数据体
|
||
/// </summary>
|
||
public class IECData
|
||
{
|
||
public ushort InfoAddr { get; set; }
|
||
public byte IValue { get; set; }
|
||
public float FValue { get; set; }
|
||
public ushort Cause { get; set; } = 20;
|
||
|
||
public IECDataType DataType { get; set; }
|
||
public DateTime Time { get; set; }
|
||
|
||
public string Msg { get; set; }
|
||
public Exception Ex { get; set; }
|
||
public IECData()
|
||
{
|
||
}
|
||
public IECData(string msg)
|
||
{
|
||
Msg = msg;
|
||
DataType = IECDataType.Msg;
|
||
}
|
||
public IECData(Exception ex)
|
||
{
|
||
Ex = ex;
|
||
DataType = IECDataType.Error;
|
||
}
|
||
|
||
public IECData(ushort infoAddr, byte iValue, ushort cause, IECDataType dataType = IECDataType.Telesignal)
|
||
{
|
||
InfoAddr = infoAddr;
|
||
IValue = iValue;
|
||
DataType = dataType;
|
||
Cause = cause;
|
||
|
||
}
|
||
public IECData(ushort infoAddr, float fValue, ushort cause, IECDataType dataType = IECDataType.Telemetering)
|
||
{
|
||
InfoAddr = infoAddr;
|
||
FValue = fValue;
|
||
DataType = dataType;
|
||
Cause = cause;
|
||
|
||
}
|
||
public IECData(ushort infoAddr, byte iValue , IECDataType dataType, ushort cause)
|
||
{
|
||
InfoAddr = infoAddr;
|
||
IValue = iValue;
|
||
DataType = dataType;
|
||
Cause = cause;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 数据类型
|
||
/// </summary>
|
||
public enum IECDataType
|
||
{
|
||
Telesignal = 0,
|
||
Telemetering = 1,
|
||
TeleCommand = 2,
|
||
Error = 3,
|
||
Msg = 4,
|
||
CompleteAll =5,//完成总召
|
||
}
|
||
|
||
}
|