Simplified constructors

This commit is contained in:
Pascal Serrarens 2025-05-28 12:10:10 +02:00
parent bd53b71cd3
commit d9d64ebc8f
16 changed files with 126 additions and 44 deletions

View File

@ -9,7 +9,7 @@ namespace RoboidControl {
readonly TouchSensor touchRight; readonly TouchSensor touchRight;
const float speed = 0.5f; const float speed = 0.5f;
public BB2B(Participant owner) : base(owner) { public BB2B(Thing parent = default) : base(parent) {
this.name = "BB2B"; this.name = "BB2B";
this.SetMotors(new Motor(this), new Motor(this)); this.SetMotors(new Motor(this), new Motor(this));
this.SetDriveDimensions(0.064f, 0.128f); this.SetDriveDimensions(0.064f, 0.128f);

View File

@ -9,15 +9,15 @@ namespace RoboidControl {
readonly TouchSensor touchRight; readonly TouchSensor touchRight;
const float speed = 180.0f; // wheel rotation speed in degrees const float speed = 180.0f; // wheel rotation speed in degrees
public BB2B_Encoder(Participant owner) : base(owner) { public BB2B_Encoder(Thing parent) : base(parent) {
this.name = "BB2B"; this.name = "BB2B";
this.SetDriveDimensions(0.064f, 0.128f); this.SetDriveDimensions(0.064f, 0.128f);
// Update the basic motors to motors with encoder // Update the basic motors to motors with encoder
EncoderMotor leftMotor = new(this, new RelativeEncoder()) { ControlledMotor leftMotor = new(this, new RelativeEncoder()) {
position = new Spherical(0.064f, Direction.left) position = new Spherical(0.064f, Direction.left)
}; };
EncoderMotor rightMotor = new(this, new RelativeEncoder()) { ControlledMotor rightMotor = new(this, new RelativeEncoder()) {
position = new Spherical(0.064f, Direction.right) position = new Spherical(0.064f, Direction.right)
}; };
this.SetMotors(leftMotor, rightMotor); this.SetMotors(leftMotor, rightMotor);

View File

@ -3,7 +3,7 @@ using RoboidControl;
class Program { class Program {
static void Main() { static void Main() {
BB2B bb2b = new(ParticipantUDP.Isolated()); BB2B bb2b = new();
while (true) { while (true) {
bb2b.Update(); bb2b.Update();

View File

@ -11,3 +11,21 @@ The documentation for Roboid Control for C# is found at https://docs.roboidcontr
- RoboidControl::Thing - RoboidControl::Thing
- RoboidControl::Participant - RoboidControl::Participant
# Get Started
## Unity
The Unity environment can use the same RoboidControl code as every other C# code, but needs a *starter* wrapper around it to make the things visibile. For example, to start the BB2B example in Unity one needs to write a BB2B_Starter.cs component as follows:
```
using RoboidControl.Unity;
public class BB2B_Starter : SiteServer {
void Start() {
new RoboidControl.BB2B();
}
}
```
This component then should be attached to a GameObject in the scene.
It is possible to create a Site Server in Unity by just adding the `SiteServer` Component to a GameObject in the scene. When this is run, other roboids will be able to connect to this site then.

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Reflection.Metadata;
namespace RoboidControl { namespace RoboidControl {
@ -14,6 +15,13 @@ 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 {
private Participant() {
this.root = Thing.CreateRoot(this);
this.root.name = "Root";
this.Add(this.root);
}
/// <summary> /// <summary>
/// Create a new participant with the given communcation info /// Create a new participant with the given communcation info
/// </summary> /// </summary>
@ -26,6 +34,10 @@ namespace RoboidControl {
this.udpClient = localParticipant.udpClient; this.udpClient = localParticipant.udpClient;
} }
public static readonly Participant localParticipant = new();
public Thing root = null;
/// <summary> /// <summary>
/// The Ip Address of a participant. When the participant is local, this contains 0.0.0.0 /// The Ip Address of a participant. When the participant is local, this contains 0.0.0.0
/// </summary> /// </summary>

View File

@ -132,9 +132,9 @@ namespace RoboidControl {
protected virtual Thing ProcessNewThing(Participant sender, ThingMsg msg) { protected virtual Thing ProcessNewThing(Participant sender, ThingMsg msg) {
return msg.thingType switch { return msg.thingType switch {
Thing.Type.TouchSensor => new TouchSensor(sender, msg.thingId), //Thing.Type.TouchSensor => new TouchSensor(sender, msg.thingId),
Thing.Type.DifferentialDrive => new DifferentialDrive(sender, msg.thingId), //Thing.Type.DifferentialDrive => new DifferentialDrive(sender, msg.thingId),
_ => new Thing(sender, msg.thingType, msg.thingId), _ => Thing.CreateRemote(sender, msg.thingType, msg.thingId)
}; };
} }

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using LinearAlgebra; using LinearAlgebra;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
namespace RoboidControl { namespace RoboidControl {
@ -26,10 +27,11 @@ namespace RoboidControl {
public const byte ControlledMotor = 0x06; public const byte ControlledMotor = 0x06;
public const byte UncontrolledMotor = 0x07; public const byte UncontrolledMotor = 0x07;
public const byte Servo = 0x08; public const byte Servo = 0x08;
public const byte IncrementalEncoder = 0x19; public const byte RelativeEncoder = 0x19;
// Other // Other
public const byte Root = 0x10;
public const byte Roboid = 0x09; public const byte Roboid = 0x09;
public const byte HUmanoid = 0x0A; public const byte Humanoid = 0x0A;
public const byte ExternalSensor = 0x0B; public const byte ExternalSensor = 0x0B;
public const byte Animator = 0x0C; public const byte Animator = 0x0C;
public const byte DifferentialDrive = 0x0D; public const byte DifferentialDrive = 0x0D;
@ -37,6 +39,29 @@ namespace RoboidControl {
#region Init #region Init
private Thing(Participant owner) {
this.type = Type.Root;
this.positionUpdated = true;
this.orientationUpdated = true;
this.hierarchyChanged = true;
this.owner = owner;
this.parent = null;
}
public static Thing CreateRoot(Participant owner) {
return new Thing(owner);
}
static Thing localRoot {
get {
Participant participant = Participant.localParticipant;
return participant.root;
}
}
/*
/// <summary> /// <summary>
/// Create a new thing without communication abilities /// Create a new thing without communication abilities
/// </summary> /// </summary>
@ -66,6 +91,7 @@ namespace RoboidControl {
this.owner.updateQueue.Enqueue(e); this.owner.updateQueue.Enqueue(e);
} }
} }
*/
/// <summary> /// <summary>
/// Create a new child thing /// Create a new child thing
@ -75,26 +101,33 @@ namespace RoboidControl {
/// <param name="thingId">The ID of the thing, leave out or set to zero to generate an ID</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> /// <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> /// <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) { 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;
} }
*/
public Thing(byte thingType = Type.Undetermined, Thing parent = default) {
this.type = thingType;
// /// <summary> this.positionUpdated = true;
// /// Create a new thing for the given participant this.orientationUpdated = true;
// /// </summary> this.hierarchyChanged = true;
// /// <param name="owner">The participant owning the thing</param>
// /// <param name="networkId">The network ID of the thing</param> if (parent == default)
// /// <param name="thingId">The ID of the thing</param> this.parent = Participant.localParticipant.root;
// /// <param name="thingType">The type of thing</param> else
// public Thing(Participant owner, byte networkId, byte thingId, byte thingType = 0) { this.parent = parent;
// this.owner = owner;
// this.id = thingId; this.owner = parent.owner;
// this.type = thingType; this.owner.Add(this, true);
// this.networkId = networkId; }
// // Console.Write($"New thing added to {owner}");
// this.owner.Add(this); public static Thing CreateRemote(Participant owner, byte thingType, byte thingId) {
// InvokeNewThing(this); Thing remoteThing = new(thingType, owner.root) {
// } id = thingId
};
return remoteThing;
}
/// <summary> /// <summary>
/// Function which can be used to create components in external engines. /// Function which can be used to create components in external engines.

View File

@ -3,12 +3,12 @@ namespace RoboidControl {
/// @brief A motor with speed control /// @brief A motor with speed control
/// It uses a feedback loop from an encoder to regulate the speed /// It uses a feedback loop from an encoder to regulate the speed
/// The speed is measured in revolutions per second. /// The speed is measured in revolutions per second.
class EncoderMotor : Motor { public class ControlledMotor : Motor {
public EncoderMotor(Thing parent, RelativeEncoder encoder) : base(parent) { public ControlledMotor(Thing parent, RelativeEncoder encoder) : base(parent) {
this.encoder = encoder; this.encoder = encoder;
} }
// Upgrade an existing motor with an encoder // Upgrade an existing motor with an encoder
public EncoderMotor(Motor motor, RelativeEncoder encoder) : base(motor.parent) { public ControlledMotor(Motor motor, RelativeEncoder encoder) : base(motor.parent) {
this.motor = motor; this.motor = motor;
this.encoder = encoder; this.encoder = encoder;
} }
@ -50,7 +50,7 @@ namespace RoboidControl {
float pidP = 0.1F; float pidP = 0.1F;
float pidD = 0.0F; float pidD = 0.0F;
float pidI = 0.0F; //float pidI = 0.0F;
public override void Update(ulong currentTimeMs, bool recurse = false) { public override void Update(ulong currentTimeMs, bool recurse = false) {
float actualSpeed = this.encoder.angularSpeed; float actualSpeed = this.encoder.angularSpeed;

View File

@ -6,11 +6,13 @@ namespace RoboidControl {
/// ///
/// @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> /// <summary>
/// Create a differential drive without communication abilities /// Create a differential drive without communication abilities
/// </summary /// </summary
/// <param name="invokeEvent">Invoke a OnNewThing event when the thing has been created</param> /// <param name="invokeEvent">Invoke a OnNewThing event when the thing has been created</param>
public DifferentialDrive(bool invokeEvent = true) : base(Type.DifferentialDrive, invokeEvent) { } public DifferentialDrive() : base(Type.DifferentialDrive) { }
/// <summary> /// <summary>
/// Create a differential drive for a participant /// Create a differential drive for a participant
/// </summary> /// </summary>
@ -28,15 +30,16 @@ namespace RoboidControl {
// sendBinary = true; // sendBinary = true;
// owner.Send(new BinaryMsg(owner.networkId, this)); // owner.Send(new BinaryMsg(owner.networkId, this));
// this.updateQueue.Enqueue(new UpdateEvent(BinaryMsg.Id)); // this.updateQueue.Enqueue(new UpdateEvent(BinaryMsg.Id));
} }
*/
/// <summary> /// <summary>
/// Create a new child differential drive /// Create a new child differential drive
/// </summary> /// </summary>
/// <param name="parent">The parent thing</param> /// <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="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> /// <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) { } public DifferentialDrive(Thing parent) : base(Type.DifferentialDrive, parent) { }
/// @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
@ -101,9 +104,9 @@ namespace RoboidControl {
/// Positive moves the robot in the forward direction. /// Positive moves the robot in the forward direction.
public void SetWheelAngularVelocity(float angularSpeedLeft, float angularSpeedRight) { public void SetWheelAngularVelocity(float angularSpeedLeft, float angularSpeedRight) {
// This only works when the motor is a motor with encoder // This only works when the motor is a motor with encoder
if (this.leftWheel is EncoderMotor leftMotor) if (this.leftWheel is ControlledMotor leftMotor)
leftMotor.targetAngularSpeed = angularSpeedLeft; leftMotor.targetAngularSpeed = angularSpeedLeft;
if (this.rightWheel is EncoderMotor rightMotor) if (this.rightWheel is ControlledMotor rightMotor)
rightMotor.targetAngularSpeed = angularSpeedRight; rightMotor.targetAngularSpeed = angularSpeedRight;
} }

View File

@ -6,6 +6,7 @@ namespace RoboidControl {
/// A sensor which can detect touches /// A sensor which can detect touches
/// </summary> /// </summary>
public class DigitalSensor : Thing { public class DigitalSensor : Thing {
/*
/// <summary> /// <summary>
/// Create a digital sensor without communication abilities /// Create a digital sensor without communication abilities
/// </summary> /// </summary>
@ -18,13 +19,14 @@ namespace RoboidControl {
/// <param name="thingId">The ID of the thing, leave out or set to zero to generate an ID</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> /// <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) { } public DigitalSensor(Participant owner, byte thingId = 0, bool invokeEvent = true) : base(owner, Type.Switch, thingId, invokeEvent) { }
*/
/// <summary> /// <summary>
/// Create a new child digital sensor /// Create a new child digital sensor
/// </summary> /// </summary>
/// <param name="parent">The parent thing</param> /// <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="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> /// <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) { } public DigitalSensor(Thing parent) : base(Type.Switch, parent) { }
/// <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

View File

@ -9,19 +9,23 @@ namespace RoboidControl {
/// </summary> /// </summary>
public float distance = 0; public float distance = 0;
/*
/// <summary> /// <summary>
/// Constructor for a new distance sensor /// Constructor for a new distance sensor
/// </summary> /// </summary>
/// <param name="participant">The participant for which the sensor is needed</param> /// <param name="participant">The participant for which the sensor is needed</param>
public DistanceSensor(Participant participant) : base(participant, Type.Undetermined) { } public DistanceSensor(Participant participant) : base(participant, Type.Undetermined) { }
/// <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="owner">The participant for with the sensor is needed</param>
/// <param name="networkId">The network ID of the sensor</param> /// <param name="networkId">The network ID of the sensor</param>
/// <param name="thingId">The ID of the thing</param> /// <param name="thingId">The ID of the thing</param>
public DistanceSensor(Participant owner, byte thingId) : base(owner, Type.TemperatureSensor, thingId) { public DistanceSensor(Participant owner, byte thingId) : base(owner, Type.TemperatureSensor, thingId) {}
} */
public DistanceSensor(Thing parent): base(Type.DistanceSensor, parent) {}
#if UNITY_5_3_OR_NEWER #if UNITY_5_3_OR_NEWER
/// @copydoc Passer::RoboidControl::Thing::CreateComponent /// @copydoc Passer::RoboidControl::Thing::CreateComponent

View File

@ -3,8 +3,8 @@ using LinearAlgebra;
namespace RoboidControl { namespace RoboidControl {
public class Motor : Thing { public class Motor : Thing {
public Motor(bool invokeEvent = true) : base(Type.UncontrolledMotor, invokeEvent) { } //public Motor(bool invokeEvent = true) : base(Type.UncontrolledMotor, invokeEvent) { }
public Motor(Thing parent, byte thingId = 0, bool invokeEvent = true) : base(parent, Type.UncontrolledMotor, thingId, invokeEvent) { } public Motor(Thing parent) : base(Type.UncontrolledMotor, parent) { }
/// @brief Motor turning direction /// @brief Motor turning direction
public enum Direction { public enum Direction {

View File

@ -1,4 +1,6 @@
using NUnit.Framework;
namespace RoboidControl { namespace RoboidControl {
/// @brief An Incremental Encoder measures the rotations of an axle using a rotary /// @brief An Incremental Encoder measures the rotations of an axle using a rotary
@ -9,7 +11,12 @@ namespace RoboidControl {
/// full rotation /// full rotation
/// @param distancePerRevolution The distance a wheel travels per full /// @param distancePerRevolution The distance a wheel travels per full
/// rotation /// rotation
/*
public RelativeEncoder(bool invokeEvent = true) : base(Type.IncrementalEncoder, invokeEvent) { } public RelativeEncoder(bool invokeEvent = true) : base(Type.IncrementalEncoder, invokeEvent) { }
*/
public RelativeEncoder(Thing parent = default) : base(Type.RelativeEncoder, parent) {
}
protected float _rotationSpeed = 0; protected float _rotationSpeed = 0;
/// @brief Get the rotation speed since the previous call /// @brief Get the rotation speed since the previous call

View File

@ -6,6 +6,7 @@ namespace RoboidControl {
/// A temperature sensor /// A temperature sensor
/// </summary> /// </summary>
public class TemperatureSensor : Thing { public class TemperatureSensor : Thing {
/*
/// <summary> /// <summary>
/// Create a temperature sensor without communication abilities /// Create a temperature sensor without communication abilities
/// </summary> /// </summary>
@ -18,14 +19,14 @@ namespace RoboidControl {
/// <param name="thingId">The ID of the thing</param> /// <param name="thingId">The ID of the thing</param>
/// <param name="invokeEvent">Invoke a OnNewThing event when the thing has been created</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) { } public TemperatureSensor(Participant owner, byte thingId = 0, bool invokeEvent = true) : base(owner, Type.TemperatureSensor, thingId, invokeEvent) { }
*/
/// <summary> /// <summary>
/// Create a new child temperature sensor /// Create a new child temperature sensor
/// </summary> /// </summary>
/// <param name="parent">The parent thing</param> /// <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="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> /// <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) { } public TemperatureSensor(Thing parent) : base(Type.TemperatureSensor, parent) { }
/// <summary> /// <summary>
/// The measured temperature /// The measured temperature

View File

@ -6,6 +6,7 @@ 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 without communication abilities
/// </summary> /// </summary>
@ -18,13 +19,14 @@ namespace RoboidControl {
/// <param name="thingId">The ID of the thing, leave out or set to zero to generate an ID</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> /// <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 thingId = 0, bool invokeEvent = true) : base(owner, Type.TouchSensor, thingId, invokeEvent) { }
*/
/// <summary> /// <summary>
/// Create a new child touch sensor /// Create a new child touch sensor
/// </summary> /// </summary>
/// <param name="parent">The parent thing</param> /// <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="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> /// <param name="invokeEvent">Invoke a OnNewThing event when the thing has been created</param>
public TouchSensor(Thing parent, byte thingId = 0, bool invokeEvent = true) : base(parent, Type.TouchSensor, thingId, invokeEvent) { public TouchSensor(Thing parent) : base(Type.TouchSensor, parent) {
this.name = "TouchSensor"; this.name = "TouchSensor";
} }

View File

@ -65,7 +65,7 @@ namespace RoboidControl.test {
public void Test_ThingMsg() { public void Test_ThingMsg() {
SiteServer siteServer = new(7681); SiteServer siteServer = new(7681);
ParticipantUDP participant = new("127.0.0.1", 7681, 7682); ParticipantUDP participant = new("127.0.0.1", 7681, 7682);
Thing thing = new(participant) { Thing thing = new() {
name = "First Thing", name = "First Thing",
modelUrl = "https://passer.life/extras/ant.jpg" modelUrl = "https://passer.life/extras/ant.jpg"
}; };