浏览代码

docstrings and update docs

/bug-failed-api-check
Chris Elion 5 年前
当前提交
8b20dcc4
共有 5 个文件被更改,包括 160 次插入24 次删除
  1. 2
      com.unity.ml-agents/Runtime/SideChannels/RawBytesChannel.cs
  2. 74
      com.unity.ml-agents/Runtime/SideChannels/SideChannel.cs
  3. 4
      com.unity.ml-agents/Tests/Editor/SideChannelTests.cs
  4. 49
      docs/Custom-SideChannels.md
  5. 55
      ml-agents-envs/mlagents_envs/side_channel/side_channel.py

2
com.unity.ml-agents/Runtime/SideChannels/RawBytesChannel.cs


/// <inheritdoc/>
public override void OnMessageReceived(IncomingMessage msg)
{
m_MessagesReceived.Add(msg.ReadRawBytes());
m_MessagesReceived.Add(msg.GetRawBytes());
}
/// <summary>

74
com.unity.ml-agents/Runtime/SideChannels/SideChannel.cs


}
}
/// <summary>
/// Utility class for reading the data sent to the SideChannel.
/// </summary>
public class IncomingMessage : IDisposable
{
byte[] m_Data;

/// <summary>
/// Construct an IncomingMessage from the byte array.
/// </summary>
/// <param name="data"></param>
public IncomingMessage(byte[] data)
{
m_Data = data;

/// <summary>
/// Read a boolan value from the message.
/// </summary>
/// <returns></returns>
/// <summary>
/// Read an integer value from the message.
/// </summary>
/// <returns></returns>
/// <summary>
/// Read a float value from the message.
/// </summary>
/// <returns></returns>
/// <summary>
/// Read a string value from the message.
/// </summary>
/// <returns></returns>
public string ReadString()
{
var strLength = ReadInt32();

/// <summary>
/// Reads a list of floats from the message. The length of the list is stored in the message.
/// </summary>
/// <returns></returns>
public IList<float> ReadFloatList()
{
var len = ReadInt32();

return output;
}
public byte[] ReadRawBytes()
/// <summary>
/// Gets the original data of the message. Note that this will return all of the data,
/// even if part of it has already been read.
/// </summary>
/// <returns></returns>
public byte[] GetRawBytes()
/// <summary>
/// Clean up the internal storage.
/// </summary>
public void Dispose()
{
m_Reader?.Dispose();

/// <summary>
/// Utility class for forming the data that is sent to the SideChannel.
/// </summary>
/// <summary>
/// Create a new empty OutgoingMessage.
/// </summary>
public OutgoingMessage()
{
m_Stream = new MemoryStream();

/// <summary>
/// Clean up the internal storage.
/// </summary>
public void Dispose()
{
m_Writer?.Dispose();

/// <summary>
/// Write a boolean value to the message.
/// </summary>
/// <param name="b"></param>
/// <summary>
/// Write an interger value to the message.
/// </summary>
/// <param name="i"></param>
/// <summary>
/// Write a float values to the message.
/// </summary>
/// <param name="f"></param>
/// <summary>
/// Write a string value to the message.
/// </summary>
/// <param name="s"></param>
public void WriteString(string s)
{
var stringEncoded = Encoding.ASCII.GetBytes(s);

/// <summary>
/// Write a list or array of floats to the message.
/// </summary>
/// <param name="floatList"></param>
public void WriteFloatList(IList<float> floatList)
{
WriteInt32(floatList.Count);

}
}
/// <summary>
/// Overwrite the message with a specific byte array.
/// </summary>
/// <param name="data"></param>
public void SetRawBytes(byte[] data)
{
// Reset first.

m_Stream.Write(data, 0, data.Length);
}
/// <summary>
/// Read the byte array of the message.
/// </summary>
/// <returns></returns>
internal byte[] ToByteArray()
{
return m_Stream.ToArray();

4
com.unity.ml-agents/Tests/Editor/SideChannelTests.cs


outgoingMsg.WriteBoolean(boolVal);
outgoingMsg.WriteInt32(intVal);
outgoingMsg.WriteFloat32(floatVal);
outgoingMsg.WriteFloatList(floatListVal);
outgoingMsg.WriteFloatList(floatListVal);
incomingMsg = new IncomingMessage(outgoingMsg.ToByteArray());
}

Assert.AreEqual(floatVal, incomingMsg.ReadFloat32());
Assert.AreEqual(floatListVal, incomingMsg.ReadFloatList());
Assert.AreEqual(floatListVal, incomingMsg.ReadFloatList());
}
}
}

49
docs/Custom-SideChannels.md


### Unity side
The side channel will have to implement the `SideChannel` abstract class and the following method.
* `OnMessageReceived(byte[] data)` : You must implement this method to specify what the side channel will be doing
with the data received from Python. The data is a `byte[]` argument.
* `OnMessageReceived(IncomingMessage msg)` : You must implement this method and read the data from IncomingMessage.
The data must be read in the order that it was written.
To send a byte array from C# to Python, call the `base.QueueMessageToSend(data)` method inside the side channel.
The `data` argument must be a `byte[]`.
To send data from C# to Python, create an `OutgoingMessage` instance, add data to it, call the
`base.QueueMessageToSend(msg)` method inside the side channel, and call the
`OutgoingMessage.Dispose()` method.
To register a side channel on the Unity side, call `Academy.Instance.RegisterSideChannel` with the side channel
as only argument.

* `on_message_received(self, data: bytes) -> None` : You must implement this method to specify what the
side channel will be doing with the data received from Unity. The data is a `byte[]` argument.
* `on_message_received(self, msg: "IncomingMessage") -> None` : You must implement this method and read the data
from IncomingMessage. The data must be read in the order that it was written.
The side channel must also assign a `channel_id` property in the constructor. The `channel_id` is a UUID
(referred in C# as Guid) used to uniquely identify a side channel. This number must be the same on C# and

super().__init__(my_channel_id)
```
To send a byte array from Python to C#, call the `super().queue_message_to_send(bytes_data)` method inside the
side channel. The `bytes_data` argument must be a `bytes` object.
To send a byte array from Python to C#, create an `OutgoingMessage` instance, add data to it, and call the
`super().queue_message_to_send(msg)` method inside the side channel.
To register a side channel on the Python side, pass the side channel as argument when creating the
`UnityEnvironment` object. One of the arguments of the constructor (`side_channels`) is a list of side channels.

ChannelId = new Guid("621f0a70-4f87-11ea-a6bf-784f4387d1f7");
}
public override void OnMessageReceived(byte[] data)
public override void OnMessageReceived(IncomingMessage msg)
var receivedString = Encoding.ASCII.GetString(data);
var receivedString = msg.ReadString();
Debug.Log("From Python : " + receivedString);
}

{
var stringToSend = type.ToString() + ": " + logString + "\n" + stackTrace;
var encodedString = Encoding.ASCII.GetBytes(stringToSend);
base.QueueMessageToSend(encodedString);
using (var msgOut = new OutgoingMessage())
{
msgOut.WriteString(stringToSend);
QueueMessageToSend(msgOut);
}
}
}
}

```python
from mlagents_envs.environment import UnityEnvironment
from mlagents_envs.side_channel.side_channel import SideChannel
from mlagents_envs.side_channel.side_channel import (
SideChannel,
IncomingMessage,
OutgoingMessage,
)
import numpy as np
import uuid

def __init__(self) -> None:
super().__init__(uuid.UUID("621f0a70-4f87-11ea-a6bf-784f4387d1f7"))
def on_message_received(self, data: bytes) -> None:
def on_message_received(self, msg: IncomingMessage) -> None:
Note :We must implement this method of the SideChannel interface to
Note: We must implement this method of the SideChannel interface to
# We simply print the data received interpreted as ascii
print(data.decode("ascii"))
# We simply read a string from the message and print it.
print(msg.read_string())
# Convert the string to ascii
bytes_data = data.encode("ascii")
# Add the string to an OutgoingMessage
msg = OutgoingMessage()
msg.write_string(data)
super().queue_message_to_send(bytes_data)
super().queue_message_to_send(msg)
```

55
ml-agents-envs/mlagents_envs/side_channel/side_channel.py


class OutgoingMessage:
"""
Utility class for forming the message that is written to a SideChannel.
All data is written in little-endian format using the struct module.
"""
"""
Create an OutgoingMessage with an empty buffer.
"""
"""
Append a boolean value.
"""
"""
Append an integer value.
"""
"""
Append a float value. It will be truncated to 32-bit precision.
"""
"""
Append a list of float values. They will be truncated to 32-bit precision.
"""
"""
Append a string value. Internally, it will be encoded to ascii, and the
encoded length will also be written to the message.
"""
"""
Set the internal buffer to a new bytearray. This will overwrite any existing data.
:param buffer:
:return:
"""
if self.buffer:
logger.warning(
"Called set_raw_bytes but the message already has been written to. This will overwrite data."

class IncomingMessage:
"""
Utility class for reading the message written to a SideChannel.
Values must be read in the order they were written.
"""
"""
Create a new IncomingMessage from the bytes.
"""
"""
Read a boolean value from the message buffer.
"""
"""
Read an integer value from the message buffer.
"""
"""
Read a float value from the message buffer.
"""
"""
Read a list of float values from the message buffer.
"""
list_len = self.read_int32()
output = []
for _ in range(list_len):

def read_string(self) -> str:
"""
Read a string value from the message buffer.
"""
encoded_str_len = self.read_int32()
val = self.buffer[self.offset : self.offset + encoded_str_len].decode("ascii")
self.offset += encoded_str_len

"""
Get a copy of the internal bytes used by the message.
"""
return bytearray(self.buffer)
正在加载...
取消
保存