Compare commits
No commits in common. "main" and "5a4daea34a78454cb64a8455409465a9358a97b1" have entirely different histories.
main
...
5a4daea34a
@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 4b081f36574e30442818c4087c3324c1
|
|
||||||
folderAsset: yes
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@ -1,7 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: f7566f52e6a505b439792756759e12e5
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@ -1,7 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 083f532f0c28cc648816ade951aa9d1e
|
|
||||||
TextScriptImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@ -1,7 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: d8fca5c59f506a347804140dd15492fb
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@ -3,11 +3,8 @@
|
|||||||
Roboid Control support for C# applications.
|
Roboid Control support for C# applications.
|
||||||
Includes support for the Unity game engine.
|
Includes support for the Unity game engine.
|
||||||
|
|
||||||
# Documentation
|
|
||||||
|
|
||||||
The documentation for Roboid Control for C# is found at https://docs.roboidcontrol.com/Csharp/
|
|
||||||
|
|
||||||
# Basic components
|
# Basic components
|
||||||
|
|
||||||
- RoboidControl::Thing
|
- RoboidControl::Thing
|
||||||
- RoboidControl::Participant
|
- RoboidControl::LocalParticipant
|
||||||
|
- RoboidControl::SiteServer
|
@ -9,7 +9,7 @@ namespace RoboidControl {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const byte Id = 0xB1;
|
public const byte Id = 0xB1;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The length of the message in bytes, excluding the binary data
|
/// The length of the message, excluding the binary data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// For the total size of the message this.bytes.Length should be added to this value.
|
/// For the total size of the message this.bytes.Length should be added to this value.
|
||||||
public const byte length = 4;
|
public const byte length = 4;
|
||||||
|
@ -9,7 +9,7 @@ namespace RoboidControl {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const byte Id = 0x20;
|
public const byte Id = 0x20;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The length of the message in bytres
|
/// The length of the message
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const byte length = 3;
|
public const byte length = 3;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -34,16 +34,13 @@ namespace RoboidControl {
|
|||||||
public DestroyMsg(byte[] buffer) : base(buffer) {
|
public DestroyMsg(byte[] buffer) : base(buffer) {
|
||||||
this.networkId = buffer[1];
|
this.networkId = buffer[1];
|
||||||
this.thingId = buffer[2];
|
this.thingId = buffer[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @copydoc Passer::RoboidControl::IMessage::Serialize
|
/// @copydoc Passer::RoboidControl::IMessage::Serialize
|
||||||
public override byte Serialize(ref byte[] buffer) {
|
public override byte Serialize(ref byte[] buffer) {
|
||||||
if (buffer.Length < DestroyMsg.length)
|
if (buffer.Length < DestroyMsg.length)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
System.Console.WriteLine($"Send DestroyMsg [{this.networkId}/{this.thingId}]");
|
|
||||||
#endif
|
|
||||||
byte ix = 0;
|
byte ix = 0;
|
||||||
buffer[ix++] = DestroyMsg.Id;
|
buffer[ix++] = DestroyMsg.Id;
|
||||||
buffer[ix++] = this.networkId;
|
buffer[ix++] = this.networkId;
|
||||||
|
@ -9,7 +9,7 @@ namespace RoboidControl {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const byte Id = 0x81;
|
public const byte Id = 0x81;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// /// The length of the message
|
/// The length of the message
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const byte length = 3;
|
public const byte length = 3;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -22,13 +22,13 @@ namespace RoboidControl {
|
|||||||
public byte thingId;
|
public byte thingId;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create an investigate message
|
/// Create a new message for sending
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="networkId">The network ID for the thing</param>
|
/// <param name="networkId">The network ID for the thing</param>
|
||||||
/// <param name="thing">The thing for which the details are requested</param>
|
/// <param name="thingId">The ID of the thing</param>
|
||||||
public InvestigateMsg(byte networkId, Thing thing) {
|
public InvestigateMsg(byte networkId, byte thingId) {
|
||||||
this.networkId = networkId;
|
this.networkId = networkId;
|
||||||
this.thingId = thing.id;
|
this.thingId = thingId;
|
||||||
}
|
}
|
||||||
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
|
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
|
||||||
public InvestigateMsg(byte[] buffer) : base(buffer) { }
|
public InvestigateMsg(byte[] buffer) : base(buffer) { }
|
||||||
|
@ -8,6 +8,7 @@ namespace RoboidControl {
|
|||||||
SendAngle8(buffer, ref ix, v.direction.horizontal);
|
SendAngle8(buffer, ref ix, v.direction.horizontal);
|
||||||
SendAngle8(buffer, ref ix, v.direction.vertical);
|
SendAngle8(buffer, ref ix, v.direction.vertical);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Spherical ReceiveSpherical(byte[] data, ref byte ix) {
|
public static Spherical ReceiveSpherical(byte[] data, ref byte ix) {
|
||||||
float distance = ReceiveFloat16(data, ref ix);
|
float distance = ReceiveFloat16(data, ref ix);
|
||||||
float horizontal = ReceiveAngle8(data, ref ix);
|
float horizontal = ReceiveAngle8(data, ref ix);
|
||||||
@ -16,6 +17,11 @@ namespace RoboidControl {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void SendQuat32(byte[] buffer, ref byte ix, SwingTwist s) {
|
||||||
|
Quat32 q32 = Quat32.FromSwingTwist(s);
|
||||||
|
SendQuat32(buffer, ref ix, q32);
|
||||||
|
|
||||||
|
}
|
||||||
public static void SendQuat32(byte[] buffer, ref byte ix, Quat32 q) {
|
public static void SendQuat32(byte[] buffer, ref byte ix, Quat32 q) {
|
||||||
int qx = (int)(q.x * 127 + 128);
|
int qx = (int)(q.x * 127 + 128);
|
||||||
int qy = (int)(q.y * 127 + 128);
|
int qy = (int)(q.y * 127 + 128);
|
||||||
@ -42,11 +48,6 @@ namespace RoboidControl {
|
|||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SendQuat32(byte[] buffer, ref byte ix, SwingTwist s) {
|
|
||||||
Quat32 q32 = Quat32.FromSwingTwist(s);
|
|
||||||
SendQuat32(buffer, ref ix, q32);
|
|
||||||
|
|
||||||
}
|
|
||||||
public static SwingTwist ReceiveSwingTwist(byte[] data, ref byte ix) {
|
public static SwingTwist ReceiveSwingTwist(byte[] data, ref byte ix) {
|
||||||
Quat32 q32 = ReceiveQuat32(data, ref ix);
|
Quat32 q32 = ReceiveQuat32(data, ref ix);
|
||||||
// UnityEngine.Quaternion q = new(q32.x, q32.y, q32.z, q32.w);
|
// UnityEngine.Quaternion q = new(q32.x, q32.y, q32.z, q32.w);
|
||||||
@ -65,6 +66,7 @@ namespace RoboidControl {
|
|||||||
sbyte value = (sbyte)(angle / 360.0f * 256.0f);
|
sbyte value = (sbyte)(angle / 360.0f * 256.0f);
|
||||||
buffer[ix++] = (byte)value;
|
buffer[ix++] = (byte)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float ReceiveAngle8(byte[] data, ref byte ix) {
|
public static float ReceiveAngle8(byte[] data, ref byte ix) {
|
||||||
float value = (data[ix++] * 180) / 128.0F;
|
float value = (data[ix++] * 180) / 128.0F;
|
||||||
return value;
|
return value;
|
||||||
@ -81,6 +83,7 @@ namespace RoboidControl {
|
|||||||
data[ix++] = (byte)((binary >> 8) & 0xFF);
|
data[ix++] = (byte)((binary >> 8) & 0xFF);
|
||||||
data[ix++] = (byte)(binary & 0xFF);
|
data[ix++] = (byte)(binary & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float ReceiveFloat16(byte[] data, ref byte ix) {
|
public static float ReceiveFloat16(byte[] data, ref byte ix) {
|
||||||
byte msb = data[ix++];
|
byte msb = data[ix++];
|
||||||
byte lsb = data[ix++];
|
byte lsb = data[ix++];
|
||||||
|
@ -11,6 +11,7 @@ namespace RoboidControl {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="buffer">The byte array to parse</param>
|
/// <param name="buffer">The byte array to parse</param>
|
||||||
public IMessage(byte[] buffer) {
|
public IMessage(byte[] buffer) {
|
||||||
|
//Deserialize(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
@ -40,6 +40,18 @@ namespace RoboidControl {
|
|||||||
this.urlLength = (byte)thing.modelUrl.Length;
|
this.urlLength = (byte)thing.modelUrl.Length;
|
||||||
this.url = thing.modelUrl;
|
this.url = thing.modelUrl;
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new message for sending
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="networkId">The network ID of the thing</param>
|
||||||
|
/// <param name="thingId">The ID of the thing</param>
|
||||||
|
/// <param name="url">The URL to send</param>
|
||||||
|
// public ModelUrlMsg(byte networkId, byte thingId, string url) {
|
||||||
|
// this.networkId = networkId;
|
||||||
|
// this.thingId = thingId;
|
||||||
|
// this.urlLength = (byte)url.Length;
|
||||||
|
// this.url = url;
|
||||||
|
// }
|
||||||
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
|
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
|
||||||
public ModelUrlMsg(byte[] buffer) {
|
public ModelUrlMsg(byte[] buffer) {
|
||||||
byte ix = 1;
|
byte ix = 1;
|
||||||
|
@ -66,8 +66,28 @@ namespace RoboidControl {
|
|||||||
/// Create a new message for sending
|
/// Create a new message for sending
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="networkId">The network ID of the thing</param>
|
/// <param name="networkId">The network ID of the thing</param>
|
||||||
/// <param name="thing">The thing for which the pose should be sent</param>
|
/// <param name="thingId">The ID of the thing</param>
|
||||||
/// <param name="force">If true, position and orientation are always included, even when they are not updated</param>
|
/// <param name="position">The position of the thing in local space in meters</param>
|
||||||
|
/// <param name="orientation">The orientation of the thing in local space</param>
|
||||||
|
public PoseMsg(byte networkId, byte thingId, Spherical position, SwingTwist orientation, Spherical linearVelocity = null, Spherical angularVelocity = null) {
|
||||||
|
this.networkId = networkId;
|
||||||
|
this.thingId = thingId;
|
||||||
|
|
||||||
|
this.poseType = 0;
|
||||||
|
if (this.position != null)
|
||||||
|
this.poseType |= Pose_Position;
|
||||||
|
if (this.orientation != null)
|
||||||
|
this.poseType |= Pose_Orientation;
|
||||||
|
if (this.linearVelocity != null)
|
||||||
|
this.poseType |= Pose_LinearVelocity;
|
||||||
|
if (this.angularVelocity != null)
|
||||||
|
this.poseType |= Pose_AngularVelocity;
|
||||||
|
|
||||||
|
this.position = position;
|
||||||
|
this.orientation = orientation;
|
||||||
|
this.linearVelocity = linearVelocity;
|
||||||
|
this.angularVelocity = angularVelocity;
|
||||||
|
}
|
||||||
public PoseMsg(byte networkId, Thing thing, bool force = false) {
|
public PoseMsg(byte networkId, Thing thing, bool force = false) {
|
||||||
this.networkId = networkId;
|
this.networkId = networkId;
|
||||||
this.thingId = thing.id;
|
this.thingId = thing.id;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
namespace RoboidControl {
|
namespace RoboidControl {
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Message providing generic details about a Thing
|
/// Message providing generic information about a Thing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ThingMsg : IMessage {
|
public class ThingMsg : IMessage {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -21,11 +21,11 @@ namespace RoboidControl {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public byte thingId;
|
public byte thingId;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The type of thing
|
/// The Thing.Type of the thing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte thingType;
|
public byte thingType;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ID of the parent thing in the hierarchy. This is zero for root things
|
/// The parent of the thing in the hierarachy. This is null for root Things
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte parentId;
|
public byte parentId;
|
||||||
|
|
||||||
@ -44,6 +44,19 @@ namespace RoboidControl {
|
|||||||
this.parentId = 0;
|
this.parentId = 0;
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// Create a message for sending
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="networkId">The network ID of the thing</param>
|
||||||
|
/// <param name="thingId">The ID of the thing</param>
|
||||||
|
/// <param name="thingType">The type of thing</param>
|
||||||
|
/// <param name="parentId">The parent of the thing</param>
|
||||||
|
public ThingMsg(byte networkId, byte thingId, byte thingType, byte parentId) {
|
||||||
|
this.networkId = networkId;
|
||||||
|
this.thingId = thingId;
|
||||||
|
this.thingType = thingType;
|
||||||
|
this.parentId = parentId;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
/// Create a message for receiving
|
/// Create a message for receiving
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="buffer">The byte array to parse</param>
|
/// <param name="buffer">The byte array to parse</param>
|
||||||
|
@ -11,6 +11,10 @@ namespace RoboidControl {
|
|||||||
/// It also maintains the communcation information to contact the participant.
|
/// It also maintains the communcation information to contact the participant.
|
||||||
/// It is used as a basis for the local participant, but also as a reference to remote participants.
|
/// It is used as a basis for the local participant, but also as a reference to remote participants.
|
||||||
public class Participant {
|
public class Participant {
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public Participant() { }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new participant with the given communcation info
|
/// Create a new participant with the given communcation info
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -31,21 +35,64 @@ namespace RoboidControl {
|
|||||||
public int port = 0;
|
public int port = 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// he network Id to identify the participant
|
/// The network ID of the participant
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte networkId = 0;
|
public byte networkId;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The things managed by this participant
|
/// The things managed by this participant
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly List<Thing> things = new();
|
public readonly List<Thing> things = new List<Thing>();
|
||||||
|
|
||||||
|
public virtual void Update(ulong currentTimeMS = 0) {
|
||||||
|
int n = this.things.Count;
|
||||||
|
for (int ix = 0; ix < n; ix++) {
|
||||||
|
Thing thing = this.things[ix];
|
||||||
|
if (thing != null)
|
||||||
|
thing.Update(currentTimeMS, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Participant> participants = new List<Participant>();
|
||||||
|
|
||||||
|
public static Participant GetParticipant(string ipAddress, int port) {
|
||||||
|
//Console.WriteLine($"Get Participant {ipAddress}:{port}");
|
||||||
|
foreach (Participant participant in Participant.participants) {
|
||||||
|
if (participant.ipAddress == ipAddress && participant.port == port)
|
||||||
|
return participant;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public static Participant GetParticipant(int participantId) {
|
||||||
|
//Console.WriteLine($"Get Participant [participantId]");
|
||||||
|
foreach (Participant participant in Participant.participants) {
|
||||||
|
if (participant.networkId == participantId)
|
||||||
|
return participant;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public static Participant AddParticipant(string ipAddress, int port) {
|
||||||
|
Console.WriteLine($"New Participant {ipAddress}:{port}");
|
||||||
|
Participant participant = new(ipAddress, port) {
|
||||||
|
networkId = (byte)(Participant.participants.Count + 1)
|
||||||
|
};
|
||||||
|
Participant.participants.Add(participant);
|
||||||
|
return participant;
|
||||||
|
}
|
||||||
|
public static void AddParticipant(Participant participant) {
|
||||||
|
Participant foundParticipant = Participant.GetParticipant(participant.networkId);
|
||||||
|
if (foundParticipant == null)
|
||||||
|
Participant.participants.Add(participant);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the thing with the given properties
|
/// Get a thing with the given ids
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <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>
|
||||||
/// <returns>The thing when it is found, null in other cases.</returns>
|
/// <returns>The thing when it is found, null in other cases.</returns>
|
||||||
public Thing Get(byte thingId) {
|
public Thing Get(byte networkId, byte thingId) {
|
||||||
Thing thing = things.Find(aThing => aThing.id == thingId);
|
Thing thing = things.Find(aThing => aThing.id == thingId);
|
||||||
//Thing thing = things.Find(aThing => Thing.IsThing(aThing, networkId, thingId));
|
//Thing thing = things.Find(aThing => Thing.IsThing(aThing, networkId, thingId));
|
||||||
// if (thing == null)
|
// if (thing == null)
|
||||||
@ -57,17 +104,30 @@ namespace RoboidControl {
|
|||||||
/// Add a new thing for this participant
|
/// Add a new thing for this participant
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="thing">The thing to add</param>
|
/// <param name="thing">The thing to add</param>
|
||||||
/// <param name="checkId">If true, the thing.id is regenerated if it is zero
|
/// <param name="invokeEvent">Invoke an notification event when the thing has been added</param>
|
||||||
public void Add(Thing thing, bool checkId = true) {
|
public void Add(Thing thing, bool checkId = true, bool invokeEvent = true) {
|
||||||
if (checkId && thing.id == 0) {
|
if (checkId && thing.id == 0) {
|
||||||
thing.id = (byte)(this.things.Count + 1);
|
thing.id = (byte)(this.things.Count + 1);
|
||||||
this.things.Add(thing);
|
this.things.Add(thing);
|
||||||
}
|
}
|
||||||
else {
|
// Console.WriteLine($"added thing [{thing.networkId}/{thing.id}]");
|
||||||
Thing foundThing = Get(thing.id);
|
Thing foundThing = Get(thing.networkId, thing.id);
|
||||||
if (foundThing == null)
|
|
||||||
this.things.Add(thing);
|
if (foundThing == null) {
|
||||||
|
this.things.Add(thing);
|
||||||
|
|
||||||
|
// if (invokeEvent)
|
||||||
|
// Thing.InvokeNewThing(thing);
|
||||||
|
// Console.Write($"Add thing {ipAddress}:{port}[{networkId}/{thing.id}]");
|
||||||
}
|
}
|
||||||
|
// else {
|
||||||
|
// if (thing != foundThing) {
|
||||||
|
// // should be: find first non-existing id...
|
||||||
|
// thing.id = (byte)this.things.Count;
|
||||||
|
// things.Add(thing);
|
||||||
|
// // Console.Write($"Add thing, updated thing id to [{thing.networkId}/{thing.id}]");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -78,77 +138,6 @@ namespace RoboidControl {
|
|||||||
this.things.Remove(thing);
|
this.things.Remove(thing);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <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) {
|
|
||||||
int n = this.things.Count;
|
|
||||||
for (int ix = 0; ix < n; ix++) {
|
|
||||||
Thing thing = this.things[ix];
|
|
||||||
if (thing != null)
|
|
||||||
thing.Update(currentTimeMS, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The collection of known participants.
|
|
||||||
/// </summary>
|
|
||||||
public readonly static List<Participant> participants = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieve a participant using ip address and port number
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ipAddress">The ip address of the participant</param>
|
|
||||||
/// <param name="port">The port number used to send messages to the participant</param>
|
|
||||||
/// <returns>The participant or null if it is not found.</returns>
|
|
||||||
public static Participant GetParticipant(string ipAddress, int port) {
|
|
||||||
//Console.WriteLine($"Get Participant {ipAddress}:{port}");
|
|
||||||
foreach (Participant participant in Participant.participants) {
|
|
||||||
if (participant.ipAddress == ipAddress && participant.port == port)
|
|
||||||
return participant;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieve a participant using a network ID
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="networkId">The network ID of the participant</param>
|
|
||||||
/// <returns>The participant or null if it is not found.</returns>
|
|
||||||
public static Participant GetParticipant(int networkId) {
|
|
||||||
//Console.WriteLine($"Get Participant [networkId]");
|
|
||||||
foreach (Participant participant in Participant.participants) {
|
|
||||||
if (participant.networkId == networkId)
|
|
||||||
return participant;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Add a new participant to the collection of participants
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ipAddress">The IP address of the participant</param>
|
|
||||||
/// <param name="port">The port used to send messages to this participant</param>
|
|
||||||
/// <returns>The added participant</returns>
|
|
||||||
public static Participant AddParticipant(string ipAddress, int port) {
|
|
||||||
Console.WriteLine($"New Participant {ipAddress}:{port}");
|
|
||||||
Participant participant = new(ipAddress, port) {
|
|
||||||
networkId = (byte)(Participant.participants.Count + 1)
|
|
||||||
};
|
|
||||||
Participant.participants.Add(participant);
|
|
||||||
return participant;
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Add a new participant to the collection of participants
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="participant">The participant to add</param>
|
|
||||||
/// <note>This function only adds the participant if it is not yet in the collection</note>
|
|
||||||
public static void AddParticipant(Participant participant) {
|
|
||||||
Participant foundParticipant = Participant.GetParticipant(participant.networkId);
|
|
||||||
if (foundParticipant == null)
|
|
||||||
Participant.participants.Add(participant);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -7,41 +7,40 @@ using System.Net.NetworkInformation;
|
|||||||
namespace RoboidControl {
|
namespace RoboidControl {
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A participant using UDP communication
|
/// A participant is used for communcation between things
|
||||||
/// </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 without connecting to a site
|
/// Create a participant with the give UDP port
|
||||||
/// </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 participant which will try to connect to a site.
|
/// 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>
|
||||||
/// <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;
|
||||||
@ -82,48 +81,16 @@ namespace RoboidControl {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ParticipantUDP isolatedParticipant = null;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Isolated participant is used when the application is run without networking
|
/// Create a participant using the given udp client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A participant without networking support</returns>
|
/// <param name="udpClient">UDP client to use for communication</param>
|
||||||
public static ParticipantUDP Isolated() {
|
/// <param name="port">The port number on which to communicate</param>
|
||||||
if (isolatedParticipant == null)
|
public ParticipantUDP(UdpClient udpClient, int port) : this() {
|
||||||
isolatedParticipant = new ParticipantUDP(0);
|
this.udpClient = udpClient;
|
||||||
return isolatedParticipant;
|
this.port = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
#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();
|
||||||
@ -143,6 +110,14 @@ 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;
|
||||||
@ -233,19 +208,9 @@ 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)
|
||||||
@ -257,6 +222,14 @@ 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)
|
||||||
@ -267,6 +240,24 @@ 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
|
||||||
@ -359,63 +350,62 @@ namespace RoboidControl {
|
|||||||
|
|
||||||
protected virtual void Process(Participant sender, InvestigateMsg msg) {
|
protected virtual void Process(Participant sender, InvestigateMsg msg) {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Console.WriteLine($"{this.name}: Process InvestigateMsg [{msg.networkId}/{msg.thingId}]");
|
Console.WriteLine($"Participant: 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($"{this.name}: Process ThingMsg [{msg.networkId}/{msg.thingId}] {msg.thingType} {msg.parentId}");
|
Console.WriteLine($"Participant: 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($"{this.name}: Process NameMsg [{msg.networkId}/{msg.thingId}] {msg.nameLength} {msg.name}");
|
Console.WriteLine($"Participant: Process NameMsg [{msg.networkId}/{msg.thingId}] {msg.nameLength} {msg.name}");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Thing thing = sender.Get(msg.thingId);
|
Thing thing = sender.Get(msg.networkId, msg.thingId);
|
||||||
if (thing != null)
|
if (thing != null)
|
||||||
thing.name = msg.name;
|
thing.name = msg.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Process(Participant sender, ModelUrlMsg msg) {
|
protected virtual void Process(Participant sender, ModelUrlMsg msg) {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Console.WriteLine($"{this.name}: Process ModelUrlMsg [{msg.networkId}/{msg.thingId}] {msg.urlLength} {msg.url}");
|
Console.WriteLine($"Participant: Process ModelUrlMsg [{msg.networkId}/{msg.thingId}] {msg.urlLength} {msg.url}");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Thing thing = sender.Get(msg.thingId);
|
Thing thing = sender.Get(msg.networkId, msg.thingId);
|
||||||
if (thing != null)
|
if (thing != null)
|
||||||
thing.modelUrl = msg.url;
|
thing.modelUrl = msg.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Process(Participant sender, PoseMsg msg) {
|
protected virtual void Process(Participant sender, PoseMsg msg) {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Console.WriteLine($"{this.name}: Process PoseMsg [{msg.networkId}/{msg.thingId}] {msg.poseType}");
|
Console.WriteLine($"Participant: Process PoseMsg [{msg.networkId}/{msg.thingId}] {msg.poseType}");
|
||||||
#endif
|
#endif
|
||||||
Participant owner = Participant.GetParticipant(msg.networkId);
|
Thing thing = sender.Get(msg.networkId, msg.thingId);
|
||||||
if (owner == null)
|
if (thing != null) {
|
||||||
return;
|
if ((msg.poseType & PoseMsg.Pose_Position) != 0)
|
||||||
|
thing.position = msg.position;
|
||||||
Thing thing = owner.Get(msg.thingId);
|
if ((msg.poseType & PoseMsg.Pose_Orientation) != 0)
|
||||||
if (thing == null)
|
thing.orientation = msg.orientation;
|
||||||
return;
|
if ((msg.poseType & PoseMsg.Pose_LinearVelocity) != 0) {
|
||||||
|
thing.linearVelocity = msg.linearVelocity;
|
||||||
if ((msg.poseType & PoseMsg.Pose_Position) != 0)
|
//Console.Write($"linear velocity = {thing.linearVelocity.ToVector3()}");
|
||||||
thing.position = msg.position;
|
}
|
||||||
if ((msg.poseType & PoseMsg.Pose_Orientation) != 0)
|
if ((msg.poseType & PoseMsg.Pose_AngularVelocity) != 0) {
|
||||||
thing.orientation = msg.orientation;
|
thing.angularVelocity = msg.angularVelocity;
|
||||||
if ((msg.poseType & PoseMsg.Pose_LinearVelocity) != 0)
|
//Console.Write($"angular velocity = {thing.angularVelocity.ToVector3()}");
|
||||||
thing.linearVelocity = msg.linearVelocity;
|
}
|
||||||
if ((msg.poseType & PoseMsg.Pose_AngularVelocity) != 0)
|
}
|
||||||
thing.angularVelocity = msg.angularVelocity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Process(Participant sender, BinaryMsg msg) {
|
protected virtual void Process(Participant sender, BinaryMsg msg) {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Console.WriteLine($"{this.name}: Process BinaryMsg [{msg.networkId}/{msg.thingId}] {msg.dataLength}");
|
Console.WriteLine($"Participant: Process BinaryMsg [{msg.networkId}/{msg.thingId}] {msg.dataLength}");
|
||||||
#endif
|
#endif
|
||||||
Thing thing = sender.Get(msg.thingId);
|
Thing thing = sender.Get(msg.networkId, msg.thingId);
|
||||||
thing?.ProcessBinary(msg.data);
|
thing?.ProcessBinary(msg.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,12 +420,10 @@ namespace RoboidControl {
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
Console.WriteLine($"Participant: Process Destroy Msg [{msg.networkId}/{msg.thingId}]");
|
Console.WriteLine($"Participant: Process Destroy Msg [{msg.networkId}/{msg.thingId}]");
|
||||||
#endif
|
#endif
|
||||||
Thing thing = sender.Get(msg.thingId);
|
Thing thing = sender.Get(msg.networkId, msg.thingId);
|
||||||
if (thing != null)
|
if (thing != null)
|
||||||
this.Remove(thing);
|
this.Remove(thing);
|
||||||
#if UNITY_5_3_OR_NEWER
|
|
||||||
thing.component.core = null;
|
thing.component.core = null;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ForwardMessage(IMessage msg) {
|
private void ForwardMessage(IMessage msg) {
|
||||||
|
@ -10,14 +10,13 @@ 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">The port of which to receive the messages</param>
|
/// <param name="port"></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);
|
||||||
|
|
||||||
// Determine local IP address
|
// Determine local IP address
|
||||||
IPAddress localIpAddress = null;
|
IPAddress localIpAddress = null;
|
||||||
@ -52,8 +51,6 @@ namespace RoboidControl {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Init
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Close the site
|
/// Close the site
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -108,20 +105,20 @@ namespace RoboidControl {
|
|||||||
protected override void Process(Participant sender, ThingMsg msg) {
|
protected override void Process(Participant sender, ThingMsg msg) {
|
||||||
Console.WriteLine($"SiteServer: Process thing [{msg.networkId}/{msg.thingId}] {msg.thingType} {msg.parentId} ");
|
Console.WriteLine($"SiteServer: Process thing [{msg.networkId}/{msg.thingId}] {msg.thingType} {msg.parentId} ");
|
||||||
|
|
||||||
Thing thing = sender.Get(msg.thingId);
|
Thing thing = sender.Get(msg.networkId, msg.thingId);
|
||||||
if (thing == null) {
|
if (thing == null) {
|
||||||
switch (msg.thingType) {
|
switch (msg.thingType) {
|
||||||
case (byte)Thing.Type.TouchSensor:
|
case (byte)Thing.Type.TouchSensor:
|
||||||
new TouchSensor(sender, msg.thingId);
|
new TouchSensor(sender, msg.networkId, msg.thingId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thing == null)
|
if (thing == null)
|
||||||
thing = new Thing(sender, msg.thingType, msg.thingId);
|
thing = new Thing(sender, msg.networkId, msg.thingId, msg.thingType);
|
||||||
|
|
||||||
if (msg.parentId != 0) {
|
if (msg.parentId != 0) {
|
||||||
thing.parent = sender.Get(msg.parentId);
|
thing.parent = sender.Get(msg.networkId, msg.parentId);
|
||||||
if (thing.parent == null)
|
if (thing.parent == null)
|
||||||
Console.WriteLine($"Could not find parent [{msg.networkId}/{msg.parentId}]");
|
Console.WriteLine($"Could not find parent [{msg.networkId}/{msg.parentId}]");
|
||||||
}
|
}
|
||||||
|
411
src/Thing.cs
411
src/Thing.cs
@ -10,84 +10,90 @@ namespace RoboidControl {
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class Thing {
|
public class Thing {
|
||||||
|
|
||||||
|
#region Types
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Predefined thing types
|
/// Predefined thing types
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Type {
|
public enum Type {
|
||||||
public const byte Undetermined = 0x00;
|
Undetermined,
|
||||||
// sensor
|
// Sensor
|
||||||
public const byte Switch = 0x01;
|
Switch,
|
||||||
public const byte DistanceSensor = 0x02;
|
DistanceSensor,
|
||||||
public const byte DirectionalSensor = 0x03;
|
DirectionalSensor,
|
||||||
public const byte TemperatureSensor = 0x04;
|
TemperatureSensor,
|
||||||
public const byte TouchSensor = 0x05;
|
TouchSensor,
|
||||||
// Motor
|
// Motor
|
||||||
public const byte ControlledMotor = 0x06;
|
ControlledMotor,
|
||||||
public const byte UncontrolledMotor = 0x07;
|
UncontrolledMotor,
|
||||||
public const byte Servo = 0x08;
|
Servo,
|
||||||
// Other
|
// Other
|
||||||
public const byte Roboid = 0x09;
|
Roboid,
|
||||||
public const byte HUmanoid = 0x0A;
|
Humanoid,
|
||||||
public const byte ExternalSensor = 0x0B;
|
ExternalSensor
|
||||||
public const byte Animator = 0x0C;
|
};
|
||||||
public const byte DifferentialDrive = 0x0D;
|
|
||||||
}
|
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 owning participant</param>
|
/// <param name="owner">The participant owning the thing</param>
|
||||||
/// <param name="thingType">The type of thing (can use \ref RoboidControl::Thing::Type "Thing.Type")</param>
|
/// <param name="thingType">The type of thing</param>
|
||||||
/// <param name="thingId">The ID of the thing, leave out or set to zero to generate an ID</param>
|
public Thing(Participant owner, byte thingType = (byte)Type.Undetermined, bool invokeEvent = true) {
|
||||||
/// <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 child thing
|
/// Create a new thing as a child of another thing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parent">The parent thing</param>
|
/// <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="thingType">The type of thing</param>
|
||||||
/// <param name="thingId">The ID of the thing, leave out or set to zero to generate an ID</param>
|
public Thing(Thing parent, byte thingType = (byte)Type.Undetermined, bool invokeEvent = true) : this(parent.owner, thingType, invokeEvent) {
|
||||||
/// <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;
|
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.
|
||||||
@ -102,8 +108,6 @@ namespace RoboidControl {
|
|||||||
|
|
||||||
#endregion Init
|
#endregion Init
|
||||||
|
|
||||||
public bool terminate = false;
|
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -111,6 +115,11 @@ 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;
|
||||||
@ -118,8 +127,105 @@ namespace RoboidControl {
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The type of this thing.
|
/// The type of this thing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// This can be either a \ref RoboidControl::Thing::Type "Thing.Type" or a byte value for custom types.
|
/// This can be either a Thing::Type (needs casting) or a byte value for custom types.
|
||||||
public byte type = Type.Undetermined;
|
public byte type = (byte)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>
|
||||||
@ -146,120 +252,6 @@ 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.
|
||||||
@ -277,7 +269,7 @@ namespace RoboidControl {
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when the pose has changed
|
/// Event triggered when the pose has changed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event ChangeHandler OnPoseChanged = delegate { };
|
public event ChangeHandler OnPoseChanged = null; //delegate { };
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Boolean indicating that the thing has an updated position
|
/// Boolean indicating that the thing has an updated position
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -348,36 +340,39 @@ namespace RoboidControl {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool angularVelocityUpdated = false;
|
public bool angularVelocityUpdated = false;
|
||||||
|
|
||||||
#endregion Pose
|
#if UNITY_5_3_OR_NEWER
|
||||||
|
|
||||||
#region Update
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the current time in milliseconds
|
/// A reference to the representation of the thing in Unity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The current time in milliseconds</returns>
|
[NonSerialized]
|
||||||
|
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);
|
||||||
#else
|
|
||||||
return TimeManager.GetCurrentTimeMilliseconds();
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update de state of the thing
|
/// Update de state of the thing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="recurse">When true, this will Update the descendants recursively</param>
|
/// <param name="recursively">When true, this will Update the descendants recursively</param>
|
||||||
public void Update(bool recurse = false) {
|
public void Update(bool recursively = false) {
|
||||||
Update(GetTimeMs(), recurse);
|
Update(TimeManager.GetCurrentTimeMilliseconds(), recursively);
|
||||||
}
|
}
|
||||||
// #endif
|
// #endif
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update this thing
|
/// Update this thing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="currentTime">he current clock time in milliseconds; if this is zero, the current time is retrieved automatically</param>
|
/// <param name="currentTime">The current time in milliseconds</param>
|
||||||
/// <param name="recurse">When true, this will Update the descendants recursively</param>
|
public virtual void Update(ulong currentTimeMs, bool recursively = false) {
|
||||||
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;
|
||||||
@ -387,19 +382,31 @@ namespace RoboidControl {
|
|||||||
//this.hierarchyChanged = false;
|
//this.hierarchyChanged = false;
|
||||||
|
|
||||||
// should recurse over children...
|
// should recurse over children...
|
||||||
if (recurse) {
|
if (recursively) {
|
||||||
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, recurse);
|
child.Update(currentTimeMs, recursively);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public delegate void ChangeHandler();
|
/// <summary>
|
||||||
public delegate void SphericalHandler(Spherical v);
|
/// Function used to generate binary data for this thing
|
||||||
public delegate void ThingHandler(Thing t);
|
/// </summary>
|
||||||
|
/// <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
|
||||||
@ -413,20 +420,18 @@ namespace RoboidControl {
|
|||||||
OnNewThing?.Invoke(thing);
|
OnNewThing?.Invoke(thing);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Update
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Function used to generate binary data for this thing
|
/// Check if the thing has the given properaties
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A byte array with the binary data</returns>
|
/// <param name="thing">The thing to check</param>
|
||||||
/// @sa Passer::RoboidControl::BinaryMsg
|
/// <param name="networkId">The network ID to compare to</param>
|
||||||
public virtual byte[] GenerateBinary() { return Array.Empty<byte>(); }
|
/// <param name="thingId">The thing ID to compare to</param>
|
||||||
|
/// <returns>True when the thing has the given properties</returns>
|
||||||
/// <summary>
|
public static bool IsThing(Thing thing, byte networkId, byte thingId) {
|
||||||
/// Function used to process binary data received for this thing
|
if (thing == null)
|
||||||
/// </summary>
|
return false;
|
||||||
/// <param name="bytes">The binary data to process</param>
|
return (thing.networkId == networkId) && (thing.id == thingId);
|
||||||
public virtual void ProcessBinary(byte[] bytes) {
|
//return (thing.id == thingId);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,14 @@
|
|||||||
using LinearAlgebra;
|
|
||||||
|
|
||||||
namespace RoboidControl {
|
namespace RoboidControl {
|
||||||
|
|
||||||
/// @brief A thing which can move itself using a differential drive system
|
/// @brief A thing which can move itself using a differential drive system
|
||||||
///
|
///
|
||||||
/// @sa @link https://en.wikipedia.org/wiki/Differential_wheeled_robot @endlink
|
/// @sa @link https://en.wikipedia.org/wiki/Differential_wheeled_robot @endlink
|
||||||
public class DifferentialDrive : Thing {
|
public class DifferentialDrive : Thing {
|
||||||
/// <summary>
|
/// @brief Create a differential drive without networking support
|
||||||
/// Create a differential drive without communication abilities
|
public DifferentialDrive() { }
|
||||||
/// </summary
|
/// @brief Create a differential drive with networking support
|
||||||
/// <param name="invokeEvent">Invoke a OnNewThing event when the thing has been created</param>
|
/// @param participant The local participant
|
||||||
public DifferentialDrive(bool invokeEvent = true) : base(Type.DifferentialDrive, invokeEvent) { }
|
public DifferentialDrive(ParticipantUDP participant) : base(participant, Type.Undetermined) { }
|
||||||
/// <summary>
|
|
||||||
/// Create a differential drive for a participant
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="owner">The owning participant</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 DifferentialDrive(ParticipantUDP participant, byte thingId = 0, bool invokeEvent = true) : base(participant, Type.DifferentialDrive, thingId, invokeEvent) { }
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new child differential drive
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="parent">The parent thing</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 DifferentialDrive(Thing parent, byte thingId = 0, bool invokeEvent = true) : base(parent, Type.DifferentialDrive, thingId, invokeEvent) { }
|
|
||||||
|
|
||||||
/// @brief Configures the dimensions of the drive
|
/// @brief Configures the dimensions of the drive
|
||||||
/// @param wheelDiameter The diameter of the wheels in meters
|
/// @param wheelDiameter The diameter of the wheels in meters
|
||||||
@ -33,63 +17,21 @@ namespace RoboidControl {
|
|||||||
/// These values are used to compute the desired wheel speed from the set
|
/// These values are used to compute the desired wheel speed from the set
|
||||||
/// linear and angular velocity.
|
/// linear and angular velocity.
|
||||||
/// @sa SetLinearVelocity SetAngularVelocity
|
/// @sa SetLinearVelocity SetAngularVelocity
|
||||||
public void SetDriveDimensions(float wheelDiameter, float wheelSeparation) {
|
public void SetDriveDimensions(float wheelDiameter, float wheelSeparation) { }
|
||||||
this.wheelRadius = wheelDiameter > 0 ? wheelDiameter / 2 : -wheelDiameter / 2;
|
|
||||||
this.wheelSeparation = wheelSeparation > 0 ? wheelSeparation : -wheelSeparation;
|
|
||||||
this.rpsToMs = wheelDiameter * Angle.pi;
|
|
||||||
|
|
||||||
float distance = this.wheelSeparation / 2;
|
|
||||||
if (this.leftWheel != null)
|
|
||||||
this.leftWheel.position = new Spherical(distance, Direction.left);
|
|
||||||
if (this.rightWheel != null)
|
|
||||||
this.rightWheel.position = new Spherical(distance, Direction.right);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Congures the motors for the wheels
|
/// @brief Congures the motors for the wheels
|
||||||
/// @param leftWheel The motor for the left wheel
|
/// @param leftWheel The motor for the left wheel
|
||||||
/// @param rightWheel The motor for the right wheel
|
/// @param rightWheel The motor for the right wheel
|
||||||
public void SetMotors(Thing leftWheel, Thing rightWheel) {
|
public void SetMotors(Thing leftWheel, Thing rightWheel) { }
|
||||||
float distance = this.wheelSeparation / 2;
|
|
||||||
|
|
||||||
this.leftWheel = leftWheel;
|
|
||||||
if (this.leftWheel != null)
|
|
||||||
this.leftWheel.position = new Spherical(distance, Direction.left);
|
|
||||||
|
|
||||||
this.rightWheel = rightWheel;
|
|
||||||
if (this.rightWheel != null)
|
|
||||||
this.rightWheel.position = new Spherical(distance, Direction.right);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Directly specify the speeds of the motors
|
/// @brief Directly specify the speeds of the motors
|
||||||
/// @param speedLeft The speed of the left wheel in degrees per second.
|
/// @param speedLeft The speed of the left wheel in degrees per second.
|
||||||
/// Positive moves the robot in the forward direction.
|
/// Positive moves the robot in the forward direction.
|
||||||
/// @param speedRight The speed of the right wheel in degrees per second.
|
/// @param speedRight The speed of the right wheel in degrees per second.
|
||||||
/// Positive moves the robot in the forward direction.
|
/// Positive moves the robot in the forward direction.
|
||||||
public void SetWheelVelocity(float speedLeft, float speedRight) {
|
public void SetWheelVelocity(float speedLeft, float speedRight) { }
|
||||||
if (this.leftWheel != null)
|
|
||||||
this.leftWheel.angularVelocity = new Spherical(speedLeft, Direction.left);
|
|
||||||
if (this.rightWheel != null)
|
|
||||||
this.rightWheel.angularVelocity = new Spherical(speedRight, Direction.right);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @copydoc RoboidControl::Thing::Update(unsigned long)
|
/// @copydoc RoboidControl::Thing::Update(unsigned long)
|
||||||
public override void Update(ulong currentMs, bool recursive = true) {
|
public override void Update(ulong currentMs, bool recursive = true) { }
|
||||||
if (this.linearVelocityUpdated) {
|
|
||||||
// this assumes forward velocity only....
|
|
||||||
float linearVelocity = this.linearVelocity.distance;
|
|
||||||
|
|
||||||
Spherical angularVelocity = this.angularVelocity;
|
|
||||||
float angularSpeed = angularVelocity.distance * Angle.Deg2Rad;
|
|
||||||
// Determine the rotation direction
|
|
||||||
if (angularVelocity.direction.horizontal < 0)
|
|
||||||
angularSpeed = -angularSpeed;
|
|
||||||
|
|
||||||
// wheel separation can be replaced by this.leftwheel.position.distance
|
|
||||||
float speedLeft = (linearVelocity + angularSpeed * this.wheelSeparation / 2) / this.wheelRadius * Angle.Rad2Deg;
|
|
||||||
float speedRight = (linearVelocity - angularSpeed * this.wheelSeparation / 2) / this.wheelRadius * Angle.Rad2Deg;
|
|
||||||
this.SetWheelVelocity(speedLeft, speedRight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief The radius of a wheel in meters
|
/// @brief The radius of a wheel in meters
|
||||||
protected float wheelRadius = 1.0f;
|
protected float wheelRadius = 1.0f;
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace RoboidControl {
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A sensor which can detect touches
|
|
||||||
/// </summary>
|
|
||||||
public class DigitalSensor : Thing {
|
|
||||||
/// <summary>
|
|
||||||
/// Create a digital sensor without communication abilities
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="invokeEvent">Invoke a OnNewThing event when the thing has been created</param>
|
|
||||||
public DigitalSensor(bool invokeEvent = true) : base(Type.Switch, invokeEvent) { }
|
|
||||||
/// <summary>
|
|
||||||
/// Create a digital sensor for a participant
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="owner">The owning participant</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 DigitalSensor(Participant owner, byte thingId = 0, bool invokeEvent = true) : base(owner, Type.Switch, thingId, invokeEvent) { }
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new child digital sensor
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="parent">The parent thing</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 DigitalSensor(Thing parent, byte thingId = 0, bool invokeEvent = true) : base(parent, Type.Switch, thingId, invokeEvent) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Value which is true when the sensor is touching something, false otherwise
|
|
||||||
/// </summary>
|
|
||||||
private bool _state = false;
|
|
||||||
public bool state {
|
|
||||||
get { return _state; }
|
|
||||||
set {
|
|
||||||
if (_state != value) {
|
|
||||||
_state = value;
|
|
||||||
}
|
|
||||||
stateUpdated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private bool stateUpdated = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Function used to generate binary data for this digital sensor
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A byte array with the binary data</returns>
|
|
||||||
/// <remark>The byte array will be empty when the digital status has not changed</remark>
|
|
||||||
public override byte[] GenerateBinary() {
|
|
||||||
if (!stateUpdated)
|
|
||||||
return Array.Empty<byte>();
|
|
||||||
|
|
||||||
byte[] bytes = new byte[1];
|
|
||||||
bytes[0] = (byte)(state ? 1 : 0);
|
|
||||||
stateUpdated = false;
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Function used to process binary data received for this digital sensor
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="bytes">The binary data to process</param>
|
|
||||||
public override void ProcessBinary(byte[] bytes) {
|
|
||||||
this.state |= (bytes[0] == 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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="owner">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 DistanceSensor(Participant owner, byte thingId) : base(owner, Type.TemperatureSensor, thingId) {
|
public DistanceSensor(Participant participant, byte networkId, byte thingId) : base(participant, networkId, thingId, (byte)Type.TemperatureSensor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#if UNITY_5_3_OR_NEWER
|
#if UNITY_5_3_OR_NEWER
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
//using System;
|
||||||
|
|
||||||
namespace RoboidControl {
|
namespace RoboidControl {
|
||||||
|
|
||||||
@ -6,50 +6,27 @@ namespace RoboidControl {
|
|||||||
/// A temperature sensor
|
/// A temperature sensor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TemperatureSensor : Thing {
|
public class TemperatureSensor : Thing {
|
||||||
/// <summary>
|
|
||||||
/// Create a temperature sensor without communication abilities
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="invokeEvent">Invoke a OnNewThing event when the thing has been created</param>
|
|
||||||
public TemperatureSensor(bool invokeEvent = true) : base(Type.TemperatureSensor, invokeEvent) { }
|
|
||||||
/// <summary>
|
|
||||||
/// Create a temperature sensor for a participant
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="owner">The participant for with the sensor is needed</param>
|
|
||||||
/// <param name="thingId">The ID of the thing</param>
|
|
||||||
/// <param name="invokeEvent">Invoke a OnNewThing event when the thing has been created</param>
|
|
||||||
public TemperatureSensor(Participant owner, byte thingId = 0, bool invokeEvent = true) : base(owner, Type.TemperatureSensor, thingId, invokeEvent) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new child temperature sensor
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="parent">The parent thing</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 TemperatureSensor(Thing parent, byte thingId = 0, bool invokeEvent = true) : base(parent, Type.TemperatureSensor, thingId, invokeEvent) { }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The measured temperature
|
/// The measured temperature
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float temperature = 0;
|
public float temperature = 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Function used to generate binary data for this temperature sensor
|
/// Create a temperature sensor with the given ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A byte array with the binary data</returns>
|
/// <param name="participant">The participant for with the sensor is needed</param>
|
||||||
public override byte[] GenerateBinary() {
|
/// <param name="networkId">The network ID of the sensor</param>
|
||||||
byte[] bytes = new byte[2];
|
/// <param name="thingId">The ID of the thing</param>
|
||||||
byte ix = 0;
|
public TemperatureSensor(Participant participant, byte networkId, byte thingId) : base(participant, networkId, thingId, (byte)Type.TemperatureSensor) { }
|
||||||
LowLevelMessages.SendFloat16(bytes, ref ix, this.temperature);
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Function to process the temperature from the binary data
|
/// Function to extract the temperature received in the binary message
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bytes">The binary data to process</param>
|
/// <param name="bytes">The byte array</param>
|
||||||
public override void ProcessBinary(byte[] bytes) {
|
public override void ProcessBinary(byte[] bytes) {
|
||||||
byte ix = 0;
|
byte ix = 0;
|
||||||
this.temperature = LowLevelMessages.ReceiveFloat16(bytes, ref ix);
|
this.temperature = LowLevelMessages.ReceiveFloat16(bytes, ref ix);
|
||||||
|
//Console.WriteLine($"temperature {this.name} = {this.temperature} C");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,29 +6,32 @@ namespace RoboidControl {
|
|||||||
/// A sensor which can detect touches
|
/// A sensor which can detect touches
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TouchSensor : Thing {
|
public class TouchSensor : Thing {
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a touch sensor without communication abilities
|
/// Create a touch sensor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="invokeEvent">Invoke a OnNewThing event when the thing has been created</param>
|
/// <param name="owner">The participant for with the sensor is needed</param>
|
||||||
public TouchSensor(bool invokeEvent = true) : base(Type.TouchSensor, invokeEvent) { }
|
/// <param name="invokeEvent">True when the creation should trigger an event</param>
|
||||||
/// <summary>
|
public TouchSensor(Participant owner) : base(owner, Type.TouchSensor) {
|
||||||
/// Create a touch sensor for a participant
|
Console.Write("TouchSensor constructor");
|
||||||
/// </summary>
|
//touchedSomething = false;
|
||||||
/// <param name="owner">The owning participant</param>
|
//thisParticipant = owner;
|
||||||
/// <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 TouchSensor(Participant owner, byte thingId = 0, bool invokeEvent = true) : base(owner, Type.TouchSensor, thingId, invokeEvent) { }
|
public TouchSensor(Participant owner, byte networkId, byte thingId) : base(owner, networkId, thingId) {
|
||||||
/// <summary>
|
// Console.Write("TouchSensor constructor");
|
||||||
/// Create a new child touch sensor
|
//touchedSomething = false;
|
||||||
/// </summary>
|
//thisParticipant = participant;
|
||||||
/// <param name="parent">The parent thing</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 TouchSensor(Thing parent, bool invokeEvent = true) : base(parent, (byte)Type.TouchSensor, invokeEvent) { }
|
||||||
public TouchSensor(Thing parent, byte thingId = 0, bool invokeEvent = true) : base(parent, Type.TouchSensor, thingId, invokeEvent) { }
|
|
||||||
|
public ParticipantUDP thisParticipant;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Value which is true when the sensor is touching something, false otherwise
|
/// Value which is true when the sensor is touching something, false otherwise
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
//public bool touchedSomething = false;
|
||||||
private bool _touchedSomething = false;
|
private bool _touchedSomething = false;
|
||||||
public bool touchedSomething {
|
public bool touchedSomething {
|
||||||
get { return _touchedSomething; }
|
get { return _touchedSomething; }
|
||||||
@ -49,27 +52,14 @@ namespace RoboidControl {
|
|||||||
this.component.core = this;
|
this.component.core = this;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/// <summary>
|
|
||||||
/// Function used to generate binary data for this touch sensor
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A byte array with the binary data</returns>
|
|
||||||
/// <remark>The byte array will be empty when the touch status has not changed</remark>
|
|
||||||
public override byte[] GenerateBinary() {
|
public override byte[] GenerateBinary() {
|
||||||
if (!touchUpdated)
|
if (!touchUpdated)
|
||||||
return Array.Empty<byte>();
|
return new byte[0];
|
||||||
|
|
||||||
byte[] bytes = new byte[1];
|
byte[] buffer = new byte[1];
|
||||||
bytes[0] = (byte)(touchedSomething ? 1 : 0);
|
buffer[0] = (byte)(touchedSomething ? 1 : 0);
|
||||||
touchUpdated = false;
|
touchUpdated = false;
|
||||||
return bytes;
|
return buffer;
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Function used to process binary data received for this touch sensor
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="bytes">The binary data to process</param>
|
|
||||||
public override void ProcessBinary(byte[] bytes) {
|
|
||||||
this.touchedSomething |= (bytes[0] == 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -29,7 +29,7 @@ namespace RoboidControl.test {
|
|||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_SiteServer() {
|
public void Test_SiteServer() {
|
||||||
SiteServer siteServer = new();
|
SiteServer siteServer = new SiteServer(7681);
|
||||||
|
|
||||||
ulong milliseconds = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
ulong milliseconds = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
ulong startTime = milliseconds;
|
ulong startTime = milliseconds;
|
||||||
@ -45,8 +45,8 @@ namespace RoboidControl.test {
|
|||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_SiteParticipant() {
|
public void Test_SiteParticipant() {
|
||||||
SiteServer siteServer = new(7681);
|
SiteServer siteServer = new SiteServer(7681);
|
||||||
ParticipantUDP participant = new("127.0.0.1", 7681, 7682);
|
ParticipantUDP participant = new ParticipantUDP("127.0.0.1", 7681);
|
||||||
|
|
||||||
ulong milliseconds = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
ulong milliseconds = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
ulong startTime = milliseconds;
|
ulong startTime = milliseconds;
|
||||||
@ -58,14 +58,14 @@ namespace RoboidControl.test {
|
|||||||
milliseconds = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
milliseconds = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.That(participant.networkId, Is.EqualTo(2));
|
Assert.That(participant.networkId, Is.EqualTo(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_ThingMsg() {
|
public void Test_ThingMsg() {
|
||||||
SiteServer siteServer = new(7681);
|
SiteServer siteServer = new SiteServer(7681);
|
||||||
ParticipantUDP participant = new("127.0.0.1", 7681, 7682);
|
ParticipantUDP participant = new ParticipantUDP("127.0.0.1");
|
||||||
Thing thing = new(participant) {
|
Thing thing = new Thing(participant) {
|
||||||
name = "First Thing",
|
name = "First Thing",
|
||||||
modelUrl = "https://passer.life/extras/ant.jpg"
|
modelUrl = "https://passer.life/extras/ant.jpg"
|
||||||
};
|
};
|
||||||
@ -80,7 +80,7 @@ namespace RoboidControl.test {
|
|||||||
milliseconds = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
milliseconds = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.That(participant.networkId, Is.EqualTo(2));
|
Assert.That(participant.networkId, Is.EqualTo(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user