RoboidControl-csharp/src/Participant.cs

193 lines
7.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
namespace RoboidControl {
/// <summary>
/// A participant is a device which manages things.
/// </summary>
/// It can communicate with other participant to synchronise the state of things.
/// This class is used to register the things the participant is managing.
/// It also maintains the communcation information to contact the participant.
/// It is used as a basis for the local participant, but also as a reference to remote participants.
public class Participant {
/// <summary>
/// Create a new participant with the given communcation info
/// </summary>
/// <param name="ipAddress">The IP address of the participant</param>
/// <param name="port">The UDP port of the participant</param>
public Participant(string ipAddress, int port, Participant localParticipant = null) {
this.ipAddress = ipAddress;
this.port = port;
if (localParticipant != null)
this.udpClient = localParticipant.udpClient;
}
/// <summary>
/// The Ip Address of a participant. When the participant is local, this contains 0.0.0.0
/// </summary>
public string ipAddress = "0.0.0.0";
/// <summary>
/// The port number for UDP communication with the participant. This is 0 for isolated participants.
/// </summary>
public int port = 0;
public UdpClient udpClient = null;
/// <summary>
/// he network Id to identify the participant
/// </summary>
public byte networkId = 0;
/// <summary>
/// The things managed by this participant
/// </summary>
public readonly List<Thing> things = new();
/// <summary>
/// Get the thing with the given properties
/// </summary>
/// <param name="thingId">The ID of the thing</param>
/// <returns>The thing when it is found, null in other cases.</returns>
public Thing Get(byte thingId) {
Thing thing = things.Find(aThing => aThing.id == thingId);
//Thing thing = things.Find(aThing => Thing.IsThing(aThing, networkId, thingId));
// if (thing == null)
// Console.WriteLine($"Could not find thing {ipAddress}:{port}[{networkId}/{thingId}]");
return thing;
}
/// <summary>
/// Add a new thing for this participant
/// </summary>
/// <param name="thing">The thing to add</param>
/// <param name="checkId">If true, the thing.id is regenerated if it is zero
public void Add(Thing thing, bool checkId = true) {
if (checkId && thing.id == 0) {
thing.id = (byte)(this.things.Count + 1);
this.things.Add(thing);
}
else {
Thing foundThing = Get(thing.id);
if (foundThing == null)
this.things.Add(thing);
}
}
/// <summary>
/// Remove a thing for this participant
/// </summary>
/// <param name="thing">The thing to remove</param>
public void Remove(Thing thing) {
this.things.Remove(thing);
}
#region Update
/// <summary>
/// Update all things for this participant
/// </summary>
/// <param name="currentTimeMS">The current time in milliseconds (optional)</param>
public virtual void Update(ulong currentTimeMS = 0) {
int n = this.things.Count;
for (int ix = 0; ix < n; ix++) {
Thing thing = this.things[ix];
if (thing != null)
thing.Update(currentTimeMS, true);
}
}
public class UpdateEvent {
public int messageId; // see the communication messages
public Thing thing;
public Participant participant;
}
public ConcurrentQueue<UpdateEvent> updateQueue = new();
#endregion Update
#region Send
// Would be nice if this could be shared between all participants....
public byte[] buffer = new byte[1024];
public virtual bool Send(IMessage msg) {
int bufferSize = msg.Serialize(ref this.buffer);
if (bufferSize <= 0)
return true;
IPEndPoint participantEndpoint = new IPEndPoint(IPAddress.Parse(this.ipAddress), this.port);
Console.WriteLine($"msg to remote participant {participantEndpoint.Address.ToString()} {participantEndpoint.Port}");
if (udpClient != null) {
Console.WriteLine("sending...");
this.udpClient?.Send(this.buffer, bufferSize, participantEndpoint);
}
return true;
}
#endregion Send
/// <summary>
/// The collection of known participants.
/// </summary>
public readonly static List<Participant> participants = new();
/// <summary>
/// Retrieve a participant using ip address and port number
/// </summary>
/// <param name="ipAddress">The ip address of the participant</param>
/// <param name="port">The port number used to send messages to the participant</param>
/// <returns>The participant or null if it is not found.</returns>
public static Participant GetParticipant(string ipAddress, int port) {
//Console.WriteLine($"Get Participant {ipAddress}:{port}");
foreach (Participant participant in Participant.participants) {
if (participant.ipAddress == ipAddress && participant.port == port)
return participant;
}
return null;
}
/// <summary>
/// Retrieve a participant using a network ID
/// </summary>
/// <param name="networkId">The network ID of the participant</param>
/// <returns>The participant or null if it is not found.</returns>
public static Participant GetParticipant(int networkId) {
//Console.WriteLine($"Get Participant [networkId]");
foreach (Participant participant in Participant.participants) {
if (participant.networkId == networkId)
return participant;
}
return null;
}
/// <summary>
/// Add a new participant to the collection of participants
/// </summary>
/// <param name="ipAddress">The IP address of the participant</param>
/// <param name="port">The port used to send messages to this participant</param>
/// <returns>The added participant</returns>
public static Participant AddParticipant(string ipAddress, int port, Participant localParticipant = null) {
Console.WriteLine($"New Participant {ipAddress}:{port}");
Participant participant = new(ipAddress, port, localParticipant) {
networkId = (byte)(Participant.participants.Count + 1)
};
Participant.participants.Add(participant);
return participant;
}
/// <summary>
/// Add a new participant to the collection of participants
/// </summary>
/// <param name="participant">The participant to add</param>
/// <note>This function only adds the participant if it is not yet in the collection</note>
public static void AddParticipant(Participant participant) {
Participant foundParticipant = Participant.GetParticipant(participant.networkId);
if (foundParticipant == null)
Participant.participants.Add(participant);
}
}
}