Azure IotHub Module's twin property change detection - c#

I have an Azure IOThub that contains one edge device. Within this edge device I have several modules running and I can change the twin property of any individual module by connecting to it with it's connection string.
Now I would like the module to do something when it's twin property is changed but the module doesn't have access to it's connection string and it shouldn't because it shouldn't need to connect to itself.
How can a module detect it's twin property change without having a connection string?
I have followed this tutorial but this uses a connection string to detect changes: https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-csharp-csharp-module-twin-getstarted#create-a-module-identity
Here's the code for this module:
using System;
using Microsoft.Azure.Devices.Client;
using Microsoft.Azure.Devices.Shared;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace DataSyncService
{
class Program
{
private const string ModuleConnectionString = "CONNECTION STRING";
private static ModuleClient Client = null;
static void ConnectionStatusChangeHandler(ConnectionStatus status,
ConnectionStatusChangeReason reason)
{
Console.WriteLine("Connection Status Changed to {0}; the reason is {1}",
status, reason);
}
static void Main(string[] args)
{
Microsoft.Azure.Devices.Client.TransportType transport =
Microsoft.Azure.Devices.Client.TransportType.Amqp;
try
{
Client =
ModuleClient.CreateFromConnectionString(ModuleConnectionString, transport);
Client.SetConnectionStatusChangesHandler(ConnectionStatusChangeHandler);
// I want to set this callback but this requires a client and the client requires
// a connection string.
Client.SetDesiredPropertyUpdateCallbackAsync(OnDesiredPropertyChanged, null).Wait();
Console.WriteLine("Retrieving twin");
var twinTask = Client.GetTwinAsync();
twinTask.Wait();
var twin = twinTask.Result;
Console.WriteLine(JsonConvert.SerializeObject(twin.Properties));
Console.WriteLine("Sending app start time as reported property");
TwinCollection reportedProperties = new TwinCollection();
reportedProperties["DateTimeLastAppLaunch"] = DateTime.Now;
Client.UpdateReportedPropertiesAsync(reportedProperties);
}
catch (AggregateException ex)
{
Console.WriteLine("Error in sample: {0}", ex);
}
Console.WriteLine("Waiting for Events. Press enter to exit...");
Console.ReadLine();
Client.CloseAsync().Wait();
}
private static async Task OnDesiredPropertyChanged(TwinCollection desiredProperties,
object userContext)
{
Console.WriteLine("desired property change:");
Console.WriteLine(JsonConvert.SerializeObject(desiredProperties));
Console.WriteLine("Sending current time as reported property");
TwinCollection reportedProperties = new TwinCollection
{
["DateTimeLastDesiredPropertyChangeReceived"] = DateTime.Now
};
await Client.UpdateReportedPropertiesAsync(reportedProperties).ConfigureAwait(false);
}
}
}

When you create a new module in Visual Studio Code, it generates a template module for you that will show you how it's done. I'll include the important bit below:
static async Task Init()
{
MqttTransportSettings mqttSetting = new MqttTransportSettings(TransportType.Mqtt_Tcp_Only);
ITransportSettings[] settings = { mqttSetting };
// Open a connection to the Edge runtime
ModuleClient ioTHubModuleClient = await ModuleClient.CreateFromEnvironmentAsync(settings);
await ioTHubModuleClient.OpenAsync();
Console.WriteLine("IoT Hub module client initialized.");
// Register callback to be called when a message is received by the module
await ioTHubModuleClient.SetInputMessageHandlerAsync("input1", PipeMessage, ioTHubModuleClient);
}
The way this works is because the Azure IoT Edge runtime will create the module as a Docker container with the connection settings as environment variables. The module client uses those variables to connect to IoT Hub when you call
ModuleClient ioTHubModuleClient = await
ModuleClient.CreateFromEnvironmentAsync(settings);
There is a good sample on this Microsoft tutorial that also covers listening for Twin updates.

Related

0xC0000008: An invalid handle was specified in Hololens 2 using NetQM

I'm trying to send some data to a Python server through a ZMQ Socket. The Python server creates a socket using ZMQ as well.
```lang-python
print("Connecting with Unity Toolkit...")
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind('tcp://*:5555')
while True:
request = socket.recv_multipart()
print("Somethings received");
req = json.loads(request[1])
print("Received from Unity Toolkit: ", req)
```
I've tried to communicate with that server using a Python ZMQ client and it works, but when I try to do it with the .NET ZMQ library inside of an Unity 3D project, there is an exception that I don't know how to handle. The NETQMClient I was given (this is legacy code from an older project, but I'm allowed to change it) does something like this:
```lang-csharp
//this lines are executed using a Thread()
AsyncIO.ForceDotNet.Force();
requestSocket = new RequestSocket();
requestSocket.Connect("tcp://192.162.0.104:5555");
isAvailable = true;
while (!clientStopped){
//Debug.Log("Continuing");
}
requestSocket.Close();
NetMQConfig.Cleanup();
//end of the function running on a thread
//...
//later, when a button is pressed
try {
//endpoint is a one-character string that
//the server uses as a cue for something
requestSocket.SendMoreFrame(endpoint);
requestSocket.SendFrame(request); //this a class turned into a JSON using JsonUtility.ToJson
} catch (System.Exception) {
Debug.Log("Something went wrong");
throw;
}
```
Later the C# script waits for a response and I handle that response and use it. The thing is that I've tested it running the client and the server in the same computer and it works. HOWEVER, when I deploy the project into the real Hololens 2 (where the NETQM client runs), I get this error in Visual Studio.
I added the try catch block to see if there was some strange behaviour but there is no Exception in that point. Any hints on the right directions are appreciated.
EDIT: Whole Scripts used in this project for replicating the bug.
This class holds the data that will later be converted to JSON
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public static class Serialization {
public int numPanels;
public bool colorHarmony;
public float colorfulness;
[Serializable]
public Request(int numPanels, bool colorHarmony, float colorfulness) {
this.numPanels = numPanels;;
this.colorHarmony = colorHarmony;
this.colorfulness = colorfulness;
}
}
This is the script with the ZMQ logic
using System.Collections.Generic;
using System.Threading;
using NetMQ;
using NetMQ.Sockets;
using UnityEngine;
public class PythonNetworking {
private bool clientStopped;
private RequestSocket requestSocket;
private byte[] frame;
// for now only one request at a time is supported
public string requestResult;
private bool isAvailable;
public PythonNetworking() {
clientStopped = false;
var clientThread = new Thread(NetMQClient);
clientThread.Start();
}
public void StopClient() {
clientStopped = true;
}
// ReSharper disable once InconsistentNaming
private void NetMQClient() {
AsyncIO.ForceDotNet.Force();
requestSocket = new RequestSocket();
// Computer running the server which must receive the info
requestSocket.Connect("tcp://192.168.0.104:5555");
isAvailable = true;
while (!clientStopped)
{
//Debug.Log("Continuing");
}
requestSocket.Close();
NetMQConfig.Cleanup();
}
public void SetFrame(byte[] currFrame) {
frame = currFrame;
}
// Create queue of requests in case multiple have to be handled
private void SimpleRequest(string endpoint, string request) {
// wait until socket is available
while (!isAvailable) {
//Debug.Log("Socket unavailable");
}
isAvailable = false;
if (request == null) {
requestSocket.SendFrame(endpoint);
} else {
Debug.Log("Sending to Python server: " + request);
try
{
requestSocket.SendMoreFrame(endpoint);
Debug.Log("SendMoreFrame called");
requestSocket.SendFrame(request);
Debug.Log("SendFrame called");
}
catch (System.Exception)
{
Debug.Log("Something went wrong");
throw;
}
}
var msg = requestSocket.ReceiveFrameBytes();
Debug.Log("ReceiveFrameBytes returned a value");
isAvailable = true;
requestResult = System.Text.Encoding.UTF8.GetString(msg);
}
public void PerformRequest(string endpoint, string request) {
requestResult = null;
var requestThread = new Thread(() => SimpleRequest(endpoint, request));
requestThread.Start();
}
}
This is the script making the request when an event is triggered:
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using Microsoft.MixedReality.Toolkit.Input;
using Microsoft.MixedReality.Toolkit.UI;
using Newtonsoft.Json;
public class MyTool: MonoBehaviour {
private PythonNetworking pythonNetworking;
public struct PanelConstraints {
public string name;
public float height;
public float width;
}
public void Start() {
pythonNetworking = new PythonNetworking();
Debug.Log("Tool initialized!");
//called here for debugging purposes, this method is called when a button is pressed
SubmitConstraints();
}
//When a button is pressed
public void SubmitConstraints() {
Debug.Log("Submitting constraints...");
StartCoroutine(CreateRequest("P"));
}
private IEnumerator CreateRequest(string type) {
Serialization.Request request = new Serialization.Request(1, false, 1.0f);
var requestJson = JsonUtility.ToJson(request);
pythonNetworking.PerformRequest(type, requestJson);
yield return new WaitUntil(() => pythonNetworking.requestResult != null);
if (type == "P") {
panelData = JsonConvert.DeserializeObject<List<string>>(pythonNetworking.requestResult);
}
}
}
Finally, the Python server code is:
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind('tcp://*:5555')
while True:
request = socket.recv_multipart()
print("Somethings received");
req = json.loads(request[1])
#Data is processed
As per the setup, I'm using Unity 2019.4.26f1, I create a 3D scene, I add a GameObject to it, I attach the script MyTool.cs to it and I attach a button under the previous GameObject. On clicking the button, the SubmitContrainst() method is called (now called automatically in Start() for debugging purposes). This scene is deployed in the Hololens 2 using Visual Studio 2019 (although I've tried to run it in the Game Mode, communicating with a different computer to recreate the deployment environment, that is, with the client and the server in different devices and with different implementations of the library). When the application is about to start in the Hololens, the exception I introduced previously (see screenshot) is thrown. The Python server, running with Python version 3.10.4, doesn't receive anything. TCP communication seems to work, since when I run two Python scripts in different hosts performing client/server communication with ZMQ (using REQ and REP), it works, but I get the feeling this communication doesn't work on the Hololens 2. But this feels very strange because I've managed to communicate the Hololens 2 with another device (using another library, Windows.Networking.Socket).
Would you navigate to [Unity] -> [Player Settings] -> [Publishing Settings] to check the Capability of your project and make sure Internet Client or Internet Client Server is enabled according to the purpose. It can also be reviewed and modified in the manifest file of C# project.
The code around the thrown exception is more like Unity generated, you need to add break point to see if this issue is caused by a failed socket connection.
In addtion, you may also refer to the discussion on Web Socket Client Disconnecting Exception Being Thrown in UWP Builds - Unity Forum.

