Simulartion env setup (WIP)

This commit is contained in:
Pascal Serrarens 2025-02-04 16:28:20 +01:00
parent b2591ca5cc
commit 246a2b9a3a
5 changed files with 137 additions and 78 deletions

View File

@ -1,5 +1,3 @@
#nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@ -8,63 +6,96 @@ using System.Net.Sockets;
namespace Passer.Control.Core { namespace Passer.Control.Core {
public class Participant { public class Participant : RemoteParticipant {
//public ConnectionMethod connection; public byte[] buffer = new byte[1024];
public IPEndPoint? endPoint = null; public ulong publishInterval = 3000; // = 3 seconds
public UdpClient? udpClient = null;
public string ipAddress = "0.0.0.0";
public string broadcastIpAddress = "255.255.255.255";
public int port;
public byte[] buffer = new byte[256]; //public byte networkId = 0;
public byte networkId = 0;
public string name = "Participant"; public string name = "Participant";
public IPEndPoint endPoint = null;
public UdpClient udpClient = null;
public string broadcastIpAddress = "255.255.255.255";
public readonly ConcurrentQueue<IMessage> messageQueue = new(); public readonly ConcurrentQueue<IMessage> messageQueue = new();
public List<Participant> others = new List<Participant>();
public Participant? GetParticipant(string ipAddress, int port) {
foreach (Participant c in others) {
if (c.ipAddress == ipAddress && c.port == port)
return c;
}
return null;
}
public Participant AddParticipant(string ipAddress, int port) {
Participant participant = new Participant(ipAddress, port);
participant.networkId = (byte)this.others.Count;
//others.Add();
return participant;
}
#region Init #region Init
/// <summary>
/// Create a porticiapnt
/// </summary>
public Participant() { public Participant() {
others.Add(this); others.Add(this);
} }
/// <summary> /// <summary>
/// Create a new participant /// Create a participant with the give UDP port
/// </summary>
/// <param name="port">The port number on which to communicate</param>
public Participant(int port) : this() {
this.port = port;
}
/// <summary>
/// Create a new participant for a site at the given address and port
/// </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>
public Participant(string ipAddress = "0.0.0.0", int port = 7681) : this() { public Participant(string ipAddress = "0.0.0.0", int port = 7681) : this() {
this.ipAddress = ipAddress; this.ipAddress = ipAddress;
this.port = port; this.port = port;
this.endPoint = new IPEndPoint(IPAddress.Parse(ipAddress), port); // for sending this.endPoint = new IPEndPoint(IPAddress.Parse(ipAddress), port); // for sending
this.udpClient = new UdpClient(); // for receiving
this.udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, 0)); this.udpClient = new UdpClient(port); // for receiving
this.udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, port));
this.udpClient.BeginReceive(new AsyncCallback(result => ReceiveUDP(result)), null); this.udpClient.BeginReceive(new AsyncCallback(result => ReceiveUDP(result)), null);
} }
/// <summary>
/// Create a participant using the given udp client
/// </summary>
/// <param name="udpClient">UDP client to use for communication</param>
/// <param name="port">The port number on which to communicate</param>
public Participant(UdpClient udpClient, int port) : this() { public Participant(UdpClient udpClient, int port) : this() {
this.udpClient = udpClient; this.udpClient = udpClient;
this.port = port; this.port = port;
//clients.Add(this);
} }
public List<RemoteParticipant> others = new List<RemoteParticipant>();
public RemoteParticipant GetParticipant(string ipAddress, int port) {
foreach (RemoteParticipant c in others) {
if (c.ipAddress == ipAddress && c.port == port)
return c;
}
return null;
}
public RemoteParticipant AddParticipant(string ipAddress, int port) {
RemoteParticipant participant = new(ipAddress, port);
participant.networkId = (byte)this.others.Count;
//others.Add();
return participant;
}
protected readonly Dictionary<byte, Func<byte, byte, Thing>> thingMsgProcessors = new();
public delegate Thing ThingConstructor(byte networkId, byte thingId);
public void Register(byte thingType, ThingConstructor constr) {
thingMsgProcessors[thingType] = new Func<byte, byte, Thing>(constr);
}
public void Register<ThingClass>(Thing.Type thingType) where ThingClass : Thing {
Register<ThingClass>((byte)thingType);
}
public void Register<ThingClass>(byte thingType) where ThingClass : Thing {
thingMsgProcessors[thingType] = (byte networkId, byte thingId) =>
Activator.CreateInstance(typeof(ThingClass), networkId, thingId) as ThingClass;
Console.WriteLine($"Registering {typeof(ThingClass)} for thing type {thingType}");
}
#endregion Init #endregion Init
#region Update #region Update
@ -76,24 +107,29 @@ namespace Passer.Control.Core {
byte[] data = udpClient.EndReceive(result, ref this.endPoint); byte[] data = udpClient.EndReceive(result, ref this.endPoint);
// This does not yet take multi-packet messages into account! // This does not yet take multi-packet messages into account!
Participant? remoteParticipant = this.GetParticipant(endPoint.Address.ToString(), endPoint.Port); RemoteParticipant remoteParticipant = this.GetParticipant(endPoint.Address.ToString(), endPoint.Port);
if (remoteParticipant == null) { if (remoteParticipant == null)
remoteParticipant = this.AddParticipant(endPoint.Address.ToString(), endPoint.Port); remoteParticipant = this.AddParticipant(endPoint.Address.ToString(), endPoint.Port);
}
ReceiveData(data, remoteParticipant); ReceiveData(data, remoteParticipant);
udpClient.BeginReceive(new AsyncCallback(result => ReceiveUDP(result)), null); udpClient.BeginReceive(new AsyncCallback(result => ReceiveUDP(result)), null);
} }
private static readonly ulong publishInterval = 3000;
private ulong nextPublishMe = 0; private ulong nextPublishMe = 0;
public virtual void Update(ulong currentTimeMs) { public virtual void Update(ulong currentTimeMs = 0) {
if (currentTimeMs > this.nextPublishMe) { if (currentTimeMs == 0) {
#if UNITY_5_3_OR_NEWER
currentTimeMs = (ulong)(UnityEngine.Time.time * 1000);
#endif
}
if (this.publishInterval > 0 && currentTimeMs > this.nextPublishMe) {
this.Publish(new ClientMsg(this.networkId)); this.Publish(new ClientMsg(this.networkId));
// Console.WriteLine($"{this.name} Sent ClientMsg {this.networkId}"); // Console.WriteLine($"{this.name} Sent ClientMsg {this.networkId}");
this.nextPublishMe = currentTimeMs + Participant.publishInterval; this.nextPublishMe = currentTimeMs + this.publishInterval;
} }
// for (int ix = 0; ix < this.others.Count; ix++) { // for (int ix = 0; ix < this.others.Count; ix++) {
@ -110,11 +146,11 @@ namespace Passer.Control.Core {
#region Send #region Send
public void SendThingInfo(Thing thing) { public void SendThingInfo(RemoteParticipant remoteParticipant, Thing thing) {
Console.WriteLine("Send thing info"); Console.WriteLine("Send thing info");
this.Send(new ThingMsg(this.networkId, thing)); this.Send(remoteParticipant, new ThingMsg(this.networkId, thing));
this.Send(new NameMsg(this.networkId, thing)); this.Send(remoteParticipant, new NameMsg(this.networkId, thing));
this.Send(new ModelUrlMsg(this.networkId, thing)); this.Send(remoteParticipant, new ModelUrlMsg(this.networkId, thing));
} }
public bool Send(IMessage msg) { public bool Send(IMessage msg) {
@ -127,6 +163,17 @@ namespace Passer.Control.Core {
return true; return true;
} }
public bool Send(RemoteParticipant remoteParticipant, IMessage msg) {
int bufferSize = msg.Serialize(ref this.buffer);
if (bufferSize <= 0)
return true;
IPEndPoint participantEndpoint = new(IPAddress.Parse(remoteParticipant.ipAddress), remoteParticipant.port);
Console.WriteLine($"msg to {participantEndpoint.Address.ToString()} {participantEndpoint.Port}");
this.udpClient?.Send(this.buffer, bufferSize, participantEndpoint);
return true;
}
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)
@ -159,8 +206,9 @@ namespace Passer.Control.Core {
#region Receive #region Receive
public void ReceiveData(byte[] data, Participant remoteParticipant) { public void ReceiveData(byte[] data, RemoteParticipant remoteParticipant) {
byte msgId = data[0]; byte msgId = data[0];
Console.Write($"received msg {msgId}");
if (msgId == 0xFF) { if (msgId == 0xFF) {
// Timeout // Timeout
return; return;
@ -206,14 +254,14 @@ namespace Passer.Control.Core {
#region Process #region Process
protected virtual void Process(Participant sender, ClientMsg msg) { } protected virtual void Process(RemoteParticipant sender, ClientMsg msg) { }
protected virtual void Process(Participant sender, NetworkIdMsg msg) { protected virtual void Process(RemoteParticipant sender, NetworkIdMsg msg) {
Console.WriteLine($"{this.name} receive network id {this.networkId} {msg.networkId}"); Console.WriteLine($"{this.name} receive network id {this.networkId} {msg.networkId}");
if (this.networkId != msg.networkId) { if (this.networkId != msg.networkId) {
this.networkId = msg.networkId; this.networkId = msg.networkId;
foreach (Thing thing in Thing.GetAllThings()) foreach (Thing thing in Thing.GetAllThings())
sender.SendThingInfo(thing); this.SendThingInfo(sender, thing);
} }
} }

18
RemoteParticipant.cs Normal file
View File

@ -0,0 +1,18 @@
namespace Passer.Control.Core {
public class RemoteParticipant {
public string ipAddress = "0.0.0.0";
public int port = 0;
public byte networkId;
public RemoteParticipant() {
}
public RemoteParticipant(string ipAddress, int port) {
this.ipAddress = ipAddress;
this.port = port;
}
}
}

View File

@ -3,6 +3,8 @@ using System;
namespace Passer.Control.Core { namespace Passer.Control.Core {
public class TemperatureSensor : Thing { public class TemperatureSensor : Thing {
public float temp = 0;
public TemperatureSensor(byte networkId, byte thingId) : base(null, networkId, thingId, (byte)Type.TemperatureSensor) { public TemperatureSensor(byte networkId, byte thingId) : base(null, networkId, thingId, (byte)Type.TemperatureSensor) {
} }

View File

@ -8,50 +8,41 @@ namespace Passer.Control.Core {
public class SiteServer : Participant { public class SiteServer : Participant {
public SiteServer(int port = 7681) { public SiteServer(int port = 7681) {
this.name = "Site Server";
this.publishInterval = 0;
this.ipAddress = "0.0.0.0"; this.ipAddress = "0.0.0.0";
this.port = port; this.port = port;
this.endPoint = new IPEndPoint(IPAddress.Parse(ipAddress), 0); // for sending, but should not be used really...
this.udpClient = new UdpClient(); // for receiving
this.udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, port));
this.udpClient.BeginReceive(new AsyncCallback(result => ReceiveUDP(result)), null);
this.name = "Site Server";
Register<TemperatureSensor>((byte)Thing.Type.TemperatureSensor); this.endPoint = new IPEndPoint(IPAddress.Parse(ipAddress), port); // for sending
Console.Write($"Prepare receive on port {port}");
this.udpClient = new UdpClient(port); // for receiving
//this.udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, port));
IPEndPoint receiveEndpoint = new(IPAddress.Any, port);
this.udpClient.BeginReceive(
new AsyncCallback(result => ReceiveUDP(result)),
new Tuple<UdpClient, IPEndPoint>(this.udpClient, receiveEndpoint));
} }
public override void Update(ulong currentTimeMs) { protected override void Process(RemoteParticipant sender, ClientMsg msg) {
Thing.UpdateAll(currentTimeMs);
}
protected override void Process(Participant sender, ClientMsg msg) {
if (msg.networkId == 0) { if (msg.networkId == 0) {
Console.WriteLine($"{this.name} received New Client -> {sender.networkId}"); Console.WriteLine($"{this.name} received New Client -> {sender.networkId}");
sender.Send(new NetworkIdMsg(sender.networkId)); this.Send(sender, new NetworkIdMsg(sender.networkId));
} }
} }
protected override void Process(Participant sender, NetworkIdMsg msg) { protected override void Process(RemoteParticipant sender, NetworkIdMsg msg) { }
}
delegate Thing ThingConstructor(byte networkId, byte thingId);
readonly Dictionary<byte, ThingConstructor> thingMsgProcessors = new();
public void Register<ThingClass>(byte thingType) where ThingClass : Thing {
thingMsgProcessors[thingType] = (byte networkId, byte thingId) => {
if (Activator.CreateInstance(typeof(Thing), networkId, thingId) is not ThingClass instance)
throw new InvalidOperationException($"Could not created an instance of {(typeof(ThingClass))}.");
return instance;
};
Console.WriteLine($"Registering {typeof(ThingClass)} for thing type {thingType}");
}
protected override void Process(ThingMsg msg) { protected override void Process(ThingMsg msg) {
Console.Write("SiteServer: Processing thing msg");
Thing thing = Thing.Get(msg.networkId, msg.thingId); Thing thing = Thing.Get(msg.networkId, msg.thingId);
if (thing == null) { if (thing == null) {
if (thingMsgProcessors.TryGetValue(msg.thingType, out ThingConstructor thingConstructor)) { if (thingMsgProcessors.ContainsKey(msg.thingType))
thingConstructor(msg.networkId, msg.thingId); thingMsgProcessors[msg.thingType](msg.networkId, msg.thingId);
} // if (thingMsgProcessors.TryGetValue(msg.thingType, out ThingConstructor thingConstructor)) {
// thingConstructor(msg.networkId, msg.thingId);
// }
else { else {
new Thing(this, msg.networkId, msg.thingId, msg.thingType); new Thing(this, msg.networkId, msg.thingId, msg.thingType);
} }

View File

@ -75,7 +75,7 @@ namespace Passer.Control.Core {
public event ChangeHandler OnNameChanged; public event ChangeHandler OnNameChanged;
private string _name; private string _name;
public string name { public virtual string name {
get => _name; get => _name;
set { set {
if (_name != value) { if (_name != value) {