0

I'm trying to receive data from Azure IoT Hub to my Unity app. I'm facing issues about URI.

First the global picture is a raspberry pi sending temperature data to Azure IoT Hub (thanks to Azure Devices IoT Hub Python SDK). My Unity, thanks to the Azure Event Hub.NET SDK connect to the Event hub endpoint of my Azure IoT Hub to receive the temperature data.

I modified the c#-sample code provided by Azure to make it works with Unity. But when I play the app, I've got an error message "Invalid Uri".

Error message : UriFormatException: Invalid URI: The URI scheme is not valid. System.Uri.CreateThis (System.String uri, System.Boolean dontEscape, System.UriKind uriKind) (at :0) System.Uri..ctor (System.String uriString) (at :0) deviceToCloud+d__6.MoveNext () (at Assets/Scripts/deviceToCloud.cs:89) Rethrow as AggregateException: One or more errors occurred. System.Threading.Tasks.Task.ThrowIfExceptional (System.Boolean includeTaskCanceledExceptions) (at <1f0c1ef1ad524c38bbc5536809c46b48>:0) System.Threading.Tasks.Task.Wait (System.Int32 millisecondsTimeout, System.Threading.CancellationToken cancellationToken) (at <1f0c1ef1ad524c38bbc5536809c46b48>:0) System.Threading.Tasks.Task.Wait () (at <1f0c1ef1ad524c38bbc5536809c46b48>:0) deviceToCloud.Start () (at Assets/Scripts/deviceToCloud.cs:121)

Has someone an idea about this error and how to fix it? It is about the endpoint?

Thanks for helping!

Here is the code.



public class deviceToCloud : MonoBehaviour

{
    // Event Hub-compatible endpoint
    private readonly static string s_eventHubsCompatibleEndpoint = "Endpoint=sb://xxx.servicebus.windows.net/;SharedAccessKeyName=xxx;SharedAccessKey=xxx;EntityPath=xxx";

    // Event Hub-compatible name
    private readonly static string s_eventHubsCompatiblePath = "xxx";

    // Keys
    private readonly static string s_iotHubSasKey = "xxx";
    private readonly static string s_iotHubSasKeyName = "xxx";
    private static EventHubClient s_eventHubClient;

    // Asynchronously create a PartitionReceiver for a partition and then start 
    // reading any messages sent from the simulated client.
    private static async Task ReceiveMessagesFromDeviceAsync(string partition, CancellationToken ct)
    {

        // Create the receiver using the default consumer group.
        // For the purposes of this sample, read only messages sent since 
        // the time the receiver is created. Typically, you don't want to skip any messages.
        var eventHubReceiver = s_eventHubClient.CreateReceiver("$Default", partition, EventPosition.FromEnqueuedTime(DateTime.Now));

        while (true)
        {
            if (ct.IsCancellationRequested) break;
            // Check for EventData - this methods times out if there is nothing to retrieve.
            var events = await eventHubReceiver.ReceiveAsync(100);

            // If there is data in the batch, process it.
            if (events == null) continue;

            foreach (EventData eventData in events)
            {
                string data = Encoding.UTF8.GetString(eventData.Body.Array);
                Debug.Log("Message received: " + data);
            }

        }

    }

    //private static async Task Main(string[] args)
    public  async Task ReadD2C()

    {
        Debug.Log("Fonction principale ReadD2C");

        // Create an EventHubClient instance to connect to the
        // IoT Hub Event Hubs-compatible endpoint.
        var connectionString = new EventHubsConnectionStringBuilder(new Uri(s_eventHubsCompatibleEndpoint), s_eventHubsCompatiblePath, s_iotHubSasKeyName, s_iotHubSasKey);
        s_eventHubClient = EventHubClient.CreateFromConnectionString(connectionString.ToString());

        // Create a PartitionReciever for each partition on the hub.
        var runtimeInfo = await s_eventHubClient.GetRuntimeInformationAsync();
        var d2cPartitions = runtimeInfo.PartitionIds;

        CancellationTokenSource cts = new CancellationTokenSource();

        Console.CancelKeyPress += (s, e) =>
        {
            e.Cancel = true;
            cts.Cancel();
            Debug.Log("Exit");
        };

        var tasks = new List<Task>();
        foreach (string partition in d2cPartitions)
        {
            tasks.Add(ReceiveMessagesFromDeviceAsync(partition, cts.Token));
        }

        // Wait for all the PartitionReceivers to finsih.
        Task.WaitAll(tasks.ToArray());
    }

    private void Start()
    {
        Debug.Log("Start");

        ReadD2C().Wait();

    }
}

Anthony
  • 71
  • 1
  • 10

1 Answers1

0

Seems like s_eventHubsCompatibleEndpoint is not a correct address. Maybe try it like this instead?

sb://xxx.servicebus.windows.net/;SharedAccessKeyName=xxx;SharedAccessKey=xxx;EntityPath=xxx

Are you sure this is even the right URL?

HoloLady
  • 1,041
  • 1
  • 12
  • 28
  • When I used your proposition "sb://...", the Unity editor crashed. It may be an improvement. Try to figure out, I think those "threads" functions are making unity crashed. – Anthony May 07 '19 at 13:20
  • Unity does not do well with threads, you need to keep everything extremely seperate. The while(true) is also risky. I wouldn't use Debug.Log in the async method, or anything that's part of the Unity namespace. In these cases it's better not to link them in any way, but to have a bool flag you set to true when unity needs to grab stuff from the other thread. – HoloLady May 07 '19 at 14:00
  • What do you mean by "extremely seperate"? I didn't get it for the moment how works Unity and how works thread... – Anthony May 08 '19 at 12:44
  • Unity isn't made for multithreaded applications, you can do it, but you can't access anything from the UnityEngine in your thread. Unless you're using the [Job System](https://docs.unity3d.com/Manual/JobSystem.html) for this? – HoloLady May 08 '19 at 13:54
  • Okay I understand more. I will take a look on Job System and Thread in Unity , then I will let you know – Anthony May 09 '19 at 06:53
  • I used to understand more about Threading and Unity with this link http://www.stevevermeulen.com/index.php/2017/09/using-async-await-in-unity3d-2017 – Anthony May 10 '19 at 13:45
  • you was right, the while(true) made it crash. I succeed to use threads but still struggling to adapt this while(true). If I don't use it, I just receive 1 message. How would you change this while(true) for? – Anthony May 10 '19 at 13:48
  • You can try to convert it to a [coroutine](https://docs.unity3d.com/Manual/Coroutines.html). But I don't know how well that will go with the rest of your code. But then you can put a `yield return null` in there to wait to continue with the loop until next frame. – HoloLady May 11 '19 at 11:58