Completed core documentation

This commit is contained in:
Pascal Serrarens 2025-02-19 15:57:44 +01:00
parent a1d89ff1f7
commit 7b21331afb
23 changed files with 502 additions and 201 deletions

2
.gitignore vendored
View File

@ -4,4 +4,4 @@ test/bin
test/obj
/bin
/obj
*.meta
**.meta

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 7f964f406734cf74097d61697e5cafc9
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -47,10 +47,7 @@ namespace Passer.RoboidControl {
this.thingId = thing.id;
this.bytes = System.Array.Empty<byte>();
}
/// <summary>
/// Create the message for receiving
/// </summary>
/// <param name="buffer">The byte array to parse</param>
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
public BinaryMsg(byte[] buffer) {
byte ix = 1;
this.networkId = buffer[ix++];
@ -61,13 +58,9 @@ namespace Passer.RoboidControl {
this.bytes[bytesIx] = buffer[ix++];
}
/// <summary>
/// Serialize the message into a byte array for sending
/// </summary>
/// <param name="buffer">The buffer to serilize into</param>
/// <returns>The length of the message in the buffer</returns>
/// @copydoc Passer::RoboidControl::IMessage::Serialize
public override byte Serialize(ref byte[] buffer) {
if (buffer.Length < BinaryMsg.length + buffer.Length || bytes.Length == 0)
if (buffer.Length < BinaryMsg.length + this.bytes.Length || this.bytes.Length == 0)
return 0;
byte ix = 0;

View File

@ -3,29 +3,52 @@ using System.Threading.Tasks;
namespace Passer.RoboidControl {
/// <summary>
/// Message notifiying that a Thing no longer exists
/// </summary>
public class DestroyMsg : IMessage {
/// <summary>
/// The message ID
/// </summary>
public const byte Id = 0x20;
/// <summary>
/// The length of the message
/// </summary>
public const byte length = 3;
/// <summary>
/// The network ID of the thing
/// </summary>
public byte networkId;
/// <summary>
/// The ID of the thing
/// </summary>
public byte thingId;
/// <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>
public DestroyMsg(byte networkId, byte thingId) {
this.networkId = networkId;
this.thingId = thingId;
}
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
public DestroyMsg(byte[] buffer) : base(buffer) { }
public override void Deserialize(byte[] buffer) {
this.networkId = buffer[0];
this.thingId = buffer[1];
/// @copydoc Passer::RoboidControl::IMessage::Serialize
public override byte Serialize(ref byte[] buffer) {
if (buffer.Length < DestroyMsg.length)
return 0;
byte ix = 0;
buffer[ix++] = DestroyMsg.Id;
buffer[ix++] = this.networkId;
buffer[ix++] = this.thingId;
return ix;
}
public static async Task<bool> Receive(Stream dataStream, Participant client, byte packetSize) {
if (packetSize != length)
return false;
byte[] buffer = await Receive(dataStream, packetSize);
DestroyMsg msg = new DestroyMsg(buffer);
client.messageQueue.Enqueue(msg);
return true;
}
}
}

View File

@ -3,18 +3,40 @@ using System.Threading.Tasks;
namespace Passer.RoboidControl {
/// <summary>
/// Message to request details for a Thing
/// </summary>
public class InvestigateMsg : IMessage {
/// <summary>
/// The message Id
/// </summary>
public const byte Id = 0x81;
/// <summary>
/// The length of the message
/// </summary>
public const byte length = 3;
/// <summary>
/// The network ID of the thing
/// </summary>
public byte networkId;
/// <summary>
/// The ID of the thing
/// </summary>
public byte thingId;
/// <summary>
/// Create a new message for sending
/// </summary>
/// <param name="networkId">The network ID for the thing</param>
/// <param name="thingId">The ID of the thing</param>
public InvestigateMsg(byte networkId, byte thingId) {
this.networkId = networkId;
this.thingId = thingId;
}
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
public InvestigateMsg(byte[] buffer) : base(buffer) { }
/// @copydoc Passer::RoboidControl::IMessage::Serialize
public override byte Serialize(ref byte[] buffer) {
byte ix = 0;
buffer[ix++] = InvestigateMsg.Id;
@ -22,28 +44,28 @@ namespace Passer.RoboidControl {
buffer[ix++] = this.thingId;
return ix;
}
public override void Deserialize(byte[] buffer) {
uint ix = 0;
this.networkId = buffer[ix++];
this.thingId = buffer[ix++];
}
// public override void Deserialize(byte[] buffer) {
// uint ix = 0;
// this.networkId = buffer[ix++];
// this.thingId = buffer[ix++];
// }
//public static bool Send(Participant client, CoreThing thing) {
// InvestigateMsg msg = new(thing.networkId, thing.id);
// return SendMsg(client, msg);
//}
public static async Task<bool> Receive(Stream dataStream, Participant client, byte packetSize) {
if (packetSize != length)
return false;
// public static async Task<bool> Receive(Stream dataStream, Participant client, byte packetSize) {
// if (packetSize != length)
// return false;
byte[] buffer = await Receive(dataStream, packetSize);
InvestigateMsg msg = new InvestigateMsg(buffer);
//UnityEngine.Debug.Log($"Receive investigate [{msg.networkId}/{msg.thingId}]");
// byte[] buffer = await Receive(dataStream, packetSize);
// InvestigateMsg msg = new InvestigateMsg(buffer);
// //UnityEngine.Debug.Log($"Receive investigate [{msg.networkId}/{msg.thingId}]");
client.messageQueue.Enqueue(msg);
return true;
// client.messageQueue.Enqueue(msg);
// return true;
}
// }
}
}

View File

@ -3,40 +3,54 @@ using System.IO;
namespace Passer.RoboidControl {
/// <summary>
/// Root structure for all communcation messages
/// </summary>
public class IMessage {
public IMessage() { }
/// <summary>
/// Create a message for receiving
/// </summary>
/// <param name="buffer">The byte array to parse</param>
public IMessage(byte[] buffer) {
Deserialize(buffer);
//Deserialize(buffer);
}
/// <summary>
/// Serialize the message into a byte array for sending
/// </summary>
/// <param name="buffer">The buffer to serilize into</param>
/// <returns>The length of the message in the buffer</returns>
public virtual byte Serialize(ref byte[] buffer) { return 0; }
public virtual void Deserialize(byte[] buffer) { }
public bool SendTo(Participant client) {
Serialize(ref client.buffer);
return client.SendBuffer(client.buffer.Length);
}
//public virtual void Deserialize(byte[] buffer) { }
public static bool SendMsg(Participant client, IMessage msg) {
msg.Serialize(ref client.buffer);
return client.SendBuffer(client.buffer.Length);
}
// public bool SendTo(Participant client) {
// Serialize(ref client.buffer);
// return client.SendBuffer(client.buffer.Length);
// }
public static bool PublishMsg(Participant client, IMessage msg) {
msg.Serialize(ref client.buffer);
return client.PublishBuffer(client.buffer.Length);
}
// public static bool SendMsg(Participant client, IMessage msg) {
// msg.Serialize(ref client.buffer);
// return client.SendBuffer(client.buffer.Length);
// }
public static async Task<byte[]> Receive(Stream dataStream, byte packetSize) {
byte[] buffer = new byte[packetSize - 1]; // without msgId
int byteCount = dataStream.Read(buffer, 0, packetSize - 1);
while (byteCount < packetSize - 1) {
// not all bytes have been read, wait and try again
await Task.Delay(1);
byteCount += dataStream.Read(buffer, byteCount, packetSize - 1 - byteCount);
}
return buffer;
}
// public static bool PublishMsg(Participant client, IMessage msg) {
// msg.Serialize(ref client.buffer);
// return client.PublishBuffer(client.buffer.Length);
// }
// public static async Task<byte[]> Receive(Stream dataStream, byte packetSize) {
// byte[] buffer = new byte[packetSize - 1]; // without msgId
// int byteCount = dataStream.Read(buffer, 0, packetSize - 1);
// while (byteCount < packetSize - 1) {
// // not all bytes have been read, wait and try again
// await Task.Delay(1);
// byteCount += dataStream.Read(buffer, byteCount, packetSize - 1 - byteCount);
// }
// return buffer;
// }
}
}

View File

@ -1,23 +1,56 @@
namespace Passer.RoboidControl {
/// <summary>
/// Message for communicating the name of a thing
/// </summary>
public class NameMsg : IMessage {
/// <summary>
/// The message ID
/// </summary>
public const byte Id = 0x91; // 145
/// <summary>
/// The length of the message
/// </summary>
public const byte length = 4;
/// <summary>
/// The network ID of the thing
/// </summary>
public byte networkId;
/// <summary>
/// The ID of the thing
/// </summary>
public byte thingId;
/// <summary>
/// The length of the name, excluding null terminator
/// </summary>
public byte len;
/// <summary>
/// The name of the thing, not terminated with a null character
/// </summary>
public string name = "";
/// <summary>
/// Create a new message for sending
/// </summary>
/// <param name="networkId">The network ID of the thing</param>
/// <param name="thing">The thing</param>
public NameMsg(byte networkId, Thing thing) {
this.networkId = networkId;
this.thingId = thing.id;
this.name = thing.name;
}
/// <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="name">The name of the thing</param>
public NameMsg(byte networkId, byte thingId, string name) {
this.networkId = networkId;
this.thingId = thingId;
this.name = name;
}
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
public NameMsg(byte[] buffer) {
byte ix = 1;
this.networkId = buffer[ix++];
@ -26,8 +59,9 @@ namespace Passer.RoboidControl {
this.name = System.Text.Encoding.UTF8.GetString(buffer, (int)ix, strlen);
}
/// @copydoc Passer::RoboidControl::IMessage::Serialize
public override byte Serialize(ref byte[] buffer) {
if (this.name.Length == 0)
if (buffer.Length < NameMsg.length + this.name.Length || this.name.Length == 0)
return 0;
byte ix = 0;
@ -40,6 +74,7 @@ namespace Passer.RoboidControl {
buffer[ix++] = (byte)nameLength;
for (int nameIx = 0; nameIx < nameLength; nameIx++)
buffer[ix++] = (byte)this.name[nameIx];
return ix;
}
}

View File

@ -1,18 +1,39 @@
namespace Passer.RoboidControl {
/// <summary>
/// A message communicating the network ID for that participant
/// </summary>
public class NetworkIdMsg : IMessage {
/// <summary>
/// The message ID
/// </summary>
public const byte Id = 0xA1;
/// <summary>
/// The length of the message
/// </summary>
public const byte length = 2;
/// <summary>
/// The network ID for the participant
/// </summary>
public byte networkId;
/// <summary>
/// Create a new message for sending
/// </summary>
/// <param name="networkId">The network ID for the participant</param>
public NetworkIdMsg(byte networkId) {
this.networkId = networkId;
}
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
public NetworkIdMsg(byte[] buffer) {
this.networkId = buffer[1];
}
/// @copydoc Passer::RoboidControl::IMessage::Serialize
public override byte Serialize(ref byte[] buffer) {
if (buffer.Length < NetworkIdMsg.length)
return 0;
buffer[0] = NetworkIdMsg.Id;
buffer[1] = this.networkId;
return NetworkIdMsg.length;

View File

@ -4,23 +4,73 @@ using Passer.LinearAlgebra;
namespace Passer.RoboidControl {
/// <summary>
/// Message to communicate the pose of the thing
/// </summary>
/// The pose is in local space relative to the parent. If there is not parent (the thing is a root thing), the pose will be in world space.
public class PoseMsg : IMessage {
/// <summary>
/// The message ID
/// </summary>
public const byte Id = 0x10;
/// <summary>
/// The length of the message
/// </summary>
public const byte length = 4 + 4 + 4;
/// <summary>
/// The network ID of the thing
/// </summary>
public byte networkId;
/// <summary>
/// The ID of the thing
/// </summary>
public byte thingId;
/// <summary>
/// bitpattern stating which pose components are available
/// </summary>
public byte poseType;
/// <summary>
/// Bit pattern for a pose with position
/// </summary>
public const byte Pose_Position = 0x01;
/// <summary>
/// Bit pattern for a pose with orientation
/// </summary>
public const byte Pose_Orientation = 0x02;
/// <summary>
/// Bit pattern for a pose with linear velocity
/// </summary>
public const byte Pose_LinearVelocity = 0x04;
/// <summary>
/// Bit pattern for a pose with angular velocity
/// </summary>
public const byte Pose_AngularVelocity = 0x08;
/// <summary>
/// The position of the thing in local space in meters
/// </summary>
public Spherical position = Spherical.zero;
/// <summary>
/// The orientation of the thing in local space
/// </summary>
public SwingTwist orientation = SwingTwist.zero;
/// <summary>
/// The linear velocity of the thing in local space in meters per second
/// </summary>
public Spherical linearVelocity = Spherical.zero;
/// <summary>
/// The angular veloicty of the thing in local space
/// </summary>
public Spherical angularVelocity = Spherical.zero;
/// <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="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) {
this.networkId = networkId;
this.thingId = thingId;
@ -34,8 +84,10 @@ namespace Passer.RoboidControl {
if (this.orientation != null)
this.poseType |= Pose_Orientation;
}
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
public PoseMsg(byte[] buffer) : base(buffer) { }
/// @copydoc Passer::RoboidControl::IMessage::Serialize
public override byte Serialize(ref byte[] buffer) {
byte ix = 0;
buffer[ix++] = PoseMsg.Id;
@ -53,59 +105,59 @@ namespace Passer.RoboidControl {
LowLevelMessages.SendSpherical(buffer, ref ix, this.angularVelocity);
return ix;
}
public override void Deserialize(byte[] buffer) {
byte ix = 0;
this.networkId = buffer[ix++];
this.thingId = buffer[ix++];
this.poseType = buffer[ix++];
// public override void Deserialize(byte[] buffer) {
// byte ix = 0;
// this.networkId = buffer[ix++];
// this.thingId = buffer[ix++];
// this.poseType = buffer[ix++];
if ((poseType & Pose_Position) != 0)
this.position = LowLevelMessages.ReceiveSpherical(buffer, ref ix);
if ((poseType & Pose_Orientation) != 0)
this.orientation = LowLevelMessages.ReceiveSwingTwist(buffer, ref ix);
if ((poseType & Pose_LinearVelocity) != 0)
this.linearVelocity = LowLevelMessages.ReceiveSpherical(buffer, ref ix);
if ((poseType & Pose_AngularVelocity) != 0)
this.angularVelocity = LowLevelMessages.ReceiveSpherical(buffer, ref ix);
}
// if ((poseType & Pose_Position) != 0)
// this.position = LowLevelMessages.ReceiveSpherical(buffer, ref ix);
// if ((poseType & Pose_Orientation) != 0)
// this.orientation = LowLevelMessages.ReceiveSwingTwist(buffer, ref ix);
// if ((poseType & Pose_LinearVelocity) != 0)
// this.linearVelocity = LowLevelMessages.ReceiveSpherical(buffer, ref ix);
// if ((poseType & Pose_AngularVelocity) != 0)
// this.angularVelocity = LowLevelMessages.ReceiveSpherical(buffer, ref ix);
// }
public static bool Send(Participant client, byte thingId, Spherical position, SwingTwist orientation) {
PoseMsg msg = new PoseMsg(client.networkId, thingId, position, orientation);
return SendMsg(client, msg);
}
public static async Task<bool> Receive(Stream dataStream, Participant client, byte packetSize) {
byte[] buffer = await Receive(dataStream, packetSize);
PoseMsg msg = new PoseMsg(buffer);
// public static bool Send(Participant client, byte thingId, Spherical position, SwingTwist orientation) {
// PoseMsg msg = new PoseMsg(client.networkId, thingId, position, orientation);
// return SendMsg(client, msg);
// }
// public static async Task<bool> Receive(Stream dataStream, Participant client, byte packetSize) {
// byte[] buffer = await Receive(dataStream, packetSize);
// PoseMsg msg = new PoseMsg(buffer);
// Do no process poses with nwid == 0 (== local)
if (msg.networkId == 0)
return true;
// // Do no process poses with nwid == 0 (== local)
// if (msg.networkId == 0)
// return true;
client.messageQueue.Enqueue(msg);
return true;
}
// client.messageQueue.Enqueue(msg);
// return true;
// }
public static bool SendTo(Participant participant, Thing thing) {
if (participant == null || thing == null)
return false;
// public static bool SendTo(Participant participant, Thing thing) {
// if (participant == null || thing == null)
// return false;
byte ix = 0;
participant.buffer[ix++] = PoseMsg.Id;
participant.buffer[ix++] = participant.networkId;
participant.buffer[ix++] = thing.id;
participant.buffer[ix++] = thing.poseUpdated;
// byte ix = 0;
// participant.buffer[ix++] = PoseMsg.Id;
// participant.buffer[ix++] = participant.networkId;
// participant.buffer[ix++] = thing.id;
// participant.buffer[ix++] = thing.poseUpdated;
if ((thing.poseUpdated & Pose_Position) != 0 && thing.position != null)
LowLevelMessages.SendSpherical(participant.buffer, ref ix, thing.position);
if ((thing.poseUpdated & Pose_Orientation) != 0 && thing.orientation != null)
LowLevelMessages.SendQuat32(participant.buffer, ref ix, thing.orientation);
if ((thing.poseUpdated & Pose_LinearVelocity) != 0 && thing.linearVelocity != null)
LowLevelMessages.SendSpherical(participant.buffer, ref ix, thing.linearVelocity);
if ((thing.poseUpdated & Pose_AngularVelocity) != 0 && thing.angularVelocity != null)
LowLevelMessages.SendSpherical(participant.buffer, ref ix, thing.angularVelocity);
// if ((thing.poseUpdated & Pose_Position) != 0 && thing.position != null)
// LowLevelMessages.SendSpherical(participant.buffer, ref ix, thing.position);
// if ((thing.poseUpdated & Pose_Orientation) != 0 && thing.orientation != null)
// LowLevelMessages.SendQuat32(participant.buffer, ref ix, thing.orientation);
// if ((thing.poseUpdated & Pose_LinearVelocity) != 0 && thing.linearVelocity != null)
// LowLevelMessages.SendSpherical(participant.buffer, ref ix, thing.linearVelocity);
// if ((thing.poseUpdated & Pose_AngularVelocity) != 0 && thing.angularVelocity != null)
// LowLevelMessages.SendSpherical(participant.buffer, ref ix, thing.angularVelocity);
return participant.SendBuffer(ix);
}
// return participant.SendBuffer(ix);
// }
}
}

View File

@ -3,24 +3,59 @@ using System.Threading.Tasks;
namespace Passer.RoboidControl {
/// <summary>
/// Message for sending generic text.
/// </summary>
public class TextMsg : IMessage {
public TextMsg(byte[] buffer) : base(buffer) {}
/// <summary>
/// The message ID
/// </summary>
public const byte Id = 0xB0;
/// <summary>
/// The length of the message without the text itself
/// </summary>
public const byte length = 2;
/// <summary>
/// The text
/// </summary>
public string text = "";
public override void Deserialize(byte[] buffer) {
uint ix = 0;
uint strlen = buffer[ix++];
this.text = System.Text.Encoding.UTF8.GetString(buffer, (int)ix, (int)strlen);
/// <summary>
/// Create a new message for sending
/// </summary>
/// <param name="text">The text to send</param>
public TextMsg(string text) {
this.text = text;
}
public static async Task<bool> Receive(Stream dataStream, Participant client, byte packetSize) {
byte[] buffer = await Receive(dataStream, packetSize);
TextMsg msg = new TextMsg(buffer);
/// @copydoc Passer::RoboidControl::IMessage::IMessage(byte[] buffer)
public TextMsg(byte[] buffer) : base(buffer) { }
client.messageQueue.Enqueue(msg);
return true;
/// @copydoc Passer::RoboidControl::IMessage::Serialize
public override byte Serialize(ref byte[] buffer) {
if (buffer.Length < TextMsg.length + this.text.Length || this.text.Length == 0)
return 0;
byte ix = 0;
buffer[ix++] = TextMsg.Id;
buffer[ix++] = (byte)this.text.Length;
for (int textIx = 0; textIx < this.text.Length; textIx++)
buffer[ix++] = (byte)this.text[textIx];
return ix;
}
// public override void Deserialize(byte[] buffer) {
// uint ix = 0;
// uint strlen = buffer[ix++];
// this.text = System.Text.Encoding.UTF8.GetString(buffer, (int)ix, (int)strlen);
// }
// public static async Task<bool> Receive(Stream dataStream, Participant client, byte packetSize) {
// byte[] buffer = await Receive(dataStream, packetSize);
// TextMsg msg = new TextMsg(buffer);
// client.messageQueue.Enqueue(msg);
// return true;
// }
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 711bdb0248c9f6848a6b4da15cc05db5

View File

@ -318,7 +318,9 @@ namespace Passer.RoboidControl {
if (client == this)
continue;
//UnityEngine.Debug.Log($"---> {client.ipAddress}");
IMessage.SendMsg(client, msg);
//IMessage.SendMsg(client, msg);
msg.Serialize(ref client.buffer);
client.SendBuffer(client.buffer.Length);
}
}

View File

@ -1,3 +1,10 @@
\mainpage Control Core for C#
\mainpage Roboid Control for C#
Control Core contains generic functionality for Controlling Things.
Roboid Control support for C# applications.
Includes support for the Unity game engine.
# Basic components
- Passer::RoboidControl::Thing
- Passer::RoboidControl::Participant
- Passer::RoboidControl::SiteServer

View File

@ -1,4 +1,3 @@
#nullable enable
using System;
using System.Collections.Generic;

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: a5a7a42365df0d0459195576ad3bb50b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,22 +1,39 @@
using System;
namespace Passer.RoboidControl.Core {
namespace Passer.RoboidControl {
/// <summary>
/// A sensor measuring the distance in the forward direction
/// </summary>
public class DistanceSensor : Thing {
/// <summary>
/// The current measured distance
/// </summary>
public float distance = 0;
/// <summary>
/// Constructor for a new distance sensor
/// </summary>
/// <param name="participant">The participant for which the sensor is needed</param>
public DistanceSensor(RemoteParticipant participant) : base(participant, true) { }
/// <summary>
/// Create a distance sensor with the given ID
/// </summary>
/// <param name="participant">The participant for with the sensor is needed</param>
/// <param name="networkId">The network ID of the sensor</param>
/// <param name="thingId">The ID of the thing</param>
public DistanceSensor(RemoteParticipant participant, byte networkId, byte thingId) : base(participant, networkId, thingId, (byte)Type.TemperatureSensor) {
}
#if UNITY_5_3_OR_NEWER
/// @copydoc Passer::RoboidControl::Thing::CreateComponent
public override void CreateComponent() {
this.component = Unity.DistanceSensor.Create(this.parent);
this.component.core = this;
}
#endif
/// <summary>
/// Function to extract the distance received in the binary message
/// </summary>
/// <param name="bytes">The byte array</param>
public override void ProcessBinary(byte[] bytes) {
byte ix = 0;
this.distance = LowLevelMessages.ReceiveFloat16(bytes, ref ix);

View File

@ -1,16 +1,32 @@
using System;
//using System;
namespace Passer.RoboidControl.Core {
namespace Passer.RoboidControl {
/// <summary>
/// A temperature sensor
/// </summary>
public class TemperatureSensor : Thing {
public float temp = 0;
/// <summary>
/// The measured temperature
/// </summary>
public float temperature = 0;
/// <summary>
/// Create a temperature sensor with the given ID
/// </summary>
/// <param name="participant">The participant for with the sensor is needed</param>
/// <param name="networkId">The network ID of the sensor</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) {}
/// <summary>
/// Function to extract the temperature received in the binary message
/// </summary>
/// <param name="bytes">The byte array</param>
public override void ProcessBinary(byte[] bytes) {
byte ix = 0;
this.temp = LowLevelMessages.ReceiveFloat16(bytes, ref ix);
Console.WriteLine($"temperature {this.name} = {this.temp} C");
this.temperature = LowLevelMessages.ReceiveFloat16(bytes, ref ix);
//Console.WriteLine($"temperature {this.name} = {this.temperature} C");
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 5cd9e2534695ec844bd23b7a48ad498a

View File

@ -1,12 +1,25 @@
namespace Passer.RoboidControl {
/// <summary>
/// A sensor which can detect touches
/// </summary>
public class TouchSensor : Thing {
/// <summary>
/// Value which is true when the sensor is touching something, false otherwise
/// </summary>
public bool touchedSomething = false;
/// <summary>
/// Create a touch sensor
/// </summary>
/// <param name="participant">The participant for with the sensor is needed</param>
/// <param name="invokeEvent">True when the creation should trigger an event</param>
public TouchSensor(RemoteParticipant participant, bool invokeEvent = true) : base(participant, invokeEvent) {
touchedSomething = false;
}
#if UNITY_5_3_OR_NEWER
/// @copydoc Passer::RoboidControl::Thing::CreateComponent
public override void CreateComponent() {
this.component = Unity.TouchSensor.Create(this);
this.component.core = this;

155
Thing.cs
View File

@ -31,21 +31,41 @@ namespace Passer.RoboidControl {
Humanoid,
ExternalSensor
};
/// <summary>
/// The type of this thing. This can be either a Thing::Type (needs casting)
/// or a byte value for custom types.
/// </summary>
public byte type;
#endregion Types
#region Properties
/// <summary>
/// The participant to which this thing belongs
/// </summary>
public RemoteParticipant participant;
public delegate void ChangeHandler();
public delegate void SphericalHandler(Spherical v);
/// <summary>
/// The network ID of this thing.
/// </summary>
public byte networkId;
/// <summary>
/// The ID of this thing
/// </summary>
public byte id;
public event ChangeHandler OnParentChanged = delegate {};
/// <summary>
/// Event which is triggered when the parent changes
/// </summary>
public event ChangeHandler OnParentChanged = delegate { };
private Thing _parent;
/// <summary>
/// The parent of this thing
/// </summary>
public Thing parent {
get => _parent;
set {
@ -63,22 +83,38 @@ namespace Passer.RoboidControl {
}
}
/// <summary>
/// Attach a thing as a child of this thing
/// </summary>
/// <param name="child">The thing to attach as a child</param>
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"></param>
public void RemoveChild(Thing child) {
children.Remove(child);
}
[System.NonSerialized]
/// <summary>
/// The list of children of this thing
/// </summary>
[NonSerialized]
public List<Thing> children = new List<Thing>();
public byte type;
public event ChangeHandler OnNameChanged = delegate {};
/// <summary>
/// Event which is triggered when the name changes
/// </summary>
public event ChangeHandler OnNameChanged = delegate { };
private string _name = "";
/// <summary>
/// The name of the thing
/// </summary>
public virtual string name {
get => _name;
set {
@ -89,11 +125,19 @@ namespace Passer.RoboidControl {
}
}
/// <summary>
/// An URL pointing to the location where a model of the thing can be found
/// </summary>
public string modelUrl = "";
public byte poseUpdated = 0x00;
public event ChangeHandler OnPositionChanged = delegate {};
/// <summary>
/// Event triggered when the position has changed
/// </summary>
public event ChangeHandler OnPositionChanged = delegate { };
private Spherical _position = Spherical.zero;
/// <summary>
/// The position of the thing in local space, in meters.
/// </summary>
public Spherical position {
get { return _position; }
set {
@ -104,8 +148,14 @@ namespace Passer.RoboidControl {
}
}
public event ChangeHandler OnOrientationChanged = delegate {};
/// <summary>
/// Event triggered when the orientation has changed
/// </summary>
public event ChangeHandler OnOrientationChanged = delegate { };
private SwingTwist _orientation = SwingTwist.zero;
/// <summary>
/// The orientation of the thing in local space
/// </summary>
public SwingTwist orientation {
get { return _orientation; }
set {
@ -116,8 +166,14 @@ namespace Passer.RoboidControl {
}
}
public event SphericalHandler OnLinearVelocityChanged = delegate {};
/// <summary>
/// Event triggered when the linear velocity has changed
/// </summary>
public event SphericalHandler OnLinearVelocityChanged = delegate { };
private Spherical _linearVelocity = Spherical.zero;
/// <summary>
/// The linear velocity of the thing in local space in meters per second
/// </summary>
public Spherical linearVelocity {
get => _linearVelocity;
set {
@ -127,9 +183,15 @@ namespace Passer.RoboidControl {
}
}
}
/// <summary>
/// The angular velocity of the thing in local space
/// </summary>
public Spherical angularVelocity = Spherical.zero;
#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
@ -138,67 +200,92 @@ namespace Passer.RoboidControl {
#region Init
/// <summary>
/// Create a new thing for the given participant
/// </summary>
/// <param name="participant">The participant for which this thing is created</param>
/// <param name="invokeEvent">True when a new thing event should be triggered</param>
public Thing(RemoteParticipant participant, bool invokeEvent = false) {
this.participant = participant;
if (invokeEvent)
InvokeNewThing(this);
}
public Thing(RemoteParticipant sender, byte networkId, byte thingId, byte thingType = 0) {
this.participant = sender;
/// <summary>
/// Create a new thing for the given participant
/// </summary>
/// <param name="participant">The participant for which this thing is created</param>
/// <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>
public Thing(RemoteParticipant participant, byte networkId, byte thingId, byte thingType = 0) {
this.participant = participant;
this.id = thingId;
this.type = thingType;
this.networkId = networkId;
}
public virtual void CreateComponent() {}
/// <summary>
/// Function which can be used to create components in external engines.
/// </summary>
/// Currently this is used to create GameObjects in Unity
public virtual void CreateComponent() { }
#endregion Init
#region Update
#if UNITY_5_3_OR_NEWER
/// <summary>
/// Convience function for use in Unity which removes the need for a currentTime argument
/// </summary>
public void Update() {
Update((ulong)UnityEngine.Time.time * 1000);
}
#endif
/// <summary>
/// Update this thing
/// </summary>
/// <param name="currentTime">The current time in milliseconds</param>
public virtual void Update(ulong currentTime) {
// should recurse over children...
}
/// <summary>
/// Function used to generate binary data for this thing
/// </summary>
/// <returns>a byte array with the binary data</returns>
/// @sa Passer::RoboidControl::BinaryMsg
public virtual byte[] GenerateBinary() { return new byte[0]; }
/// <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) {
//if (sensor != null)
// sensor.ProcessBytes(bytes);
}
#endregion Update
// Experimental
// public float stressLevel = 0;
// protected delegate void ReceptorFunc(Sensor sensor);
// protected void SetupReceptor(Sensor sensor, ReceptorFunc receptor) {
// sensor.Signaller += (sensor => Receptor(receptor, sensor));
// }
// protected void Receptor(ReceptorFunc receptor, Sensor sensor) {
// if (sensor.signalStrength <= stressLevel)
// return;
// receptor(sensor);
// }
//---------- All Things
// private static readonly List<Thing> allThings = new();
public delegate void ThingHandler(Thing t);
public static event ThingHandler OnNewThing = delegate {};
/// <summary>
/// Event triggered when a new thing has been created
/// </summary>
public static event ThingHandler OnNewThing = delegate { };
/// <summary>
/// Trigger the creation for the given thing
/// </summary>
/// <param name="thing">The created thing</param>
public static void InvokeNewThing(Thing thing) {
OnNewThing?.Invoke(thing);
OnNewThing?.Invoke(thing);
}
/// <summary>
/// Check if the thing has the given properaties
/// </summary>
/// <param name="thing">The thing to check</param>
/// <param name="networkId">The network ID to compare to</param>
/// <param name="thingId">The thing ID to compare to</param>
/// <returns>True when the thing has the given properties</returns>
public static bool IsThing(Thing thing, byte networkId, byte thingId) {
if (thing == null)
return false;

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: d5d87461365fd8a4da528aa84a49e62c
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 743b128a79ef8414fa29d7bb3b9e2ac8

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 92729868a8379c04197dcb80d0276a63
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: