using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using Tango.Core;
using Tango.Core.ExtensionMethods;
using Tango.Logging;
using Tango.PMR.Discovery;
using Tango.PMR.Integration;
using Tango.Transport.Helpers;
using Tango.Transport.Servers;
namespace Tango.Transport.Discovery
{
///
/// Represents UDP discovery service broadcasting a discovery message of type .
///
///
public class UdpDiscoveryService : ExtendedObject, IDiscoveryService where DiscoveryMessage : IMessage
{
private Timer _timer;
private TcpServer _tcpValidationServer;
///
/// Occurs before broadcasting the discovery message and gives a chance to modify the packet message.
///
public event EventHandler BeforeBroadcasting;
///
/// Gets or sets the current discovery message.
///
public DiscoveryMessage CurrentDiscoveryMessage { get; set; }
///
/// Gets or sets the TCP validation information.
///
public TcpValidationInfo TcpValidationInfo { get; set; }
///
/// Gets or sets the multi-cast group address when using multi-cast discovery method.
///
public String MulticastGroupAddress { get; set; }
///
/// Gets or sets the interval in which the discovery message will be sent.
///
public TimeSpan Interval { get; set; }
///
/// Gets a value indicating whether this service has been started.
///
public bool IsStarted { get; private set; }
///
/// Gets the UDP port number.
///
public int Port { get; private set; }
///
/// Prevents a default instance of the class from being created.
///
private UdpDiscoveryService()
{
TcpValidationInfo = new TcpValidationInfo();
Interval = TimeSpan.FromSeconds(5);
CurrentDiscoveryMessage = Activator.CreateInstance();
}
///
/// Initializes a new instance of the class.
///
/// The port.
public UdpDiscoveryService(int port) : this()
{
Port = port;
_tcpValidationServer = new TcpServer(Port);
_tcpValidationServer.ClientConnected += (x, e) =>
{
try
{
var data = GenericMessageSerializer.Serialize(TcpValidationInfo, GenericMessageProtocol.Bson);
e.Socket.GetStream().Write(data, 0, data.Length);
e.Socket.Dispose();
}
catch (Exception ex)
{
LogManager.Log(ex, "TCP Validation Server Response Error.");
}
};
}
///
/// Initializes a new instance of the class.
///
/// The port.
/// The discovery message.
public UdpDiscoveryService(int port, DiscoveryMessage discoveryMessage) : this(port)
{
CurrentDiscoveryMessage = discoveryMessage;
}
///
/// Starts the discovery service.
///
public void Start()
{
if (!IsStarted)
{
_tcpValidationServer.Start();
_timer = new Timer();
_timer.Interval = Interval.TotalMilliseconds;
_timer.Elapsed += _timer_Elapsed;
_timer.Enabled = true;
_timer.Start();
IsStarted = true;
}
}
///
/// Stops the discovery service.
///
public void Stop()
{
if (IsStarted)
{
_tcpValidationServer.Stop();
//Transmit the discovery packet one more time so clients can tell that we have disconnected.
BroadcastDiscoveryPacket();
_timer.Stop();
IsStarted = false;
}
}
///
/// Handles the Elapsed event of the _timer control.
///
/// The source of the event.
/// The instance containing the event data.
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
BroadcastDiscoveryPacket();
}
private void BroadcastDiscoveryPacket()
{
//Broadcast
try
{
BeforeBroadcasting?.Invoke(this, CurrentDiscoveryMessage);
UdpClient client = new UdpClient();
IPEndPoint endPoint = new IPEndPoint(IPAddress.Broadcast, Port);
byte[] bytes = CurrentDiscoveryMessage.ToByteArray();
client.EnableBroadcast = true;
client.Send(bytes, bytes.Length, endPoint);
client.Close();
}
catch (Exception ex)
{
LogManager.Log(ex, "Error broadcasting discovery packet.");
}
//Multicast
try
{
UdpClient client = new UdpClient(AddressFamily.InterNetwork);
IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(MulticastGroupAddress), Port);
client.JoinMulticastGroup(IPAddress.Parse(MulticastGroupAddress), IPAddress.Parse(IPAddressHelper.GetLocalIpAddress()));
byte[] bytes = CurrentDiscoveryMessage.ToByteArray();
client.Client.Ttl = 10;
client.Send(bytes, bytes.Length, endPoint);
client.Close();
}
catch (Exception ex)
{
LogManager.Log(ex, LogCategory.Debug, "Error multicasting discovery packet.");
}
}
}
}