Handle network issues when wi-fi is switched off

I am testing .NET version of gRPC to understand how to handle network failures. I put the server to one external machine and debugging the client. The server ticks with a message onnce a second and the client just shows it on the console. So when I stop my local Wi-Fi connection for seconds, then gRPC engine automatically recovers and I even get remaining values. However, if I disable Wi-Fi for longer time like a minute, then it just gets stuck. I don't even get any exceptions so that I can just handle this case and recover manually. This scenario works fine when I close the server app manually, then an exception will occur on the client. This is what I have on the client:
static async Task Main(string[] args)
{
try
{
await Subscribe();
}
catch (Exception)
{
Console.WriteLine("Fail");
Thread.Sleep(1000);
await Main(args);
}
Console.ReadLine();
}
private static async Task Subscribe()
{
using var channel = GrpcChannel.ForAddress("http://x.x.x.x:5555");
var client = new Greeter.GreeterClient(channel);
var replies = client.GerReplies(new HelloRequest { Message = "Test" });
while (await replies.ResponseStream.MoveNext(CancellationToken.None))
{
Console.WriteLine(replies.ResponseStream.Current.Message);
}
Console.WriteLine("Completed");
}
This works when the server app stopped but it doesn't work if I just disable loca Wi-Fi connection on the client side. How can I handle such a case and similar ones?
I've managed to solve it by KeepAlivePingDelay setting:
var handler = new SocketsHttpHandler
{
KeepAlivePingDelay = TimeSpan.FromSeconds(5),
KeepAlivePingTimeout = TimeSpan.FromSeconds(5),
};
using var channel = GrpcChannel.ForAddress("http://x.x.x.x:5555", new GrpcChannelOptions
{
HttpHandler = handler
});
This configuration force gRPC fail after 10 seconds in case of no connection.

C# Azure ServiceBus / Azure SQL - works locally, does not seem to work when deployed to Azure

