Building Azure IoT Gateway Modules in .NET
January 26, 2018 ยท View on GitHub
Overview
This document describes the high level design of the .NET binding mechanism used by Azure IoT Gateway SDK. It describes how the .NET CLR is hosted in the gateway process's memory and how the interaction between the native and .NET Managed Modules will work.
Design

.NET Module Host
The .NET Module Host is a C module that
-
Creates a .NET CLR instance;
-
Brokers calls to the .NET Module (create, destroy, receive).
-
Brokers calls from the .NET Module (dotnetHost_PublishMessage, which invokes
MessageCreate_FromByteArrayandBroker_Publish);
###JSON Configuration
The JSON configuration for .NET Module will be similar to the configuration for Node and Java Module Host:
{
"modules": [
{
"name": "csharp_hello_world",
"loader": {
"name": "dotnet",
"entrypoint": {
"assembly.name": "mymoduleassembly",
"entry.type": "mycsharpmodule.classname"
}
},
"args": "module configuration"
}
]
}
-
nameis the name of the module created (.NET Module) that will be stored by the gateway and used internally; -
loaderis the configuration specifically for the .NET Loader.2.1
loader->assembly.name: Name of the managed assembly that contains the module implementation;2.2
loader->entry.type: Fully qualified name of the managed class that implementsIGatewayModule; -
argsThe value of this property is used to supply configuration information specific to a given .NET module. The value is passed as a byte[] to .NET module and it shall be converted as a UTF-8 String;
##Native methods description
Module_Create
When the .NET Module Host's Module_Create function is invoked by the
gateway process, it:
-
Creates a CLR instance;
-
Loads the .NET module from the path indicated on the configuration (assembly_name) into Default App domain, by calling
ExecuteInDefaultAppDomain, invokes default constructor at the class (entry_type) and callsCreatemethod, implemented by theIGatewayModuleinterface;
Module_Start
When the .NET Module Host's Module_Start function is invoked by the
gateway, it:
- Checks to see if the .NET module has implemented the
Startmethod. - If defined, invokes the
Startmethod implemented by the .NET module.
Module_Receive
When the .NET Module Host's Module_Receive function is invoked by the
gateway process, it:
- Serializes (by calling
Message_ToByteArraythe message content and properties and invokes theReceivemethod implemented by the .NET module (IGatewayInterfacebelow). The .NET module will deserialize this byte_array into a Message object.
Module_Destroy
When the .NET Module Host's Module_Destroy function is invoked by the
gateway, it:
- Releases resources allocated and calls the
Destroymethod implemented by the .NET Module.
.NET Wrappers and objects
This is going to be a layer written in .NET that will wrap a method in our host that is responsible to publish a given message. For .NET Modules the following wrappers will be provided:
-
Message- Object that represents a message; -
Broker- Object that represents the broker, which passes messages between modules; -
IGatewayModule- interface that has to be implemented by the .NET Module; -
nativeDotNetHostWrapper- Uses DLLImport to marshal call to dotnetHost_PublishMessage. This will be transparent to the .NET User, it will be called by the Broker Class when the user calls Publish.
The high level design of these objects and interfaces is documented below:
Message
namespace Microsoft.Azure.IoT.Gateway
{
/// <summary> Object that represents a message passed between modules. </summary>
public class Message
{
public byte[] Content { set; get; };
public Dictionary<string,string> Properties { set; get; };
public Message();
public Message(byte[] msgInByteArray);
public Message(string content, Dictionary<string, string> properties);
public Message(Message message);
public byte[] ToByteArray();
}
}
Broker
namespace Microsoft.Azure.IoT.Gateway
{
/// <summary> Object that represents the message broker, to which messsages will be published. </summary>
public class Broker
{
/// <summary>
/// Publish a message to the message broker.
/// </summary>
/// <param name="message">Object representing the message to be published to the broker.</param>
/// <returns></returns>
public void Publish(Message message);
}
}
IGatewayModule
/// <summary> Interface to be implemented by the .NET Module </summary>
public interface IGatewayModule
{
/// <summary>
/// Creates a module using the specified configuration connecting to the specified message broker.
/// </summary>
/// <param name="broker">The broker to which this module will connect.</param>
/// <param name="configuration">A byte[] with user-defined configuration for this module. This parameter shall be enconded to a UTF-8 String.</param>
/// <returns></returns>
void Create(Broker broker, byte[] configuration);
/// <summary>
/// Disposes of the resources allocated by/for this module.
/// </summary>
/// <returns></returns>
void Destroy();
/// <summary>
/// The module's callback function that is called upon message receipt.
/// </summary>
/// <param name="received_message">The message being sent to the module.</param>
/// <returns></returns>
void Receive(Message received_message);
}
/// <summary> Optional Start Interface to be implemented by the .NET Module </summary>
public interface IGatewayModuleStart
{
/// <summary>
/// Informs module the gateway is ready to send and receive messages.
/// </summary>
/// <returns></returns>
void Start();
}
Flow Diagram
Following is the flow diagram of a lifecycle of the .NET module:
