Aligned Participants

This commit is contained in:
Pascal Serrarens 2025-04-30 17:00:21 +02:00
parent 09783d2103
commit 36740cf598
2 changed files with 93 additions and 79 deletions

View File

@ -7,40 +7,41 @@ using System.Net.NetworkInformation;
namespace RoboidControl { namespace RoboidControl {
/// <summary> /// <summary>
/// A participant is used for communcation between things /// A participant using UDP communication
/// </summary> /// </summary>
/// A local participant is the local device which can communicate with
/// other participants It manages all local things and communcation with other
/// participants. Each application has a local participant which is usually
/// explicit in the code. An participant can be isolated. In that case it is
/// standalong and does not communicate with other participants.
///
/// It is possible to work with an hidden participant by creating things without
/// specifying a participant in the constructor. In that case an hidden isolated
/// participant is created which can be obtained using
/// RoboidControl::IsolatedParticipant::Isolated().
/// @sa RoboidControl::Thing::Thing()
public class ParticipantUDP : Participant { public class ParticipantUDP : Participant {
public byte[] buffer = new byte[1024];
public ulong publishInterval = 3000; // = 3 seconds
public string name = "Participant";
public bool isIsolated = false;
public Participant remoteSite;
public IPEndPoint endPoint = null;
public UdpClient udpClient = null;
public string broadcastIpAddress = "255.255.255.255";
public readonly ConcurrentQueue<IMessage> messageQueue = new ConcurrentQueue<IMessage>();
#region Init #region Init
/// <summary> /// <summary>
/// Create a participant with the give UDP port /// Create a participant without connecting to a site
/// </summary> /// </summary>
/// <param name="port">The port number on which to communicate</param> /// <param name="port">The port number on which to communicate</param>
/// These participant typically broadcast Participant messages to let site
/// servers on the local network know their presence. Alternatively they can
/// broadcast information which can be used directly by other participants.
public ParticipantUDP(int port = 0) : base("127.0.0.1", port) { public ParticipantUDP(int port = 0) : base("127.0.0.1", port) {
if (this.port == 0) if (this.port == 0)
this.isIsolated = true; this.isIsolated = true;
Participant.AddParticipant(this); Participant.AddParticipant(this);
} }
/// <summary> /// <summary>
/// Create a new participant for a site at the given address and port /// Create a participant which will try to connect to a site.
/// </summary> /// </summary>
/// <param name="ipAddress">The ip address of the site server</param> /// <param name="ipAddress">The ip address of the site server</param>
/// <param name="port">The port number of the site server</param> /// <param name="port">The port number of the site server</param>
/// <param name="localPort">The port used by the local participant</param>
public ParticipantUDP(string ipAddress, int port = 7681, int localPort = 7681) : base("127.0.0.1", localPort) { public ParticipantUDP(string ipAddress, int port = 7681, int localPort = 7681) : base("127.0.0.1", localPort) {
if (this.port == 0) if (this.port == 0)
this.isIsolated = true; this.isIsolated = true;
@ -81,16 +82,48 @@ namespace RoboidControl {
} }
private static ParticipantUDP isolatedParticipant = null;
/// <summary> /// <summary>
/// Create a participant using the given udp client /// Isolated participant is used when the application is run without networking
/// </summary> /// </summary>
/// <param name="udpClient">UDP client to use for communication</param> /// <returns>A participant without networking support</returns>
/// <param name="port">The port number on which to communicate</param> public static ParticipantUDP Isolated() {
public ParticipantUDP(UdpClient udpClient, int port) : this() { if (isolatedParticipant == null)
this.udpClient = udpClient; isolatedParticipant = new ParticipantUDP(0);
this.port = port; return isolatedParticipant;
} }
#endregion Init
/// <summary>
/// The name of the participant
/// </summary>
public string name = "ParticipantUDP";
/// <summary>
/// True if the participant is running isolated.
/// </summary>
/// Isolated participants do not communicate with other participants
public bool isIsolated = false;
/// <summary>
/// The remote site when this participant is connected to a site
/// </summary>
public Participant remoteSite = null;
/// <summary>
/// The interval in milliseconds for publishing (broadcasting) data on the local network
/// </summary>
public ulong publishInterval = 3000; // = 3 seconds
public byte[] buffer = new byte[1024];
public IPEndPoint endPoint = null;
public UdpClient udpClient = null;
public string broadcastIpAddress = "255.255.255.255";
public readonly ConcurrentQueue<IMessage> messageQueue = new ConcurrentQueue<IMessage>();
protected void GetBroadcastAddress(IPAddress ip, IPAddress subnetMask) { protected void GetBroadcastAddress(IPAddress ip, IPAddress subnetMask) {
byte[] ipBytes = ip.GetAddressBytes(); byte[] ipBytes = ip.GetAddressBytes();
byte[] maskBytes = subnetMask.GetAddressBytes(); byte[] maskBytes = subnetMask.GetAddressBytes();
@ -110,14 +143,6 @@ namespace RoboidControl {
Console.WriteLine($"Broadcast address: {this.broadcastIpAddress}"); Console.WriteLine($"Broadcast address: {this.broadcastIpAddress}");
} }
private static ParticipantUDP isolatedParticipant = null;
public static ParticipantUDP Isolated() {
if (isolatedParticipant == null)
isolatedParticipant = new ParticipantUDP(0);
return isolatedParticipant;
}
#endregion Init
#region Update #region Update
protected ulong nextPublishMe = 0; protected ulong nextPublishMe = 0;
@ -208,9 +233,19 @@ namespace RoboidControl {
this.Send(owner, new ThingMsg(this.networkId, thing)); this.Send(owner, new ThingMsg(this.networkId, thing));
this.Send(owner, new NameMsg(this.networkId, thing)); this.Send(owner, new NameMsg(this.networkId, thing));
this.Send(owner, new ModelUrlMsg(this.networkId, thing)); this.Send(owner, new ModelUrlMsg(this.networkId, thing));
this.Send(owner, new PoseMsg(this.networkId, thing));
this.Send(owner, new BinaryMsg(this.networkId, thing)); this.Send(owner, new BinaryMsg(this.networkId, thing));
} }
public void PublishThingInfo(Thing thing) {
// Console.WriteLine("Publish thing info");
this.Publish(new ThingMsg(this.networkId, thing));
this.Publish(new NameMsg(this.networkId, thing));
this.Publish(new ModelUrlMsg(this.networkId, thing));
this.Publish(new PoseMsg(this.networkId, thing));
this.Publish(new BinaryMsg(this.networkId, thing));
}
public bool Send(Participant owner, IMessage msg) { public bool Send(Participant owner, IMessage msg) {
int bufferSize = msg.Serialize(ref this.buffer); int bufferSize = msg.Serialize(ref this.buffer);
if (bufferSize <= 0) if (bufferSize <= 0)
@ -222,14 +257,6 @@ namespace RoboidControl {
return true; return true;
} }
public void PublishThingInfo(Thing thing) {
// Console.WriteLine("Publish thing info");
this.Publish(new ThingMsg(this.networkId, thing));
this.Publish(new NameMsg(this.networkId, thing));
this.Publish(new ModelUrlMsg(this.networkId, thing));
this.Publish(new BinaryMsg(this.networkId, thing));
}
public bool Publish(IMessage msg) { public bool Publish(IMessage msg) {
int bufferSize = msg.Serialize(ref this.buffer); int bufferSize = msg.Serialize(ref this.buffer);
if (bufferSize <= 0) if (bufferSize <= 0)
@ -240,24 +267,6 @@ namespace RoboidControl {
return true; return true;
} }
// public bool SendBuffer(int bufferSize) {
// //if (this.ipAddress == null)
// // return false;
// // UnityEngine.Debug.Log($"Send msg {buffer[0]} to {ipAddress}");
// //this.udpClient.Send(this.buffer, bufferSize, this.ipAddress, this.port);
// this.udpClient?.Send(this.buffer, bufferSize, this.endPoint);
// return true;
// }
// public bool PublishBuffer(int bufferSize) {
// if (this.broadcastIpAddress == null)
// return false;
// this.udpClient?.Send(this.buffer, bufferSize, this.broadcastIpAddress, this.port);
// return true;
// }
#endregion #endregion
#region Receive #region Receive
@ -350,19 +359,19 @@ namespace RoboidControl {
protected virtual void Process(Participant sender, InvestigateMsg msg) { protected virtual void Process(Participant sender, InvestigateMsg msg) {
#if DEBUG #if DEBUG
Console.WriteLine($"Participant: InvestigateMsg [{msg.networkId}/{msg.thingId}]"); Console.WriteLine($"{this.name}: Process InvestigateMsg [{msg.networkId}/{msg.thingId}]");
#endif #endif
} }
protected virtual void Process(Participant sender, ThingMsg msg) { protected virtual void Process(Participant sender, ThingMsg msg) {
#if DEBUG #if DEBUG
Console.WriteLine($"Participant: Process ThingMsg [{msg.networkId}/{msg.thingId}] {msg.thingType} {msg.parentId}"); Console.WriteLine($"{this.name}: Process ThingMsg [{msg.networkId}/{msg.thingId}] {msg.thingType} {msg.parentId}");
#endif #endif
} }
protected virtual void Process(Participant sender, NameMsg msg) { protected virtual void Process(Participant sender, NameMsg msg) {
#if DEBUG #if DEBUG
Console.WriteLine($"Participant: Process NameMsg [{msg.networkId}/{msg.thingId}] {msg.nameLength} {msg.name}"); Console.WriteLine($"{this.name}: Process NameMsg [{msg.networkId}/{msg.thingId}] {msg.nameLength} {msg.name}");
#endif #endif
Thing thing = sender.Get(msg.thingId); Thing thing = sender.Get(msg.thingId);
@ -372,7 +381,7 @@ namespace RoboidControl {
protected virtual void Process(Participant sender, ModelUrlMsg msg) { protected virtual void Process(Participant sender, ModelUrlMsg msg) {
#if DEBUG #if DEBUG
Console.WriteLine($"Participant: Process ModelUrlMsg [{msg.networkId}/{msg.thingId}] {msg.urlLength} {msg.url}"); Console.WriteLine($"{this.name}: Process ModelUrlMsg [{msg.networkId}/{msg.thingId}] {msg.urlLength} {msg.url}");
#endif #endif
Thing thing = sender.Get(msg.thingId); Thing thing = sender.Get(msg.thingId);
@ -382,28 +391,29 @@ namespace RoboidControl {
protected virtual void Process(Participant sender, PoseMsg msg) { protected virtual void Process(Participant sender, PoseMsg msg) {
#if DEBUG #if DEBUG
Console.WriteLine($"Participant: Process PoseMsg [{msg.networkId}/{msg.thingId}] {msg.poseType}"); Console.WriteLine($"{this.name}: Process PoseMsg [{msg.networkId}/{msg.thingId}] {msg.poseType}");
#endif #endif
Thing thing = sender.Get(msg.thingId); Participant owner = Participant.GetParticipant(msg.networkId);
if (thing != null) { if (owner == null)
return;
Thing thing = owner.Get(msg.thingId);
if (thing == null)
return;
if ((msg.poseType & PoseMsg.Pose_Position) != 0) if ((msg.poseType & PoseMsg.Pose_Position) != 0)
thing.position = msg.position; thing.position = msg.position;
if ((msg.poseType & PoseMsg.Pose_Orientation) != 0) if ((msg.poseType & PoseMsg.Pose_Orientation) != 0)
thing.orientation = msg.orientation; thing.orientation = msg.orientation;
if ((msg.poseType & PoseMsg.Pose_LinearVelocity) != 0) { if ((msg.poseType & PoseMsg.Pose_LinearVelocity) != 0)
thing.linearVelocity = msg.linearVelocity; thing.linearVelocity = msg.linearVelocity;
//Console.Write($"linear velocity = {thing.linearVelocity.ToVector3()}"); if ((msg.poseType & PoseMsg.Pose_AngularVelocity) != 0)
}
if ((msg.poseType & PoseMsg.Pose_AngularVelocity) != 0) {
thing.angularVelocity = msg.angularVelocity; thing.angularVelocity = msg.angularVelocity;
//Console.Write($"angular velocity = {thing.angularVelocity.ToVector3()}");
}
}
} }
protected virtual void Process(Participant sender, BinaryMsg msg) { protected virtual void Process(Participant sender, BinaryMsg msg) {
#if DEBUG #if DEBUG
Console.WriteLine($"Participant: Process BinaryMsg [{msg.networkId}/{msg.thingId}] {msg.dataLength}"); Console.WriteLine($"{this.name}: Process BinaryMsg [{msg.networkId}/{msg.thingId}] {msg.dataLength}");
#endif #endif
Thing thing = sender.Get(msg.thingId); Thing thing = sender.Get(msg.thingId);
thing?.ProcessBinary(msg.data); thing?.ProcessBinary(msg.data);

View File

@ -10,10 +10,12 @@ namespace RoboidControl {
/// </summary> /// </summary>
public class SiteServer : ParticipantUDP { public class SiteServer : ParticipantUDP {
#region Init
/// <summary> /// <summary>
/// Create a new site server /// Create a new site server
/// </summary> /// </summary>
/// <param name="port"></param> /// <param name="port">The port of which to receive the messages</param>
public SiteServer(int port = 7681) : base(port) { public SiteServer(int port = 7681) : base(port) {
this.name = "Site Server"; this.name = "Site Server";
Participant.AddParticipant(this); Participant.AddParticipant(this);
@ -51,6 +53,8 @@ namespace RoboidControl {
} }
#endregion Init
/// <summary> /// <summary>
/// Close the site /// Close the site
/// </summary> /// </summary>