2024-08-21 16:50:14 +08:00

387 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,//完成总召
}
}