Aligned Thing implementation

This commit is contained in:
Pascal Serrarens 2025-04-29 11:46:57 +02:00
parent 052620c0f0
commit 346263d311
4 changed files with 203 additions and 211 deletions

View File

@ -115,7 +115,7 @@ namespace RoboidControl {
} }
if (thing == null) if (thing == null)
thing = new Thing(sender, msg.networkId, msg.thingId, msg.thingType); thing = new Thing(sender, msg.thingType, msg.thingId);
if (msg.parentId != 0) { if (msg.parentId != 0) {
thing.parent = sender.Get(msg.parentId); thing.parent = sender.Get(msg.parentId);

View File

@ -10,90 +10,83 @@ namespace RoboidControl {
[Serializable] [Serializable]
public class Thing { public class Thing {
#region Types
/// <summary> /// <summary>
/// Predefined thing types /// Predefined thing types
/// </summary> /// </summary>
public enum Type { public static class Type {
Undetermined, public const byte Undetermined = 0x00;
// Sensor // sensor
Switch, public const byte Switch = 0x01;
DistanceSensor, public const byte DistanceSensor = 0x02;
DirectionalSensor, public const byte DirectionalSensor = 0x03;
TemperatureSensor, public const byte TemperatureSensor = 0x04;
TouchSensor, public const byte TouchSensor = 0x05;
// Motor // Motor
ControlledMotor, public const byte ControlledMotor = 0x06;
UncontrolledMotor, public const byte UncontrolledMotor = 0x07;
Servo, public const byte Servo = 0x08;
// Other // Other
Roboid, public const byte Roboid = 0x09;
Humanoid, public const byte HUmanoid = 0x010;
ExternalSensor public const byte ExternalSensor = 0x11;
}; public const byte Animator = 0x40;
}
public delegate void ChangeHandler();
public delegate void SphericalHandler(Spherical v);
public delegate void ThingHandler(Thing t);
#endregion Types
#region Init #region Init
/// <summary>
/// Create a new thing without communication abilities
/// </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> /// <summary>
/// Create a new thing for a participant /// Create a new thing for a participant
/// </summary> /// </summary>
/// <param name="owner">The participant owning the thing</param> /// <param name="owner">The owning participant</param>
/// <param name="thingType">The type of thing</param> /// <param name="thingType">The type of thing (can use \ref RoboidControl::Thing::Type "Thing.Type")</param>
public Thing(Participant owner, byte thingType = (byte)Type.Undetermined, bool invokeEvent = true) { /// <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.owner = owner;
this.id = thingId;
this.type = thingType; this.type = thingType;
if (this.owner != null) if (this.owner != null)
this.owner.Add(this); this.owner.Add(this);
if (invokeEvent) if (invokeEvent)
InvokeNewThing(this); InvokeNewThing(this);
} }
public Thing(Participant owner) : this(owner, Type.Undetermined) { }
/// <summary>
/// Create a new thing for a participant
/// </summary>
/// <param name="owner">The participant owning the thing</param>
/// <param name="thingType">The type of thing</param>
public Thing(Participant owner, Type thingType = Type.Undetermined, bool invokeEvent = true) : this(owner, (byte)thingType, invokeEvent) {
}
/// <summary>
/// Create a new thing without communication abilities
/// </summary>
/// <param name="thingType">The type of thing</param>
public Thing(byte thingType = (byte)Type.Undetermined, bool invokeEvent = true) : this(ParticipantUDP.Isolated(), thingType, invokeEvent) {
}
/// <summary> /// <summary>
/// Create a new thing as a child of another thing /// Create a new child thing
/// </summary> /// </summary>
/// <param name="parent">The parent thing</param> /// <param name="parent">The parent thing</param>
/// <param name="thingType">The type of thing</param> /// <param name="thingType">The type of thing (can use \ref RoboidControl::Thing::Type "Thing.Type")</param>
public Thing(Thing parent, byte thingType = (byte)Type.Undetermined, bool invokeEvent = true) : this(parent.owner, thingType, invokeEvent) { /// <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, bool invokeEvent = true) : this(parent.owner, thingType, 0, invokeEvent) {
this.parent = parent; this.parent = parent;
} }
/// <summary> // /// <summary>
/// Create a new thing for the given participant // /// Create a new thing for the given participant
/// </summary> // /// </summary>
/// <param name="owner">The participant owning the thing</param> // /// <param name="owner">The participant owning the thing</param>
/// <param name="networkId">The network ID of the thing</param> // /// <param name="networkId">The network ID of the thing</param>
/// <param name="thingId">The ID of the thing</param> // /// <param name="thingId">The ID of the thing</param>
/// <param name="thingType">The type of thing</param> // /// <param name="thingType">The type of thing</param>
public Thing(Participant owner, byte networkId, byte thingId, byte thingType = 0) { // public Thing(Participant owner, byte networkId, byte thingId, byte thingType = 0) {
this.owner = owner; // this.owner = owner;
this.id = thingId; // this.id = thingId;
this.type = thingType; // this.type = thingType;
this.networkId = networkId; // this.networkId = networkId;
// Console.Write($"New thing added to {owner}"); // // Console.Write($"New thing added to {owner}");
this.owner.Add(this); // this.owner.Add(this);
InvokeNewThing(this); // InvokeNewThing(this);
} // }
/// <summary> /// <summary>
/// Function which can be used to create components in external engines. /// Function which can be used to create components in external engines.
@ -108,6 +101,8 @@ namespace RoboidControl {
#endregion Init #endregion Init
public bool terminate = false;
#region Properties #region Properties
/// <summary> /// <summary>
@ -115,11 +110,6 @@ namespace RoboidControl {
/// </summary> /// </summary>
public Participant owner = null; public Participant owner = null;
/// <summary> /// <summary>
/// The network ID of this thing.
/// </summary>
/// @note This field will likely disappear in future versions
public byte networkId = 0;
/// <summary>
/// The ID of this thing /// The ID of this thing
/// </summary> /// </summary>
public byte id = 0; public byte id = 0;
@ -127,105 +117,8 @@ namespace RoboidControl {
/// <summary> /// <summary>
/// The type of this thing. /// The type of this thing.
/// </summary> /// </summary>
/// This can be either a Thing::Type (needs casting) or a byte value for custom types. /// This can be either a \ref RoboidControl::Thing::Type "Thing.Type" or a byte value for custom types.
public byte type = (byte)Type.Undetermined; public byte type = Type.Undetermined;
private Thing _parent;
/// <summary>
/// The parent of this thing
/// </summary>
public Thing parent {
get => _parent;
set {
if (_parent == value)
return;
if (value == null) {
_parent?.RemoveChild(this);
_parent = null;
}
else {
value.AddChild(this);
}
this.hierarchyChanged = true;
}
}
/// <summary>
/// Add a child Thing to this Thing
/// </summary>
/// <param name="child">The Thing which should become a child</param>
/// @remark When the Thing is already a child, it will not be added again
public void AddChild(Thing child) {
if (children.Find(thing => thing == child) != null)
return;
child._parent = this;
children.Add(child);
}
/// <summary>
/// Remove the given thing as a child of this thing
/// </summary>
/// <param name="child">The child to remove</param>
/// <returns>True when the child was present or false when it was not found</returns>
public bool RemoveChild(Thing child) {
return children.Remove(child);
}
/// <summary>
/// Get a child by thing Id
/// </summary>
/// <param name="thingId"></param>
/// <param name="recursively"></param>
/// <returns></returns>
Thing GetChild(byte thingId, bool recursively = false) {
foreach (Thing child in this.children) {
if (child == null)
continue;
if (child.id == thingId)
return child;
if (recursively) {
Thing foundChild = child.GetChild(thingId, recursively);
if (foundChild != null)
return foundChild;
}
}
return null;
}
/// <summary>
/// Find a child by name
/// </summary>
/// <param name="name">The name of the child thing</param>
/// <param name="recursively">If true, the name will be searched through descendants recursively</param>
/// <returns>The found thing or null when nothing is found</returns>
Thing FindChild(string name, bool recursively = true) {
foreach (Thing child in this.children) {
if (child == null)
continue;
if (child.name == name)
return child;
if (recursively) {
Thing foundChild = child.FindChild(name, recursively);
if (foundChild != null)
return foundChild;
}
}
return null;
}
/// <summary>
/// Indicator that the hierarchy of the thing has changed
/// </summary>
public bool hierarchyChanged = true;
/// <summary>
/// The children of this thing
/// </summary>
[NonSerialized]
protected List<Thing> children = new();
private string _name = ""; private string _name = "";
/// <summary> /// <summary>
@ -252,6 +145,120 @@ namespace RoboidControl {
/// </summary> /// </summary>
public string modelUrl = ""; public string modelUrl = "";
#if UNITY_5_3_OR_NEWER
/// <summary>
/// A reference to the representation of the thing in Unity
/// </summary>
[NonSerialized]
public Unity.Thing component = null;
#endif
#endregion Properties
#region Hierarchy
private Thing _parent;
/// <summary>
/// The parent of this thing
/// </summary>
public Thing parent {
get => _parent;
set {
if (_parent == value)
return;
if (value == null) {
_parent?.RemoveChild(this);
_parent = null;
}
else {
value.AddChild(this);
}
this.hierarchyChanged = true;
}
}
/// <summary>
/// The children of this thing
/// </summary>
[NonSerialized]
protected List<Thing> children = new();
/// <summary>
/// Add a child Thing to this Thing
/// </summary>
/// <param name="child">The Thing which should become a child</param>
/// @remark When the Thing is already a child, it will not be added again
public void AddChild(Thing child) {
if (children.Find(thing => thing == child) != null)
return;
child._parent = this;
children.Add(child);
}
/// <summary>
/// Remove the given thing as a child of this thing
/// </summary>
/// <param name="child">The child to remove</param>
/// <returns>True when the child was present or false when it was not found</returns>
public bool RemoveChild(Thing child) {
return children.Remove(child);
}
/// <summary>
/// Get a child by thing Id
/// </summary>
/// <param name="thingId">The thing ID to find</param>
/// <param name="recurse">Look recursively through all descendants</param>
/// <returns></returns>
Thing GetChild(byte thingId, bool recurse = false) {
foreach (Thing child in this.children) {
if (child == null)
continue;
if (child.id == thingId)
return child;
if (recurse) {
Thing foundChild = child.GetChild(thingId, recurse);
if (foundChild != null)
return foundChild;
}
}
return null;
}
/// <summary>
/// Find a child by name
/// </summary>
/// <param name="name">The name of the child thing</param>
/// <param name="recurse">Look recursively through all descendants</param>
/// <returns>The found thing or null when nothing is found</returns>
Thing FindChild(string name, bool recurse = true) {
foreach (Thing child in this.children) {
if (child == null)
continue;
if (child.name == name)
return child;
if (recurse) {
Thing foundChild = child.FindChild(name, recurse);
if (foundChild != null)
return foundChild;
}
}
return null;
}
/// <summary>
/// Indicator that the hierarchy of the thing has changed
/// </summary>
public bool hierarchyChanged = true;
#endregion Hierarchy
#region Pose
private Spherical _position = Spherical.zero; private Spherical _position = Spherical.zero;
/// <summary> /// <summary>
/// The position of the thing in local space, in meters. /// The position of the thing in local space, in meters.
@ -340,20 +347,14 @@ namespace RoboidControl {
/// </summary> /// </summary>
public bool angularVelocityUpdated = false; public bool angularVelocityUpdated = false;
#if UNITY_5_3_OR_NEWER #endregion Pose
#region Update
/// <summary> /// <summary>
/// A reference to the representation of the thing in Unity /// Get the current time in milliseconds
/// </summary> /// </summary>
[NonSerialized] /// <returns>The current time in milliseconds</returns>
public Unity.Thing component = null;
#endif
public bool terminate = false;
#endregion Properties
#region Methods
public static ulong GetTimeMs() { public static ulong GetTimeMs() {
#if UNITY_5_3_OR_NEWER #if UNITY_5_3_OR_NEWER
return (ulong)(UnityEngine.Time.time * 1000); return (ulong)(UnityEngine.Time.time * 1000);
@ -365,16 +366,17 @@ namespace RoboidControl {
/// <summary> /// <summary>
/// Update de state of the thing /// Update de state of the thing
/// </summary> /// </summary>
/// <param name="recursively">When true, this will Update the descendants recursively</param> /// <param name="recurse">When true, this will Update the descendants recursively</param>
public void Update(bool recursively = false) { public void Update(bool recurse = false) {
Update(GetTimeMs(), recursively); Update(GetTimeMs(), recurse);
} }
// #endif // #endif
/// <summary> /// <summary>
/// Update this thing /// Update this thing
/// </summary> /// </summary>
/// <param name="currentTime">The current time in milliseconds</param> /// <param name="currentTime">he current clock time in milliseconds; if this is zero, the current time is retrieved automatically</param>
public virtual void Update(ulong currentTimeMs, bool recursively = false) { /// <param name="recurse">When true, this will Update the descendants recursively</param>
public virtual void Update(ulong currentTimeMs, bool recurse = false) {
if (this.positionUpdated || this.orientationUpdated) if (this.positionUpdated || this.orientationUpdated)
OnPoseChanged?.Invoke(); OnPoseChanged?.Invoke();
this.positionUpdated = false; this.positionUpdated = false;
@ -384,31 +386,19 @@ namespace RoboidControl {
//this.hierarchyChanged = false; //this.hierarchyChanged = false;
// should recurse over children... // should recurse over children...
if (recursively) { if (recurse) {
for (byte childIx = 0; childIx < this.children.Count; childIx++) { for (byte childIx = 0; childIx < this.children.Count; childIx++) {
Thing child = this.children[childIx]; Thing child = this.children[childIx];
if (child == null) if (child == null)
continue; continue;
child.Update(currentTimeMs, recursively); child.Update(currentTimeMs, recurse);
} }
} }
} }
/// <summary> public delegate void ChangeHandler();
/// Function used to generate binary data for this thing public delegate void SphericalHandler(Spherical v);
/// </summary> public delegate void ThingHandler(Thing t);
/// <returns>a byte array with the binary data</returns>
/// @sa Passer::RoboidControl::BinaryMsg
public virtual byte[] GenerateBinary() { return Array.Empty<byte>(); }
/// <summary>
/// Function used to process binary data received for this thing
/// </summary>
/// <param name="bytes">The binary data</param>
public virtual void ProcessBinary(byte[] bytes) {
}
#endregion Methods
/// <summary> /// <summary>
/// Event triggered when a new thing has been created /// Event triggered when a new thing has been created
@ -422,18 +412,20 @@ namespace RoboidControl {
OnNewThing?.Invoke(thing); OnNewThing?.Invoke(thing);
} }
#endregion Update
/// <summary> /// <summary>
/// Check if the thing has the given properaties /// Function used to generate binary data for this thing
/// </summary> /// </summary>
/// <param name="thing">The thing to check</param> /// <returns>A byte array with the binary data</returns>
/// <param name="networkId">The network ID to compare to</param> /// @sa Passer::RoboidControl::BinaryMsg
/// <param name="thingId">The thing ID to compare to</param> public virtual byte[] GenerateBinary() { return Array.Empty<byte>(); }
/// <returns>True when the thing has the given properties</returns>
public static bool IsThing(Thing thing, byte networkId, byte thingId) { /// <summary>
if (thing == null) /// Function used to process binary data received for this thing
return false; /// </summary>
return (thing.networkId == networkId) && (thing.id == thingId); /// <param name="bytes">The binary data</param>
//return (thing.id == thingId); public virtual void ProcessBinary(byte[] bytes) {
} }
} }

View File

@ -17,10 +17,10 @@ namespace RoboidControl {
/// <summary> /// <summary>
/// Create a distance sensor with the given ID /// Create a distance sensor with the given ID
/// </summary> /// </summary>
/// <param name="participant">The participant for with the sensor is needed</param> /// <param name="owner">The participant for with the sensor is needed</param>
/// <param name="networkId">The network ID of the sensor</param> /// <param name="networkId">The network ID of the sensor</param>
/// <param name="thingId">The ID of the thing</param> /// <param name="thingId">The ID of the thing</param>
public DistanceSensor(Participant participant, byte networkId, byte thingId) : base(participant, networkId, thingId, (byte)Type.TemperatureSensor) { public DistanceSensor(Participant owner, byte thingId) : base(owner, Type.TemperatureSensor, thingId) {
} }
#if UNITY_5_3_OR_NEWER #if UNITY_5_3_OR_NEWER

View File

@ -17,7 +17,7 @@ namespace RoboidControl {
/// <param name="participant">The participant for with the sensor is needed</param> /// <param name="participant">The participant for with the sensor is needed</param>
/// <param name="networkId">The network ID of the sensor</param> /// <param name="networkId">The network ID of the sensor</param>
/// <param name="thingId">The ID of the thing</param> /// <param name="thingId">The ID of the thing</param>
public TemperatureSensor(Participant participant, byte networkId, byte thingId) : base(participant, networkId, thingId, (byte)Type.TemperatureSensor) { } public TemperatureSensor(Participant participant, byte thingId) : base(participant, Type.TemperatureSensor, thingId) { }
/// <summary> /// <summary>
/// Function to extract the temperature received in the binary message /// Function to extract the temperature received in the binary message