Just starting to work with Azure.
Have a simple C# .NET Core app, which connects to Azure ServiceBus, reads messages, and writes them to Azure SQL database.
Works locally just fine - connects to remote Azure Service Bus Queue, reads messages, connect to remote Azure SQL db, writes records.
Same exact app, when deployed to Azure as a WebApp, appears to "run", but no longer reads messages from Services Bus, and no longer writes anything to Azure SQL.
Here is the entire app (i.e. Program.cs):
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
using System.Data.SqlClient;
namespace ServiceBusReader
{
class Program
{
const string ServiceBusConnectionString = "SB_CONNECTION_STRING";
const string QueueName = "BasicQueue";
static IQueueClient queueClient;
static SqlConnection connection = null;
public static async Task Main(string[] args)
{
System.Diagnostics.Trace.TraceError("Inside Main function...");
queueClient = new QueueClient(ServiceBusConnectionString, QueueName);
Console.WriteLine("======================================================");
System.Diagnostics.Trace.TraceError("======================================================");
Console.WriteLine("Press ENTER key to exit after receiving all the messages.");
Console.WriteLine("======================================================");
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
builder.DataSource = "XXXX.database.windows.net";
builder.UserID = "USERID";
builder.Password = "PASSWORD";
builder.InitialCatalog = "mySampleDatabase";
connection = new SqlConnection(builder.ConnectionString);
connection.Open();
// Register the queue message handler and receive messages in a loop
RegisterOnMessageHandlerAndReceiveMessages();
Console.ReadKey();
await queueClient.CloseAsync();
}
static void RegisterOnMessageHandlerAndReceiveMessages()
{
// Configure the message handler options in terms of exception handling, number of concurrent messages to deliver, etc.
var messageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
{
// Maximum number of concurrent calls to the callback ProcessMessagesAsync(), set to 1 for simplicity.
// Set it according to how many messages the application wants to process in parallel.
MaxConcurrentCalls = 1,
// Indicates whether the message pump should automatically complete the messages after returning from user callback.
// False below indicates the complete operation is handled by the user callback as in ProcessMessagesAsync().
AutoComplete = false
};
// Register the function that processes messages.
queueClient.RegisterMessageHandler(ProcessMessagesAsync, messageHandlerOptions);
}
static async Task ProcessMessagesAsync(Message message, CancellationToken token)
{
// Process the message.
Console.WriteLine($"Received message: SequenceNumber:{message.SystemProperties.SequenceNumber} Body:{Encoding.UTF8.GetString(message.Body)}");
string query = "INSERT INTO [SalesLT].[messages] (message) VALUES(#Message)";
SqlCommand cmd = new SqlCommand(query, connection);
System.Diagnostics.Trace.TraceError(Encoding.UTF8.GetString(message.Body));
cmd.Parameters.AddWithValue("#Message", Encoding.UTF8.GetString(message.Body));
cmd.ExecuteNonQuery();
Console.WriteLine("Records Inserted Successfully...");
System.Diagnostics.Trace.TraceError("Records Inserted Successfully...");
// Complete the message so that it is not received again.
// This can be done only if the queue Client is created in ReceiveMode.PeekLock mode (which is the default).
await queueClient.CompleteAsync(message.SystemProperties.LockToken);
// Note: Use the cancellationToken passed as necessary to determine if the queueClient has already been closed.
// If queueClient has already been closed, you can choose to not call CompleteAsync() or AbandonAsync() etc.
// to avoid unnecessary exceptions.
}
// Use this handler to examine the exceptions received on the message pump.
static Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs)
{
Console.WriteLine($"Message handler encountered an exception {exceptionReceivedEventArgs.Exception}.");
var context = exceptionReceivedEventArgs.ExceptionReceivedContext;
Console.WriteLine("Exception context for troubleshooting:");
Console.WriteLine($"- Endpoint: {context.Endpoint}");
Console.WriteLine($"- Entity Path: {context.EntityPath}");
Console.WriteLine($"- Executing Action: {context.Action}");
return Task.CompletedTask;
}
}
}
Should I do anything differently in this app in order to make it work in Azure?
It seems that you are trying to deploy a webjob to web app. May I know if you have set it to Continuous type? If you have set it to Continuous, the webjob will automatically run after deployment.
By default, the type is Triggered and you need to manually start the webjob from portal.

Cloud message does not receive on console?

using System;
using System.Text;
using System.Threading.Tasks;
namespace eNtsaIOTMqttApp
{
class Program
{
// you can get DeviceConnectionString from your IoT hub
private const string DeviceConnection = "HostName=eNstaIot.azure-devices.net;DeviceId=GcobaniTesting1;SharedAccessKey=*********";
static ServiceClient serviceClient;
static void Main(string[] args)
{
serviceClient = serviceClient.CreateFromConnectionString(DeviceConnectionString);
Program prog = new Program();
}
public Program() {
DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(DeviceConnectionString);
SendEvent().Wait();
ReceiveCommands(deviceClient).Wait();
}
// This method is responsible for sending Event to Iot Hub.
static async Task SendEvent()
{
string dataBuffer = "IOT in 90 seconds";
Microsoft.Azure.Devices.Message eventMessage = new Microsoft.Azure.Devices.Messages(Encoding.ASCII);
await serviceClient.SendAysnc("GcobaniTesting1", eventMessage);
}
// this method is responbile for receive message on the IOT hub.
async TaskReceiveCommands(DeviceClient deviceClient)
{
Console.WriteLine("\nDevice waiting for IoT hub command...\n");
Microsoft.Azure.Devices.Client.Message receiveMessage;
string messageData;
while (true)
{
receiveMessage = await deviceClient.ReceiveAsync(TimeSpan.FromSeconds(1));
if(receiveMessage != null)
{
messageData = Encoding.ASCII.GetString(receiveMessage.GetBytes());
Console.WriteLine("\t{0}> Message received: {1}", DateTime.Now.ToLocalTime(),messageData);
await deviceClient.CompleteAsync(receiveMessage);
}
}
}
}
}
Why am i not receive the message on my console when i am running this console application? I do get iot device, but when i send message from iot hub i dont get on visual studio.
is there some useful link to this rule?
This line is wrong:
serviceClient = serviceClient.CreateFromConnectionString(DeviceConnectionString);
You must use the service-side connection string to create the ServiceClient, for example the IoTHubOwner connection string. You are trying to create your service client with the device connection string. This should actually throw an error.
See here for guidance on connection strings: https://devblogs.microsoft.com/iotdev/understand-different-connection-strings-in-azure-iot-hub/

