Merge commit '9e85419e35233598ba347f4af7da6f5ec033d1d2' into V2

This commit is contained in:
Pascal Serrarens 2025-02-19 14:13:39 +01:00
commit d3e12c16f2
27 changed files with 310 additions and 337 deletions

View File

@ -42,7 +42,7 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places.
# The default value is: My Project.
PROJECT_NAME = "Control Core for C#"
PROJECT_NAME = "Roboid Control for C#"
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
@ -68,7 +68,7 @@ PROJECT_LOGO = //intranet/home/Afbeeldingen/PasserVR/Logos/Logo3NameRi
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
OUTPUT_DIRECTORY = //intranet/web/passer_life/apis/ControlCore/Csharp/
OUTPUT_DIRECTORY = //intranet/web/roboidcontrol_doc/Csharp/
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
# sub-directories (in 2 levels) under the output directory of each output format
@ -777,7 +777,7 @@ MAX_INITIALIZER_LINES = 30
# list will mention the files that were used to generate the documentation.
# The default value is: YES.
SHOW_USED_FILES = YES
SHOW_USED_FILES = NO
# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
# will remove the Files entry from the Quick Index and from the Folder Tree View

View File

@ -1,175 +0,0 @@
/*
using System;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Concurrent;
public class EchoStream : Stream {
public override bool CanTimeout { get; } = true;
public override int ReadTimeout { get; set; } = Timeout.Infinite;
public override int WriteTimeout { get; set; } = Timeout.Infinite;
public override bool CanRead { get; } = true;
public override bool CanSeek { get; } = false;
public override bool CanWrite { get; } = true;
public bool CopyBufferOnWrite { get; set; } = false;
private readonly object _lock = new object();
// Default underlying mechanism for BlockingCollection is ConcurrentQueue<T>, which is what we want
private readonly BlockingCollection<byte[]> _Buffers;
private int _maxQueueDepth = 10;
private byte[] m_buffer = null;
private int m_offset = 0;
private int m_count = 0;
private bool m_Closed = false;
private bool m_FinalZero = false; //after the stream is closed, set to true after returning a 0 for read()
public override void Close() {
m_Closed = true;
// release any waiting writes
_Buffers.CompleteAdding();
}
public bool DataAvailable {
get {
return _Buffers.Count > 0;
}
}
private long _Length = 0L;
public override long Length {
get {
return _Length;
}
}
private long _Position = 0L;
public override long Position {
get {
return _Position;
}
set {
throw new NotImplementedException();
}
}
public EchoStream() : this(10) {
}
public EchoStream(int maxQueueDepth) {
_maxQueueDepth = maxQueueDepth;
_Buffers = new BlockingCollection<byte[]>(_maxQueueDepth);
}
// we override the xxxxAsync functions because the default base class shares state between ReadAsync and WriteAsync, which causes a hang if both are called at once
public new Task WriteAsync(byte[] buffer, int offset, int count) {
return Task.Run(() => Write(buffer, offset, count));
}
// we override the xxxxAsync functions because the default base class shares state between ReadAsync and WriteAsync, which causes a hang if both are called at once
public new Task<int> ReadAsync(byte[] buffer, int offset, int count) {
return Task.Run(() => {
return Read(buffer, offset, count);
});
}
public override void Write(byte[] buffer, int offset, int count) {
if (m_Closed || buffer.Length - offset < count || count <= 0)
return;
byte[] newBuffer;
if (!CopyBufferOnWrite && offset == 0 && count == buffer.Length)
newBuffer = buffer;
else {
newBuffer = new byte[count];
System.Buffer.BlockCopy(buffer, offset, newBuffer, 0, count);
}
if (!_Buffers.TryAdd(newBuffer, WriteTimeout))
throw new TimeoutException("EchoStream Write() Timeout");
_Length += count;
}
public override int Read(byte[] buffer, int offset, int count) {
if (count == 0)
return 0;
lock (_lock) {
if (m_count == 0 && _Buffers.Count == 0) {
if (m_Closed) {
if (!m_FinalZero) {
m_FinalZero = true;
return 0;
}
else {
return -1;
}
}
if (_Buffers.TryTake(out m_buffer, ReadTimeout)) {
m_offset = 0;
m_count = m_buffer.Length;
}
else {
if (m_Closed) {
if (!m_FinalZero) {
m_FinalZero = true;
return 0;
}
else {
return -1;
}
}
else {
return 0;
}
}
}
int returnBytes = 0;
while (count > 0) {
if (m_count == 0) {
if (_Buffers.TryTake(out m_buffer, 0)) {
m_offset = 0;
m_count = m_buffer.Length;
}
else
break;
}
var bytesToCopy = (count < m_count) ? count : m_count;
System.Buffer.BlockCopy(m_buffer, m_offset, buffer, offset, bytesToCopy);
m_offset += bytesToCopy;
m_count -= bytesToCopy;
offset += bytesToCopy;
count -= bytesToCopy;
returnBytes += bytesToCopy;
}
_Position += returnBytes;
return returnBytes;
}
}
public override int ReadByte() {
byte[] returnValue = new byte[1];
return (Read(returnValue, 0, 1) <= 0 ? -1 : (int)returnValue[0]);
}
public override void Flush() {
}
public override long Seek(long offset, SeekOrigin origin) {
throw new NotImplementedException();
}
public override void SetLength(long value) {
throw new NotImplementedException();
}
}
*/

