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 _handleDataAction; public event NotifyCallAllCompleteDelegate NotifyCallAllCompleteEvent; public IEC104Client(string host, int port,ActionBlock 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 DatagramToShow = new LinkedList(); 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; /// /// 连接 /// 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; /// /// 发送遥控 /// /// /// 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)); } } } /// /// 数据体 /// 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; } } /// /// 数据类型 /// public enum IECDataType { Telesignal = 0, Telemetering = 1, TeleCommand = 2, Error = 3, Msg = 4, CompleteAll =5,//完成总召 } }