From 9d68f2043df00e46fd0ec05578b1e6e1732ba2fc Mon Sep 17 00:00:00 2001 From: Pascal Serrarens Date: Wed, 4 Jun 2025 11:49:56 +0200 Subject: [PATCH] Alignment with C++ --- Examples/BB2B/BB2B.cs | 4 +- Examples/BB2B/BB2B_Encoder.cs | 4 +- src/Participant.cs | 78 +++++++++--- src/Participants/ParticipantUDP.cs | 9 +- src/Participants/SiteServer.cs | 5 +- src/Thing.cs | 183 +++++++++++++---------------- src/Things/ControlledMotor.cs | 3 +- src/Things/DifferentialDrive.cs | 2 +- src/Things/EncoderMotor.cs | 3 +- test/UnitTest1.cs | 12 +- 10 files changed, 164 insertions(+), 139 deletions(-) diff --git a/Examples/BB2B/BB2B.cs b/Examples/BB2B/BB2B.cs index 2a398ca..3036333 100644 --- a/Examples/BB2B/BB2B.cs +++ b/Examples/BB2B/BB2B.cs @@ -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); } } diff --git a/Examples/BB2B/BB2B_Encoder.cs b/Examples/BB2B/BB2B_Encoder.cs index 21787c6..7870126 100644 --- a/Examples/BB2B/BB2B_Encoder.cs +++ b/Examples/BB2B/BB2B_Encoder.cs @@ -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); } } diff --git a/src/Participant.cs b/src/Participant.cs index 066cb44..43b42d9 100644 --- a/src/Participant.cs +++ b/src/Participant.cs @@ -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 + + /// + /// Create a generic participant + /// private Participant() { - this.root = Thing.CreateRoot(this); - this.root.name = "Root"; - this.Add(this.root); + Thing.CreateRoot(this); } /// @@ -26,47 +29,60 @@ namespace RoboidControl { /// /// The IP address of the participant /// The UDP port of the participant + /// 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; } + /// + /// The local participant for this application + /// public static Participant localParticipant = new(); - public static void ReplaceLocalParticipant(Participant participant) { - Participant.localParticipant = participant; + /// + /// Replace the local participant + /// + /// The new local participant + public static void ReplaceLocalParticipant(Participant newParticipant) { + Participant.localParticipant = newParticipant; } - public Thing root = null; + #endregion Init + + #region Properties + /// /// The Ip Address of a participant. When the participant is local, this contains 0.0.0.0 /// + /// This does not belong here, it should move to ParticipantUDP or something like that in the future public string ipAddress = "0.0.0.0"; /// /// The port number for UDP communication with the participant. This is 0 for isolated participants. /// + /// 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 /// - /// A reference to the representation of the thing in Unity + /// The udpClient for this participant /// - [NonSerialized] - public Unity.Participant component = null; -#endif - + /// This does not belong here, it should move to ParticipantUDP or something like that in the future public UdpClient udpClient = null; /// - /// he network Id to identify the participant + /// The network Id to identify the participant /// public byte networkId = 0; + /// + /// The root thing for this participant + /// + public Thing root = null; + /// /// The things managed by this participant /// @@ -110,26 +126,47 @@ namespace RoboidControl { this.things.Remove(thing); } +#if UNITY_5_3_OR_NEWER + /// + /// A reference to the representation of the thing in Unity + /// + [NonSerialized] + public Unity.Participant component = null; +#endif + + #endregion properties + #region Update /// /// Update all things for this participant /// - /// The current time in milliseconds (optional) - 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); } } + /// + /// Event for a participant + /// public class UpdateEvent { + /// + /// The type of event happened. + /// + /// This value is filled with the Ids of the communication messages. public int messageId; // see the communication messages + /// + /// The thing relevant fo the event + /// public Thing thing; - public Participant participant; } + /// + /// Queue containing events happened to this participant + /// public ConcurrentQueue updateQueue = new(); #endregion Update @@ -155,6 +192,8 @@ namespace RoboidControl { #endregion Send + #region Participant Registry + /// /// The collection of known participants. /// @@ -213,6 +252,7 @@ namespace RoboidControl { Participant.participants.Add(participant); } + #endregion Participant Registery } } \ No newline at end of file diff --git a/src/Participants/ParticipantUDP.cs b/src/Participants/ParticipantUDP.cs index d54a607..4e3de23 100644 --- a/src/Participants/ParticipantUDP.cs +++ b/src/Participants/ParticipantUDP.cs @@ -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; diff --git a/src/Participants/SiteServer.cs b/src/Participants/SiteServer.cs index 682d3eb..3cbea8c 100644 --- a/src/Participants/SiteServer.cs +++ b/src/Participants/SiteServer.cs @@ -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); } diff --git a/src/Thing.cs b/src/Thing.cs index 0aea683..a9a3ef0 100644 --- a/src/Thing.cs +++ b/src/Thing.cs @@ -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; - } - } - - /* /// - /// Create a new thing without communication abilities + /// Create a new Thing /// - /// The type of thing (can use \ref RoboidControl::Thing::Type "Thing.Type") - /// Invoke a OnNewThing event when the thing has been created - public Thing(byte thingType = Type.Undetermined, bool invokeEvent = true) : this(ParticipantUDP.Isolated(), thingType, 0, invokeEvent) { - } - - /// - /// Create a new thing for a participant - /// - /// The owning participant - /// The type of thing (can use \ref RoboidControl::Thing::Type "Thing.Type") - /// The ID of the thing, leave out or set to zero to generate an ID - /// Invoke a OnNewThing event when the thing has been created - 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); - } - } - */ - - /// - /// Create a new child thing - /// - /// The parent thing - /// The type of thing (can use \ref RoboidControl::Thing::Type "Thing.Type") - /// The ID of the thing, leave out or set to zero to generate an ID - /// Invoke a OnNewThing event when the thing has been created - /// The owner will be the same as the owner of the parent thing - /* - public Thing(Thing parent, byte thingType = Type.Undetermined, byte thingId = 0, bool invokeEvent = true) : this(parent.owner, thingType, thingId, invokeEvent) { - this.parent = parent; - } - */ + /// (optional) The parent thing + /// 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. + /// 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; - // } + /// + /// Constructor to create a root thing + /// + /// The participant who will own this root thing + /// This function is private because CreateRoot() should be used instead + 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); + } + + /// + /// Create a root Thing for a participant + /// + /// The participant who will own this root thing + public static void CreateRoot(Participant owner) { + owner.root = new Thing(owner); + } + + /// + /// The root thing for the local participant + /// + public static Thing localRoot { + get { + Participant participant = Participant.localParticipant; + return participant.root; + } + } /// /// Function which can be used to create components in external engines. @@ -147,14 +117,13 @@ namespace RoboidControl { #endregion Init + /// + /// Terminated things are no longer updated + /// public bool terminate = false; #region Properties - /// - /// The participant owning this thing - /// - public Participant owner = null; /// /// The ID of this thing /// @@ -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; + /// + /// The participant owning this thing + /// + public Participant owner = null; + private string _name = ""; /// /// The name of the thing @@ -186,6 +160,9 @@ namespace RoboidControl { /// /// An URL pointing to the location where a model of the thing can be found /// + /// Although the roboid implementation is not dependent on the model, + /// the only official supported model formats are .png (sprite), .gltf and .glb + /// public string modelUrl { get => _modelUrl; set { @@ -230,6 +207,12 @@ namespace RoboidControl { } } + /// + /// Indication whether this is a root thing + /// + public bool isRoot { + get => this == localRoot || this.parent == null; + } /// /// The children of this thing @@ -392,32 +375,11 @@ namespace RoboidControl { #region Update - /// - /// Get the current time in milliseconds - /// - /// The current time in milliseconds - public static ulong GetTimeMs() { -#if UNITY_5_3_OR_NEWER - return (ulong)(UnityEngine.Time.time * 1000); -#else - return TimeManager.GetCurrentTimeMilliseconds(); -#endif - } - /// /// Update de state of the thing /// /// When true, this will Update the descendants recursively - public void Update(bool recurse = false) { - Update(GetTimeMs(), recurse); - } - // #endif - /// - /// Update this thing - /// - /// he current clock time in milliseconds; if this is zero, the current time is retrieved automatically - /// When true, this will Update the descendants recursively - 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); } } } + /// + /// An event happened to this event + /// + /// 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 + /// + /// The type of event happened. + /// + /// This value is filled with the Ids of the communication messages. + public int messageId; }; + /// + /// Queue containing events happened to this thing + /// public ConcurrentQueue updateQueue = new(); + /// + /// Get the current time in milliseconds + /// + /// The current time in milliseconds + public static ulong GetTimeMs() { +#if UNITY_5_3_OR_NEWER + return (ulong)(UnityEngine.Time.time * 1000); +#else + return TimeManager.GetCurrentTimeMilliseconds(); +#endif + } + #endregion Update /// diff --git a/src/Things/ControlledMotor.cs b/src/Things/ControlledMotor.cs index fb5ad79..f08df2b 100644 --- a/src/Things/ControlledMotor.cs +++ b/src/Things/ControlledMotor.cs @@ -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; diff --git a/src/Things/DifferentialDrive.cs b/src/Things/DifferentialDrive.cs index c266d4f..78459ae 100644 --- a/src/Things/DifferentialDrive.cs +++ b/src/Things/DifferentialDrive.cs @@ -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; diff --git a/src/Things/EncoderMotor.cs b/src/Things/EncoderMotor.cs index 7a1302e..e20f6b5 100644 --- a/src/Things/EncoderMotor.cs +++ b/src/Things/EncoderMotor.cs @@ -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; diff --git a/test/UnitTest1.cs b/test/UnitTest1.cs index 9fc8f79..0cf87a5 100644 --- a/test/UnitTest1.cs +++ b/test/UnitTest1.cs @@ -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();