84
Messages/BinaryMsg.cs Normal file
View File

@ -0,0 +1,84 @@
namespace Passer.RoboidControl {
/// <summary>
/// A message containing binary data for custom communication
/// </summary>
public class BinaryMsg : IMessage {
/// <summary>
/// The message ID
/// </summary>
public const byte Id = 0xB1;
/// <summary>
/// The length of the message, excluding the binary data
/// </summary>
/// For the total size of the message this.bytes.Length should be added to this value.
public const byte length = 2;
/// <summary>
/// The network ID identifying the thing
/// </summary>
public byte networkId;
/// <summary>
/// The ID of the thing
/// </summary>
public byte thingId;
/// <summary>
/// The binary data
/// </summary>
public byte[] bytes;
/// <summary>
/// Create a new message for sending
/// </summary>
/// <param name="networkId">The netowork ID of the thing</param>
/// <param name="thingId">The ID of the thing</param>
/// <param name="bytes">The binary data for the thing</param>
public BinaryMsg(byte networkId, byte thingId, byte[] bytes) : base() {
this.networkId = networkId;
this.thingId = thingId;
this.bytes = bytes;
}
/// <summary>
/// Create an empty message for sending
/// </summary>
/// <param name="networkId">The netowork ID of the thing</param>
/// <param name="thingId">The ID of the thing</param>
public BinaryMsg(byte networkId, Thing thing) : base() {
this.networkId = networkId;
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>
public BinaryMsg(byte[] buffer) {
byte ix = 1;
this.networkId = buffer[ix++];
this.thingId = buffer[ix++];
byte length = (byte)(buffer.Length - ix);
this.bytes = new byte[length];
for (uint bytesIx = 0; bytesIx < length; bytesIx++)
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>
public override byte Serialize(ref byte[] buffer) {
if (buffer.Length < BinaryMsg.length + buffer.Length || bytes.Length == 0)
return 0;
byte ix = 0;
buffer[ix++] = BinaryMsg.Id;
buffer[ix++] = this.networkId;
buffer[ix++] = this.thingId;
foreach (byte b in bytes)
buffer[ix++] = b;
return ix;
}
}
}

View File

@ -1,23 +0,0 @@
namespace Passer.Control.Core {
public class ClientMsg : IMessage {
public const byte Id = 0xA0;
public const byte length = 2;
public byte networkId;
public ClientMsg(byte networkId) {
this.networkId = networkId;
}
public ClientMsg(byte[] buffer) {
this.networkId = buffer[1];
}
public override byte Serialize(ref byte[] buffer) {
byte ix = 0;
buffer[ix++] = ClientMsg.Id;
buffer[ix++] = networkId;
return ClientMsg.length;
}
}
}

View File

@ -1,53 +0,0 @@
namespace Passer.Control.Core {
public class CustomMsg : IMessage {
public const byte Id = 0xB1;
public byte networkId;
public byte thingId;
public byte[] bytes;
public CustomMsg(byte[] buffer) {
byte ix = 1;
this.networkId = buffer[ix++];
this.thingId = buffer[ix++];
byte length = (byte)(buffer.Length - ix);
this.bytes = new byte[length];
for (uint bytesIx = 0; bytesIx < length; bytesIx++)
this.bytes[bytesIx] = buffer[ix++];
}
public CustomMsg(byte networkId, byte thingId, byte[] bytes) : base() {
this.networkId = networkId;
this.thingId = thingId;
this.bytes = bytes;
}
public CustomMsg(byte networkId, Thing thing) : base() {
this.networkId = networkId;
this.thingId = thing.id;
this.bytes = new byte[0];
}
public override byte Serialize(ref byte[] buffer) {
if (bytes.Length == 0)
return 0;
byte ix = 0;
buffer[ix++] = CustomMsg.Id;
buffer[ix++] = this.networkId;
buffer[ix++] = this.thingId;
//buffer[ix++] = (byte)bytes.Length;
foreach (byte b in bytes)
buffer[ix++] = b;
return ix;
}
//public static async Task<bool> Receive(Stream dataStream, Participant client, byte packetSize) {
// byte[] buffer = await Receive(dataStream, packetSize);
// CustomMsg msg = new(buffer);
// client.messageQueue.Enqueue(msg);
// return true;
//}
}
}

View File

@ -1,7 +1,7 @@
using System.IO;
using System.Threading.Tasks;
namespace Passer.Control.Core {
namespace Passer.RoboidControl {
public class DestroyMsg : IMessage {
public const byte Id = 0x20;

View File

@ -1,7 +1,7 @@
using System.IO;
using System.Threading.Tasks;
namespace Passer.Control.Core {
namespace Passer.RoboidControl {
public class InvestigateMsg : IMessage {
public const byte Id = 0x81;

View File

@ -1,6 +1,6 @@
using Passer.LinearAlgebra;
namespace Passer.Control.Core
namespace Passer.RoboidControl
{
public class LowLevelMessages
{

View File

@ -1,7 +1,7 @@
using System.Threading.Tasks;
using System.IO;
namespace Passer.Control.Core {
namespace Passer.RoboidControl {
public class IMessage {
public IMessage() { }

View File

@ -1,4 +1,4 @@
namespace Passer.Control.Core {
namespace Passer.RoboidControl {
public class NameMsg : IMessage {
public const byte Id = 0x91; // 145

View File

@ -1,4 +1,4 @@
namespace Passer.Control.Core {
namespace Passer.RoboidControl {
public class NetworkIdMsg : IMessage {
public const byte Id = 0xA1;

View File

@ -0,0 +1,51 @@
namespace Passer.RoboidControl {
/// <summary>
/// A participant messages notifies other participants of its presence
/// </summary>
public class ParticipantMsg : IMessage {
/// <summary>
/// The message ID
/// </summary>
public const byte Id = 0xA0;
/// <summary>
/// The length of the message
/// </summary>
public const byte length = 2;
/// <summary>
/// The network ID known by the participant
/// </summary>
public byte networkId;
/// <summary>
/// Create a new message for sending
/// </summary>
/// <param name="networkId"></param>
public ParticipantMsg(byte networkId) {
this.networkId = networkId;
}
/// <summary>
/// Create a message for receiving
/// </summary>
/// <param name="buffer">The byte array to parse</param>
public ParticipantMsg(byte[] buffer) {
this.networkId = buffer[1];
}
/// <summary>
/// Serialize the message into a byte array
/// </summary>
/// <param name="buffer">The buffer to serialize into</param>
/// <returns>The length of the message in the buffer</returns>
public override byte Serialize(ref byte[] buffer) {
if (buffer.Length < ParticipantMsg.length)
return 0;
byte ix = 0;
buffer[ix++] = ParticipantMsg.Id;
buffer[ix++] = networkId;
return ParticipantMsg.length;
}
}
}

View File

@ -2,7 +2,7 @@ using System.IO;
using System.Threading.Tasks;
using Passer.LinearAlgebra;
namespace Passer.Control.Core {
namespace Passer.RoboidControl {
public class PoseMsg : IMessage {
public const byte Id = 0x10;

View File

@ -1,7 +1,7 @@
using System.IO;
using System.Threading.Tasks;
namespace Passer.Control.Core {
namespace Passer.RoboidControl {
public class TextMsg : IMessage {
public TextMsg(byte[] buffer) : base(buffer) {}

90
Messages/ThingMsg.cs Normal file
View File

@ -0,0 +1,90 @@
namespace Passer.RoboidControl {
/// <summary>
/// Message providing generic information about a Thing
/// </summary>
public class ThingMsg : IMessage {
/// <summary>
/// The message ID
/// </summary>
public const byte id = 0x80;
/// <summary>
/// The length of the message
/// </summary>
public const byte length = 5;
/// <summary>
/// The network ID of the thing
/// </summary>
public byte networkId;
/// <summary>
/// The ID of the thing
/// </summary>
public byte thingId;
/// <summary>
/// The Thing.Type of the thing
/// </summary>
public byte thingType;
/// <summary>
/// The parent of the thing in the hierarachy. This is null for root Things
/// </summary>
public byte parentId;
/// <summary>
/// Create a message for sending
/// </summary>
/// <param name="networkId">The network ID of the thing</param>
/// <param name="thing">The thing</param>
public ThingMsg(byte networkId, Thing thing) {
this.networkId = networkId;
this.thingId = thing.id;
this.thingType = thing.type;
if (thing.parent != null)
this.parentId = thing.parent.id;
else
this.parentId = 0;
}
/// <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
/// </summary>
/// <param name="buffer">The byte array to parse</param>
public ThingMsg(byte[] buffer) {
uint ix = 1;
this.networkId = buffer[ix++];
this.thingId = buffer[ix++];
this.thingType = buffer[ix++];
this.parentId = buffer[ix];
}
/// <summary>
/// Serialize the message into a byte array
/// </summary>
/// <param name="buffer">The buffer to serialize into</param>
/// <returns>The length of the message in the bufer. 0 when the buffer was too small</returns>
public override byte Serialize(ref byte[] buffer) {
if (buffer.Length < ThingMsg.length)
return 0;
byte ix = 0;
buffer[ix++] = ThingMsg.id;
buffer[ix++] = this.networkId;
buffer[ix++] = this.thingId;
buffer[ix++] = this.thingType;
buffer[ix++] = this.parentId;
return ThingMsg.length;
}
}
}

View File

@ -1,4 +1,4 @@
namespace Passer.Control.Core {
namespace Passer.RoboidControl {
public class ModelUrlMsg : IMessage {
public const byte Id = 0x90; // (144) Model URL

View File

@ -4,13 +4,15 @@ using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
namespace Passer.Control.Core {
namespace Passer.RoboidControl {
/// <summary>
/// A participant is used for communcation between things
/// </summary>
public class Participant : RemoteParticipant {
public byte[] buffer = new byte[1024];
public ulong publishInterval = 3000; // = 3 seconds
//public byte networkId = 0;
public string name = "Participant";
public IPEndPoint endPoint = null;
@ -148,7 +150,7 @@ namespace Passer.Control.Core {
}
public virtual void Publish() {
this.Publish(new ClientMsg(this.networkId));
this.Publish(new ParticipantMsg(this.networkId));
}
#endregion Update
@ -160,7 +162,7 @@ namespace Passer.Control.Core {
this.Send(remoteParticipant, new ThingMsg(this.networkId, thing));
this.Send(remoteParticipant, new NameMsg(this.networkId, thing));
this.Send(remoteParticipant, new ModelUrlMsg(this.networkId, thing));
this.Send(remoteParticipant, new CustomMsg(this.networkId, thing));
this.Send(remoteParticipant, new BinaryMsg(this.networkId, thing));
}
public bool Send(IMessage msg) {
@ -189,7 +191,7 @@ namespace Passer.Control.Core {
this.Publish(new ThingMsg(this.networkId, thing));
this.Publish(new NameMsg(this.networkId, thing));
this.Publish(new ModelUrlMsg(this.networkId, thing));
this.Publish(new CustomMsg(this.networkId, thing));
this.Publish(new BinaryMsg(this.networkId, thing));
}
public bool Publish(IMessage msg) {
@ -232,8 +234,8 @@ namespace Passer.Control.Core {
}
switch (msgId) {
case ClientMsg.Id: // 0xA0 / 160
this.Process(remoteParticipant, new ClientMsg(data));
case ParticipantMsg.Id: // 0xA0 / 160
this.Process(remoteParticipant, new ParticipantMsg(data));
break;
case NetworkIdMsg.Id: // 0xA1 / 161
this.Process(remoteParticipant, new NetworkIdMsg(data));
@ -253,8 +255,8 @@ namespace Passer.Control.Core {
case PoseMsg.Id: // 0x10 / 16
// result = await PoseMsg.Receive(dataStream, client, packetSize);
break;
case CustomMsg.Id: // 0xB1 / 177
this.Process(remoteParticipant, new CustomMsg(data));
case BinaryMsg.Id: // 0xB1 / 177
this.Process(remoteParticipant, new BinaryMsg(data));
break;
case TextMsg.Id: // 0xB0 / 176
// result = await TextMsg.Receive(dataStream, client, packetSize);
@ -271,7 +273,7 @@ namespace Passer.Control.Core {
#region Process
protected virtual void Process(RemoteParticipant sender, ClientMsg msg) { }
protected virtual void Process(RemoteParticipant sender, ParticipantMsg msg) { }
protected virtual void Process(RemoteParticipant sender, NetworkIdMsg msg) {
Console.WriteLine($"{this.name} receive network id {this.networkId} {msg.networkId}");
@ -301,7 +303,7 @@ namespace Passer.Control.Core {
protected virtual void Process(PoseMsg msg) { }
protected virtual void Process(RemoteParticipant sender, CustomMsg msg) {
protected virtual void Process(RemoteParticipant sender, BinaryMsg msg) {
// Console.WriteLine($"Participant: Process binary [{msg.networkId}/{msg.thingId}]");
Thing thing = sender.Get(msg.networkId, msg.thingId);
thing?.ProcessBinary(msg.bytes);

View File

@ -2,32 +2,63 @@
using System;
using System.Collections.Generic;
namespace Passer.Control.Core {
namespace Passer.RoboidControl {
/// <summary>
/// A reference to a participant, possible on a remote location
/// </summary>
public class RemoteParticipant {
/// <summary>
/// The internet address of the participant
/// </summary>
public string ipAddress = "0.0.0.0";
/// <summary>
/// The UDP port on which the participant can be reached
/// </summary>
public int port = 0;
/// <summary>
/// The network ID of the participant
/// </summary>
public byte networkId;
public RemoteParticipant() {
}
/// <summary>
/// Default constructor
/// </summary>
public RemoteParticipant() {}
/// <summary>
/// Create a new remote participant
/// </summary>
/// <param name="ipAddress">The ip address of the participant</param>
/// <param name="port">The UDP port of the participant</param>
public RemoteParticipant(string ipAddress, int port) {
this.ipAddress = ipAddress;
this.port = port;
}
/// <summary>
/// The things reported by this participant
/// </summary>
protected readonly List<Thing> things = new List<Thing>();
/// <summary>
/// Get a thing with the given ids
/// </summary>
/// <param name="networkId">The networkId 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>
public Thing Get(byte networkId, byte thingId) {
Thing thing = things.Find(aThing => Thing.IsThing(aThing, networkId, thingId));
// if (thing == null)
// Console.WriteLine($"Could not find thing {ipAddress}:{port}[{networkId}/{thingId}]");
return thing;
}
// if (thing == null)
// Console.WriteLine($"Could not find thing {ipAddress}:{port}[{networkId}/{thingId}]");
/// <summary>
/// Add a new thing for this participant
/// </summary>
/// <param name="thing">The thing to add</param>
/// <param name="invokeEvent">Invoke an notification event when the thing has been added</param>
public void Add(Thing thing, bool invokeEvent = true) {
// Console.WriteLine($"added thing [{thing.networkId}/{thing.id}]");
Thing foundThing = Get(thing.networkId, thing.id);

View File

@ -1,6 +1,6 @@
using System;
namespace Passer.Control.Core {
namespace Passer.RoboidControl.Core {
public class DistanceSensor : Thing {
public float distance = 0;

View File

@ -1,6 +1,6 @@
using System;
namespace Passer.Control.Core {
namespace Passer.RoboidControl.Core {
public class TemperatureSensor : Thing {
public float temp = 0;

View File

@ -1,4 +1,4 @@
namespace Passer.Control.Core {
namespace Passer.RoboidControl.Core {
public class TouchSensor : Thing {
public bool touchedSomething = false;

View File

@ -3,10 +3,17 @@ using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
namespace Passer.Control.Core {
namespace Passer.RoboidControl {
/// <summary>
/// A site server is a participant which provides a shared simulated environment
/// </summary>
public class SiteServer : Participant {
/// <summary>
/// Create a new site server
/// </summary>
/// <param name="port"></param>
public SiteServer(int port = 7681) {
this.name = "Site Server";
@ -22,6 +29,9 @@ namespace Passer.Control.Core {
new Tuple<UdpClient, IPEndPoint>(this.udpClient, new IPEndPoint(IPAddress.Any, port)));
}
/// <summary>
/// Close the site
/// </summary>
public void Close() {
this.udpClient?.Close();
}
@ -29,7 +39,7 @@ namespace Passer.Control.Core {
public override void Publish() {
}
protected override void Process(RemoteParticipant sender, ClientMsg msg) {
protected override void Process(RemoteParticipant sender, ParticipantMsg msg) {
if (msg.networkId == 0) {
Console.WriteLine($"{this.name} received New Client -> {sender.networkId}");
this.Send(sender, new NetworkIdMsg(sender.networkId));

View File

@ -2,18 +2,21 @@ using System;
using System.Collections.Generic;
using Passer.LinearAlgebra;
namespace Passer.Control.Core {
namespace Passer.RoboidControl {
/// <summary>
/// A thing is the basic building block
/// A thing is the primitive building block
/// </summary>
[Serializable]
public class Thing {
#region Types
/// <summary>
/// Predefined thing types
/// </summary>
public enum Type {
Undeterment,
Undetermined,
// Sensor
Switch,
DistanceSensor,

View File

@ -1,45 +0,0 @@
namespace Passer.Control.Core {
public class ThingMsg : IMessage {
public const byte length = 5;
public const byte id = 0x80;
public byte networkId;
public byte thingId;
public byte thingType;
public byte parentId;
public ThingMsg(byte networkId, Thing thing) {
this.networkId = networkId;
this.thingId = thing.id;
this.thingType = thing.type;
if (thing.parent != null)
this.parentId = thing.parent.id;
else
this.parentId = 0;
}
public ThingMsg(byte networkId, byte thingId, byte thingType, byte parentId) {
this.networkId = networkId;
this.thingId = thingId;
this.thingType = thingType;
this.parentId = parentId;
}
public ThingMsg(byte[] buffer) {
uint ix = 1;
this.networkId = buffer[ix++];
this.thingId = buffer[ix++];
this.thingType = buffer[ix++];
this.parentId = buffer[ix];
}
public override byte Serialize(ref byte[] buffer) {
byte ix = 0;
buffer[ix++] = ThingMsg.id;
buffer[ix++] = this.networkId;
buffer[ix++] = this.thingId;
buffer[ix++] = this.thingType;
buffer[ix++] = this.parentId;
return ThingMsg.length;
}
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 620fef383ba64a44995a234a71b2f189

View File

@ -3,7 +3,7 @@ using System;
using System.Threading;
using NUnit.Framework;
using Passer.Control.Core;
using Passer.RoboidControl;
namespace ControlCore.test {
public class Tests {