Alignment with C++

This commit is contained in:
Pascal Serrarens 2025-06-04 11:49:56 +02:00
parent 620b7e5273
commit 9d68f2043d
10 changed files with 164 additions and 139 deletions

View File

@ -25,7 +25,7 @@ namespace RoboidControl {
};
}
public override void Update(ulong currentTimeMs, bool recurse = true) {
public override void Update(bool recurse = true) {
// The left wheel turns forward when nothing is touched on the right side
// and turn backward when the roboid hits something on the right
@ -40,7 +40,7 @@ namespace RoboidControl {
this.SetWheelVelocity(leftWheelSpeed, rightWheelSpeed);
base.Update(currentTimeMs, recurse);
base.Update(recurse);
}
}

View File

@ -33,7 +33,7 @@ namespace RoboidControl {
};
}
public override void Update(ulong currentTimeMs, bool recurse = true) {
public override void Update(bool recurse = true) {
// The left wheel turns forward when nothing is touched on the right side
// and turn backward when the roboid hits something on the right
@ -48,7 +48,7 @@ namespace RoboidControl {
this.SetWheelAngularVelocity(leftWheelVelocity, rightWheelVelocity);
base.Update(currentTimeMs, recurse);
base.Update(recurse);
}
}

View File

@ -15,10 +15,13 @@ namespace RoboidControl {
/// It is used as a basis for the local participant, but also as a reference to remote participants.
public class Participant {
#region Init
/// <summary>
/// Create a generic participant
/// </summary>
private Participant() {
this.root = Thing.CreateRoot(this);
this.root.name = "Root";
this.Add(this.root);
Thing.CreateRoot(this);
}
/// <summary>
@ -26,47 +29,60 @@ namespace RoboidControl {
/// </summary>
/// <param name="ipAddress">The IP address of the participant</param>
/// <param name="port">The UDP port of the participant</param>
/// <remarks>This does not belong here, it should move to ParticipantUDP or something like that in the future
public Participant(string ipAddress, int port, Participant localParticipant = null) {
this.ipAddress = ipAddress;
this.port = port;
this.root = Thing.CreateRoot(this);
this.root.name = "Root";
Thing.CreateRoot(this);
if (localParticipant != null)
this.udpClient = localParticipant.udpClient;
}
/// <summary>
/// The local participant for this application
/// </summary>
public static Participant localParticipant = new();
public static void ReplaceLocalParticipant(Participant participant) {
Participant.localParticipant = participant;
/// <summary>
/// Replace the local participant
/// </summary>
/// <param name="newParticipant">The new local participant</param>
public static void ReplaceLocalParticipant(Participant newParticipant) {
Participant.localParticipant = newParticipant;
}
public Thing root = null;
#endregion Init
#region Properties
/// <summary>
/// The Ip Address of a participant. When the participant is local, this contains 0.0.0.0
/// </summary>
/// <remarks>This does not belong here, it should move to ParticipantUDP or something like that in the future
public string ipAddress = "0.0.0.0";
/// <summary>
/// The port number for UDP communication with the participant. This is 0 for isolated participants.
/// </summary>
/// <remarks>This does not belong here, it should move to ParticipantUDP or something like that in the future
public int port = 0;
#if UNITY_5_3_OR_NEWER
/// <summary>
/// A reference to the representation of the thing in Unity
/// The udpClient for this participant
/// </summary>
[NonSerialized]
public Unity.Participant component = null;
#endif
/// <remarks>This does not belong here, it should move to ParticipantUDP or something like that in the future
public UdpClient udpClient = null;
/// <summary>
/// he network Id to identify the participant
/// The network Id to identify the participant
/// </summary>
public byte networkId = 0;
/// <summary>
/// The root thing for this participant
/// </summary>
public Thing root = null;
/// <summary>
/// The things managed by this participant
/// </summary>
@ -110,26 +126,47 @@ namespace RoboidControl {
this.things.Remove(thing);
}
#if UNITY_5_3_OR_NEWER
/// <summary>
/// A reference to the representation of the thing in Unity
/// </summary>
[NonSerialized]
public Unity.Participant component = null;
#endif
#endregion properties
#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) {
public virtual void Update() {
int n = this.things.Count;
for (int ix = 0; ix < n; ix++) {
Thing thing = this.things[ix];
if (thing != null)
thing.Update(currentTimeMS, true);
thing.Update(true);
}
}
/// <summary>
/// Event for a participant
/// </summary>
public class UpdateEvent {
/// <summary>
/// The type of event happened.
/// </summary>
/// This value is filled with the Ids of the communication messages.
public int messageId; // see the communication messages
/// <summary>
/// The thing relevant fo the event
/// </summary>
public Thing thing;
public Participant participant;
}
/// <summary>
/// Queue containing events happened to this participant
/// </summary>
public ConcurrentQueue<UpdateEvent> updateQueue = new();
#endregion Update
@ -155,6 +192,8 @@ namespace RoboidControl {
#endregion Send
#region Participant Registry
/// <summary>
/// The collection of known participants.
/// </summary>
@ -213,6 +252,7 @@ namespace RoboidControl {
Participant.participants.Add(participant);
}
#endregion Participant Registery
}
}

View File

@ -150,9 +150,8 @@ namespace RoboidControl {
protected ulong nextPublishMe = 0;
protected ulong nextSendUpdate = 0;
public override void Update(ulong currentTimeMS = 0) {
if (currentTimeMS == 0)
currentTimeMS = Thing.GetTimeMs();
public override void Update() {
ulong currentTimeMS = Thing.GetTimeMs();
if (this.isIsolated == false) {
if (this.publishIntervalMS > 0 && currentTimeMS > this.nextPublishMe) {
@ -185,7 +184,7 @@ namespace RoboidControl {
// Because when a thing creates a thing in the update,
// that new thing is not sent out (because of hierarchyChanged)
// before it is updated itself: it is immediatedly updated!
thing.Update(currentTimeMS, false);
thing.Update(false);
if (!(this.isIsolated || this.networkId == 0)) {
if (thing.terminate) {
DestroyMsg destroyMsg = new(this.networkId, thing);
@ -213,7 +212,7 @@ namespace RoboidControl {
if (participant == null || participant == this)
continue;
participant.Update(currentTimeMS);
participant.Update();
if (this.isIsolated)
continue;

View File

@ -72,7 +72,7 @@ namespace RoboidControl {
if (thing == null)
continue;
thing.Update(currentTimeMS, false);
thing.Update(false);
if (this.isIsolated == false) {
// Send to all other participants
@ -104,8 +104,7 @@ namespace RoboidControl {
// this.Send(sender, new NetworkIdMsg(sender.networkId));
sender.Send(new NetworkIdMsg(sender.networkId));
UpdateEvent e = new() {
messageId = ParticipantMsg.Id,
participant = sender
messageId = ParticipantMsg.Id
};
this.updateQueue.Enqueue(e);
}

View File

@ -38,73 +38,14 @@ namespace RoboidControl {
#region Init
private Thing(Participant owner) {
this.type = Type.Root;
this.positionUpdated = true;
this.orientationUpdated = true;
this.hierarchyChanged = true;
this.owner = owner;
this.parent = null;
}
public static Thing CreateRoot(Participant owner) {
return new Thing(owner);
}
static Thing localRoot {
get {
Participant participant = Participant.localParticipant;
return participant.root;
}
}
/*
/// <summary>
/// Create a new thing without communication abilities
/// Create a new Thing
/// </summary>
/// <param name="thingType">The type of thing (can use \ref RoboidControl::Thing::Type "Thing.Type")</param>
/// <param name="invokeEvent">Invoke a OnNewThing event when the thing has been created</param>
public Thing(byte thingType = Type.Undetermined, bool invokeEvent = true) : this(ParticipantUDP.Isolated(), thingType, 0, invokeEvent) {
}
/// <summary>
/// Create a new thing for a participant
/// </summary>
/// <param name="owner">The owning participant</param>
/// <param name="thingType">The type of thing (can use \ref RoboidControl::Thing::Type "Thing.Type")</param>
/// <param name="thingId">The ID of the thing, leave out or set to zero to generate an ID</param>
/// <param name="invokeEvent">Invoke a OnNewThing event when the thing has been created</param>
public Thing(Participant owner, byte thingType = Type.Undetermined, byte thingId = 0, bool invokeEvent = true) {
this.owner = owner;
this.id = thingId;
this.type = thingType;
if (this.owner != null)
this.owner.Add(this);
if (invokeEvent) {
Participant.UpdateEvent e = new() {
messageId = ThingMsg.id,
thing = this
};
this.owner.updateQueue.Enqueue(e);
}
}
*/
/// <summary>
/// Create a new child thing
/// </summary>
/// <param name="parent">The parent thing</param>
/// <param name="thingType">The type of thing (can use \ref RoboidControl::Thing::Type "Thing.Type")</param>
/// <param name="thingId">The ID of the thing, leave out or set to zero to generate an ID</param>
/// <param name="invokeEvent">Invoke a OnNewThing event when the thing has been created</param>
/// <note>The owner will be the same as the owner of the parent thing</note>
/*
public Thing(Thing parent, byte thingType = Type.Undetermined, byte thingId = 0, bool invokeEvent = true) : this(parent.owner, thingType, thingId, invokeEvent) {
this.parent = parent;
}
*/
/// <param name="parent">(optional) The parent thing</param>
/// <note>The owner will be the same as the owner of the parent thing, it will
/// be Participant.LocalParticipant if the parent is not specified. A thing
/// without a parent will be connected to the root thing.
/// </note>
public Thing(Thing parent = default) {
this.type = Type.Undetermined;
@ -127,12 +68,41 @@ namespace RoboidControl {
this.owner.updateQueue.Enqueue(e);
}
// public static Thing CreateRemote(Participant owner, byte thingId) {
// Thing remoteThing = new(owner.root) {
// id = thingId
// };
// return remoteThing;
// }
/// <summary>
/// Constructor to create a root thing
/// </summary>
/// <param name="owner">The participant who will own this root thing</param>
/// <remarks>This function is private because CreateRoot() should be used instead</remarks>
private Thing(Participant owner) {
this.type = Type.Root;
this.name = "Root";
this.positionUpdated = true;
this.orientationUpdated = true;
this.hierarchyChanged = true;
this.owner = owner;
this.parent = null;
this.owner.Add(this);
}
/// <summary>
/// Create a root Thing for a participant
/// </summary>
/// <param name="owner">The participant who will own this root thing</param>
public static void CreateRoot(Participant owner) {
owner.root = new Thing(owner);
}
/// <summary>
/// The root thing for the local participant
/// </summary>
public static Thing localRoot {
get {
Participant participant = Participant.localParticipant;
return participant.root;
}
}
/// <summary>
/// Function which can be used to create components in external engines.
@ -147,14 +117,13 @@ namespace RoboidControl {
#endregion Init
/// <summary>
/// Terminated things are no longer updated
/// </summary>
public bool terminate = false;
#region Properties
/// <summary>
/// The participant owning this thing
/// </summary>
public Participant owner = null;
/// <summary>
/// The ID of this thing
/// </summary>
@ -166,6 +135,11 @@ namespace RoboidControl {
/// This can be either a \ref RoboidControl::Thing::Type "Thing.Type" or a byte value for custom types.
public byte type = Type.Undetermined;
/// <summary>
/// The participant owning this thing
/// </summary>
public Participant owner = null;
private string _name = "";
/// <summary>
/// The name of the thing
@ -186,6 +160,9 @@ namespace RoboidControl {
/// <summary>
/// An URL pointing to the location where a model of the thing can be found
/// </summary>
/// <remarks>Although the roboid implementation is not dependent on the model,
/// the only official supported model formats are .png (sprite), .gltf and .glb
/// </remarks>
public string modelUrl {
get => _modelUrl;
set {
@ -230,6 +207,12 @@ namespace RoboidControl {
}
}
/// <summary>
/// Indication whether this is a root thing
/// </summary>
public bool isRoot {
get => this == localRoot || this.parent == null;
}
/// <summary>
/// The children of this thing
@ -392,32 +375,11 @@ namespace RoboidControl {
#region Update
/// <summary>
/// Get the current time in milliseconds
/// </summary>
/// <returns>The current time in milliseconds</returns>
public static ulong GetTimeMs() {
#if UNITY_5_3_OR_NEWER
return (ulong)(UnityEngine.Time.time * 1000);
#else
return TimeManager.GetCurrentTimeMilliseconds();
#endif
}
/// <summary>
/// Update de state of the thing
/// </summary>
/// <param name="recurse">When true, this will Update the descendants recursively</param>
public void Update(bool recurse = false) {
Update(GetTimeMs(), recurse);
}
// #endif
/// <summary>
/// Update this thing
/// </summary>
/// <param name="currentTime">he current clock time in milliseconds; if this is zero, the current time is retrieved automatically</param>
/// <param name="recurse">When true, this will Update the descendants recursively</param>
public virtual void Update(ulong currentTimeMs, bool recurse = false) {
public virtual void Update(bool recurse = false) {
this.positionUpdated = false;
this.orientationUpdated = false;
this.linearVelocityUpdated = false;
@ -430,19 +392,42 @@ namespace RoboidControl {
Thing child = this.children[childIx];
if (child == null)
continue;
child.Update(currentTimeMs, recurse);
child.Update(recurse);
}
}
}
/// <summary>
/// An event happened to this event
/// </summary>
/// <note>The messageId indicates which kind of event happened
public class CoreEvent {
public CoreEvent(int messageId) {
this.messageId = messageId;
}
public int messageId; // see the communication messages
/// <summary>
/// The type of event happened.
/// </summary>
/// This value is filled with the Ids of the communication messages.
public int messageId;
};
/// <summary>
/// Queue containing events happened to this thing
/// </summary>
public ConcurrentQueue<CoreEvent> updateQueue = new();
/// <summary>
/// Get the current time in milliseconds
/// </summary>
/// <returns>The current time in milliseconds</returns>
public static ulong GetTimeMs() {
#if UNITY_5_3_OR_NEWER
return (ulong)(UnityEngine.Time.time * 1000);
#else
return TimeManager.GetCurrentTimeMilliseconds();
#endif
}
#endregion Update
/// <summary>

View File

@ -52,13 +52,14 @@ namespace RoboidControl {
float pidD = 0.0F;
//float pidI = 0.0F;
public override void Update(ulong currentTimeMs, bool recurse = false) {
public override void Update(bool recurse = false) {
float actualSpeed = this.encoder.angularSpeed;
// Simplified rotation direction, shouldbe improved
// This goes wrong when the target speed is inverted and the motor axcle is still turning in the old direction
if (this.targetAngularSpeed < 0)
actualSpeed = -actualSpeed;
ulong currentTimeMs = Thing.GetTimeMs();
float deltaTime = (currentTimeMs - this.lastUpdateTime) / 1000.0f;
float error = this.targetAngularSpeed - actualSpeed;

View File

@ -86,7 +86,7 @@ namespace RoboidControl {
}
/// @copydoc RoboidControl::Thing::Update(unsigned long)
public override void Update(ulong currentMs, bool recursive = true) {
public override void Update(bool recursive = true) {
if (this.linearVelocityUpdated) {
// this assumes forward velocity only....
float linearVelocity = this.linearVelocity.distance;

View File

@ -52,13 +52,14 @@ namespace RoboidControl {
float pidD = 0.0F;
//float pidI = 0.0F;
public override void Update(ulong currentTimeMs, bool recurse = false) {
public override void Update(bool recurse = false) {
float actualSpeed = this.encoder.angularSpeed;
// Simplified rotation direction, shouldbe improved
// This goes wrong when the target speed is inverted and the motor axcle is still turning in the old direction
if (this.targetAngularSpeed < 0)
actualSpeed = -actualSpeed;
ulong currentTimeMs = Thing.GetTimeMs();
float deltaTime = (currentTimeMs - this.lastUpdateTime) / 1000.0f;
float error = this.targetAngularSpeed - actualSpeed;

View File

@ -18,7 +18,7 @@ namespace RoboidControl.test {
ulong milliseconds = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
ulong startTime = milliseconds;
while (milliseconds < startTime + 7000) {
participant.Update(milliseconds);
participant.Update();
Thread.Sleep(100);
milliseconds = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
@ -34,7 +34,7 @@ namespace RoboidControl.test {
ulong milliseconds = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
ulong startTime = milliseconds;
while (milliseconds < startTime + 7000) {
siteServer.Update(milliseconds);
siteServer.Update();
Thread.Sleep(100);
milliseconds = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
@ -51,8 +51,8 @@ namespace RoboidControl.test {
ulong milliseconds = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
ulong startTime = milliseconds;
while (milliseconds < startTime + 1000) {
siteServer.Update(milliseconds);
participant.Update(milliseconds);
siteServer.Update();
participant.Update();
Thread.Sleep(100);
milliseconds = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
@ -73,8 +73,8 @@ namespace RoboidControl.test {
ulong milliseconds = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
ulong startTime = milliseconds;
while (milliseconds < startTime + 7000) {
siteServer.Update(milliseconds);
participant.Update(milliseconds);
siteServer.Update();
participant.Update();
Thread.Sleep(100);
milliseconds = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();