MailKit - SmtpClient Connect method freezes

I'm developing a Xamarin android app where at some point (after a successful transaction) the app must send an information email to the customer. The whole process of creating the MimeMessage instance as well as the actual sending of it is done through a task. The message (which most of the time contains linked images) is created flawlessly but as soon as I call a method to actually send it the execution freezes at that call and the whole task remains active but frozen at that point.
Here's my SendMail method which, beside of the MimeMessage parameter, receives a MailAccount class with information about the smtp machine, port and sender account. As soon as a call is made to the Connect method of the MailKit's SmtpClient instance I'm having the "frozen" behavior I described. I tried the whole thing many times - not even once the code continued beyond that line.
private static int SendMail(MailAccount account, MimeMessage message)
{
using (var client = new SmtpClient())
{
try
{
client.Connect(account.Server, account.Port, false);
}
catch (SmtpCommandException)
{
return -1;
}
catch (SmtpProtocolException)
{
return -2;
}
client.AuthenticationMechanisms.Remove("XOAUTH2");
if (client.Capabilities.HasFlag(SmtpCapabilities.Authentication))
{
try
{
client.Authenticate(account.User, Decrypt(account.Pass));
}
catch (AuthenticationException)
{
return -3;
}
catch (SmtpCommandException)
{
return -1;
}
catch (SmtpProtocolException)
{
return -2;
}
}
int status = 1; //assume success
try
{
client.Send(message);
}
catch (SmtpCommandException ex)
{
switch (ex.ErrorCode)
{
case SmtpErrorCode.MessageNotAccepted:
status = -6;
break;
case SmtpErrorCode.SenderNotAccepted:
status = -5;
break;
case SmtpErrorCode.RecipientNotAccepted:
status = -4;
break;
default:
status = -7;
break;
}
}
client.Disconnect(true);
return status;
}
}
In general, a hang while calling client.Connect suggests that the host or port that you are trying to connect with is not available. Check that the host and port are correct.
If they are correct, make sure that you are using the latest version of MailKit as some older versions used Socket.BeginConnect (string, int ...) which, on IPv6-enabled OS's, would sometimes try to use IPv4 over IPv6 and hang in some situations (such as when a virus scanner like Avast was used).
Newer versions of MailKit (>= 2.1.4 and pre-2.0 versions) do a DNS lookup of the remote host and manually specify the correct address family to use when instantiating the Socket (IPv4 vs IPv6) so as to avoid this problem.
It's also possible that the hang is caused by SSL/TLS certificate revocation checks with an unresponsive Certificate Authority. You can disable certificate revocation checks using the following code snippet:
client.CheckCertificateRevocation = false;
Just make sure to set that property before making the call to Connect.
It may be worth testing access to your SMTP server by creating a new Console application in Visual Studio and pasting in the following code and checking that it works:
using System;
using System.Threading;
using System.Threading.Tasks;
using MailKit;
using MailKit.Net.Smtp;
namespace ConsoleApp {
public class Program
{
static void Main (string[] args)
{
using (var client = new SmtpClient (new ProtocolLogger ("smtp.log"))) {
using (var cts = new CancellationTokenSource (60000)) {
try {
client.Connect ("your-host.com", 25, false, cts.Token);
client.Disconnect (true);
} catch (Exception ex) {
Console.WriteLine ("Error connecting: {0}", ex.Message);
Console.WriteLine (ex.StackTrace);
}
}
}
}
}
}
It would also be interesting to know if your Android app has problems connecting to smtp.gmail.com on port 465 (useSsl = true).

Categories

Resources