using System; using System.Collections.Generic; using System.Collections.Concurrent; namespace RoboidControl { /// /// A participant is a device which manages things. /// /// 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 { /// /// Create a new participant with the given communcation info /// /// The IP address of the participant /// The UDP port of the participant public Participant(string ipAddress, int port) { this.ipAddress = ipAddress; this.port = port; } /// /// The Ip Address of a participant. When the participant is local, this contains 0.0.0.0 /// public string ipAddress = "0.0.0.0"; /// /// The port number for UDP communication with the participant. This is 0 for isolated participants. /// public int port = 0; /// /// he network Id to identify the participant /// public byte networkId = 0; /// /// The things managed by this participant /// public readonly List things = new(); /// /// Get the thing with the given properties /// /// The ID of the thing /// The thing when it is found, null in other cases. 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; } /// /// Add a new thing for this participant /// /// The thing to add /// 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); } } /// /// Remove a thing for this participant /// /// The thing to remove public void Remove(Thing thing) { this.things.Remove(thing); } #region Update /// /// Update all things for this participant /// /// The current time in milliseconds (optional) 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 updateQueue = new(); #endregion Update /// /// The collection of known participants. /// public readonly static List participants = new(); /// /// Retrieve a participant using ip address and port number /// /// The ip address of the participant /// The port number used to send messages to the participant /// The participant or null if it is not found. 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; } /// /// Retrieve a participant using a network ID /// /// The network ID of the participant /// The participant or null if it is not found. 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; } /// /// Add a new participant to the collection of participants /// /// The IP address of the participant /// The port used to send messages to this participant /// The added participant public static Participant AddParticipant(string ipAddress, int port) { Console.WriteLine($"New Participant {ipAddress}:{port}"); Participant participant = new(ipAddress, port) { networkId = (byte)(Participant.participants.Count + 1) }; Participant.participants.Add(participant); return participant; } /// /// Add a new participant to the collection of participants /// /// The participant to add /// This function only adds the participant if it is not yet in the collection public static void AddParticipant(Participant participant) { Participant foundParticipant = Participant.GetParticipant(participant.networkId); if (foundParticipant == null) Participant.participants.Add(participant); } } }