15
votes

Azure IoT Hub Supports AMQP, MQTT, HTTP protocols. In order to customize these protocols we have Azure IoT protocol gateway. I can find good samples on MQTT protocol customization. I need some sample codes for TCP based protocol customization using Azure IoT Protocol Gateway.

EDIT (in order to get an answer): what the OP was asking, is an example using the Azure Protocol Gateway to support a proprietary TCP-based protocol. Currently the IoT hub only supports AMQP, MQTT and HTTP. While those protocols actually rely on TCP, the hub doesn't support direct TCP connection without the extra layer of AMQP, MQTT or HTTP. As explained here, we need a basic example of a custom TCP based protocol.

Imagine a basic device that can only send some proprietary payload through TCP on a given IP address/port: we need an example of a gateway customization that allows this device to send data to the hub.

The current code of the protocol gateway is poorly designed, as it heavily relies on MQTT.

Adding some bounty too.

2
TCP is the underlying communication protocol for all of these message protocols. What are you trying to do? What protocol do you want to implement? Why not use one of the available protocols?Panagiotis Kanavos
My device supports only TCP communication. It sends a sync message over TCP to a static IP and port. I want this device to be virtualized in Azure IoT Hub. Something which is described here. github.com/Azure/azure-iot-protocol-gateway/issues/44arun thatham
@Panagiotis Kanavos Maybe the OP wasn't clear enough, but the question is a real, unanswered question. The OP sure understands how TCP works. To make it more clear, see my edit. In a few words: we need an example for a proprietary TCP-based protocol. IoT hub doesn't allow any TCP-based protocol to communicate with it: only MQTT, AMQP and HTTP. We need a low-level example of a basic TCP-based protocol. Take STOMP as an example if you want.ken2k
@PanagiotisKanavos Forget the load balancer. The question is pretty clear as it is now, we need an example of a TCP-based proprietary protocol. Not some MQTT tweak the Azure Protocol Gateway currently provide.ken2k
@ken2k this is SO, not Github or MSDN or UserVoice. If you can't post your solution as an answer, who can and what do you expect it to look like? BTW if that someone comes this way, he should be able to read all the required info in the question, not the comments. Mentioning STOMP or whatever protocol you used will helpPanagiotis Kanavos

2 Answers

3
votes

The default Protocol Gateway sample are indeed somewhat confusing because of all the MQTT code. The protocol gateway works by 'simulating' a IoTHub connection for each custom protocol device you connect to the gateway.

To do this translation from the TCP device to an IoTHub device you first need to have a connection to the IoTHub on behalf of the device. This is the gateway part. Below is the core essentials for this IoTHubConnection.

namespace GatewayTest
{
    using System;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using DotNetty.Buffers;
    using Microsoft.Azure.Devices.ProtocolGateway.Identity;
    using Microsoft.Azure.Devices.ProtocolGateway.IotHubClient;
    using Microsoft.Azure.Devices.ProtocolGateway.Messaging;

    public class IoTHubConnection : IMessagingChannel<IMessage>
    {
        private readonly string iotHubHostName;
        private readonly Func<IDeviceIdentity, Task<IMessagingServiceClient>> deviceClientFactory;
        private readonly Func<string, Task> onMessage;
        private IMessagingServiceClient deviceClient;
        private IDeviceIdentity deviceIdentity;

        public IoTHubConnection(
            string iotHubHostName,
            Func<IDeviceIdentity, Task<IMessagingServiceClient>> deviceClientFactory,
            Func<string, Task> onMessage)
        {
            this.iotHubHostName = iotHubHostName;
            this.deviceClientFactory = deviceClientFactory;
            this.onMessage = onMessage;
        }

        public event EventHandler CapabilitiesChanged;

        public async Task OpenAsync(string deviceId, string deviceKey)
        {
            this.deviceIdentity = this.GetDeviceIdentity(deviceId, deviceKey);
            if (this.deviceIdentity != UnauthenticatedDeviceIdentity.Instance)
            {
                this.deviceClient = await this.deviceClientFactory(this.deviceIdentity);
                this.deviceClient.BindMessagingChannel(this);
            }
        }

        public async Task CloseAsync()
        {
            await this.deviceClient.DisposeAsync(null);
            this.deviceClient = null;
        }

        public void Handle(IMessage message)
        {
            var messageBody = message.Payload.ToString(Encoding.UTF8);

            this.onMessage(messageBody);

            this.deviceClient.CompleteAsync(message.Id);
        }

        public Task SendMessage(string message)
        {
            var buffer = Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes(message));
            var deviceMessage = this.deviceClient.CreateMessage($"devices/{this.deviceIdentity.Id}/messages/events", buffer);
            return this.deviceClient.SendAsync(deviceMessage);
        }

        protected virtual void OnCapabilitiesChanged(EventArgs e)
        {
            this.CapabilitiesChanged?.Invoke(this, e);
        }

        private IDeviceIdentity GetDeviceIdentity(string userName, string deviceKey)
        {
            IotHubDeviceIdentity ideviceIdentity;
            if (!IotHubDeviceIdentity.TryParse($"{this.iotHubHostName}/{userName}", out ideviceIdentity))
            {
                return UnauthenticatedDeviceIdentity.Instance;
            }

            ideviceIdentity.WithDeviceKey(deviceKey);
            return ideviceIdentity;
        }
    }
}

The deviceClientFactory callback method should be implemented as shown below and in this line in the ProtocolGateway repo in Github.

deviceClientFactory = IotHubClient.PreparePoolFactory(
    "IotHubConnectionString",
    400,
    TimeSpan.FromMinutes(3),
    iotHubClientSettings,
    PooledByteBufferAllocator.Default,
    new ConfigurableMessageAddressConverter("TopicNameConversion"));

When a Tcp Device connects to the protocol, you should create an instance of this IoTHubConnection and send messages from the Device to the IoTHubConnection and vica versa. The code below shows a very simple version of how this should be done.

private const int BufferSize = 1024;
private byte[] buffer = new byte[BufferSize];
private IoTHubConnection ioTHubConnection;
private NetworkStream stream;

private async Task Start()
{
    listener = new TcpListener(IPAddress.Any, port);
    listener.Start();

    var client = await listener.AcceptTcpClientAsync();
    ioTHubConnection = new IoTHubConnection("IoTHubName", deviceClientFactory, OnIoTHubMessage);
    stream = client.GetStream();

    // Read DeviceId and DeviceKey from some sort of StartConnection-message send by the TcpClient.
    await ioTHubConnection.OpenAsync("DeviceId", "DeviceKey");

    stream.BeginRead(buffer, 0, BufferSize, ReadTcpStreamCallback, null);
}

private void ReadTcpStreamCallback(IAsyncResult ar)
{
    var bytesRead = stream.EndRead(ar);

    if (bytesRead > 0)
    {
        var message = System.Text.Encoding.ASCII.GetString(result);

        ioTHubConnection.SendMessage(message);

        // Read again.
        stream.BeginRead(buffer, 0, BufferSize, ReadTcpStreamCallback, null);
    }
}

private async Task OnIoTHubMessage(string message)
{
    // Potentially do some translation on the IoTHub message
    // and send it to the Device

    var byteData = Encoding.UTF8.GetBytes(message);
    stream.BeginWrite(byteData, 0, byteData.Length, SendTcpCallback, null);
}

private void SendTcpCallback(IAsyncResult ar)
{
    stream.EndWrite(ar);
}
0
votes

I know I am late to this conversation. However I have interesting add on or might be a solution for some.

Azure IoT Gateway is now known as Azure IoT Edge, this is clearly mentioned in the following Azure github repo

enter image description here

https://github.com/Azure/iot-edge-modbus.git

On the other hand, Azure IoT Edge supports TCP for some protocols which can be found in the following links

  1. https://docs.microsoft.com/en-us/azure/iot-edge/deploy-modbus-gateway
  2. https://docs.microsoft.com/en-us/azure/iot-edge/iot-edge-as-gateway