IoT Hub Event Grid integration is creating two "Disconnected" events instead of a "Connected" and "Disconnected" event - azure-iot-hub

I am using IoT Hub's Event Grid integration to monitor device connections and disconnections and am finding that I receive 2 "Disconnected" events and no "Connected" events. I am currently routing the Events to an Azure Function. Please let me know if I have configured it correctly.
IoT Hub Configuration:
Azure Function Code:
public static class IoTConnectionTrigger
{
//public static void Run([EventGridTrigger]EventGridEvent eventGridEvent, ILogger log)
[FunctionName("IoTConnectionTrigger")]
public static void Run([EventGridTrigger]JObject eventGridEvent, ILogger log)
{
log.LogInformation(eventGridEvent.ToString(Formatting.Indented));
}
}
}
Code used to create the IoT Hub message:
static void Main(string[] args)
{
for (int i=0; i<10; i++)
{
DeviceClient client = DeviceClient.CreateFromConnectionString("HostName=<IoT Hub Name>.azure-devices.net;DeviceId=SampleDevice;SharedAccessKey=<key>");
// Create JSON message
var telemetryDataPoint = new
{
Temperature = 50,
Location = "Calgary",
TimeStamp = DateTime.Now
};
var messageString = JsonConvert.SerializeObject(telemetryDataPoint);
Message message = new Message(Encoding.UTF8.GetBytes(messageString));
message.ContentType = "application/json";
message.ContentEncoding = "UTF-8";
try
{
client.SendEventAsync(message).Wait();
Console.WriteLine("Sent!");
} catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
Console.WriteLine("Complete");
}
Output: (Note the two "Microsoft.Devices.DeviceDisconnected" event types, and that no "Connected" events were raised)
Executing 'IoTConnectionTrigger' (Reason='EventGrid trigger fired at 2022-03-18T16:15:16.3717212+00:00', Id=84001f7e-6743-4847-ba73-48b597af33fa)
2022-03-18T16:15:16.372 [Information] {
"id": "de252911-9e70-b66f-954a-641d2c508040",
"topic": "/SUBSCRIPTIONS/7965FC25-694A-478C-B4FA-911F94239D30/RESOURCEGROUPS/TMRESOURCEGROUP/PROVIDERS/MICROSOFT.DEVICES/IOTHUBS/TMIOTHUB",
"subject": "devices/SampleDevice",
"eventType": "Microsoft.Devices.DeviceDisconnected",
"data": {
"deviceConnectionStateEventInfo": {
"sequenceNumber": "000000000000000001D80D890755FFF30000004500000000000000000000000B"
},
"hubName": "<IoT Hub Name>",
"deviceId": "SampleDevice"
},
"dataVersion": "",
"metadataVersion": "1",
"eventTime": "2022-03-18T16:14:36.8975903Z"
}
2022-03-18T16:15:16.372 [Information] Executed 'IoTConnectionTrigger' (Succeeded, Id=84001f7e-6743-4847-ba73-48b597af33fa, Duration=1ms)
2022-03-18T16:15:16.371 [Information] Executing 'IoTConnectionTrigger' (Reason='EventGrid trigger fired at 2022-03-18T16:15:16.3717212+00:00', Id=84001f7e-6743-4847-ba73-48b597af33fa)
2022-03-18T16:15:16.372 [Information] {
"id": "de252911-9e70-b66f-954a-641d2c508040",
"topic": "/SUBSCRIPTIONS/7965FC25-694A-478C-B4FA-911F94239D30/RESOURCEGROUPS/TMRESOURCEGROUP/PROVIDERS/MICROSOFT.DEVICES/IOTHUBS/TMIOTHUB",
"subject": "devices/SampleDevice",
"eventType": "Microsoft.Devices.DeviceDisconnected",
"data": {
"deviceConnectionStateEventInfo": {
"sequenceNumber": "000000000000000001D80D890755FFF30000004500000000000000000000000B"
},
"hubName": "<IoT Hub Name>",
"deviceId": "SampleDevice"
},
"dataVersion": "",
"metadataVersion": "1",
"eventTime": "2022-03-18T16:14:36.8975903Z"

Once a device is registered, the Hub start polling device status at a rate of about once per minute. So if a device is connected for 5 minutes, you should expect 5 reported connection events. If the device then disconnects for the next 3 minutes, you should expect 3 disconnection events. Because the sample is only taken once per minute, if a device manages to connect and then disconnect within a minute, the reported connection status may never actually report that it was connected. The way it works is documented here: https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-event-grid#limitations-for-device-connected-and-device-disconnected-events

Related

Can't serialize GeoJson coordinates in .Net Core controller

I have a .net 6 API project that exports FeatureCollections. I'm using 'NetTopologySuite.IO.GeoJSON' version 2.0.4, and have a public API call like:
public async Task<IActionResult> ExportGeoJSON()
{
GeoJSON.Net.Feature.FeatureCollection ret = new GeoJSON.Net.Feature.FeatureCollection();
const double lat = -73.697913;
const double lon = 50.659193;
GeoJSON.Net.Geometry.Position coord = new GeoJSON.Net.Geometry.Position(lat, lon);
GeoJSON.Net.Geometry.Point pt = new GeoJSON.Net.Geometry.Point(coord);
GeoJSON.Net.Feature.Feature feat = new GeoJSON.Net.Feature.Feature(pt, null, Guid.NewGuid().ToString());
ret.Features.Add(feat);
return Ok(ret);
}
When i call this, i get back only:
{
"Type": "FeatureCollection",
"Features": [
{
"Type": "Feature",
"Id": "465f399d-b45c-47ed-b9e6-f395cd86b84b",
"Geometry": {
"Type": "Point"
}
},...
Ok, i look around and find out about GeoJSON4STJ https://github.com/NetTopologySuite/NetTopologySuite.IO.GeoJSON , so I put that into my Startup:
services.AddControllers()
.AddJsonOptions(opts =>
{
opts.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
opts.JsonSerializerOptions.PropertyNamingPolicy = null;
opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
opts.JsonSerializerOptions.Converters.Add(new NetTopologySuite.IO.Converters.GeoJsonConverterFactory());
});
and run it again, but no change at all. Exactly the same response. Am i missing something?
It seems that you are mixing libraries. NetTopologySuite.IO.GeoJSON (and NetTopologySuite.IO.GeoJSON4STJ) works with features from NetTopologySuite.Features package not from GeoJSON.Net.

Consumer with message selector not working

I have a simple consumer:
try
{
factory = new NMSConnectionFactory(Settings.Endpoint);
connection = factory.CreateConnection(Settings.UserName, Settings.Password);
connection.ClientId = Settings.Name;
session = connection.CreateSession(AcknowledgementMode.AutoAcknowledge);
destination = SessionUtil.GetDestination(session, Settings.QueueName, DestinationType.Queue);
consumer = session.CreateConsumer(destination, "portCode = 'GB'", false);
consumer.Listener += new MessageListener(OnMessage);
}
catch
{
throw;
}
I need to apply a selector to get messages when the portCode field is equal to "GB".
This queue receives many messages.
The message is in JSON and a sample of this message is shown below:
{
"message": {
"list": [
{
xxxxxxx
}
]
},
"header": {
"messageCode": "xxxxxx",
"portCode": "GB",
"sourceSystem": "origin",
"messageId": "ca0bf0e0-cefa-4f5a-a80a-b518e7d2f645",
"dateTimeMessage": "2021-04-22T07:12:48.000-0300",
"version": "1.0"
}
}
However, I do not receive messages using the specified "GB" selector.
It seems simple to define selectors, but it is not working for me.
Thanks.
Selectors do not work on the body of the message (i.e. your JSON data). They only work on the headers and properties of the message.

Store single json from azure iot hub to datalake2

I added iot hub and devices. All data from iot hub is saved to data lake 2 in json format. Works fine but if there are several messages at once from device, it is saved in a single json. It causes some troubles... Is there a way to save each message-event in a separate json? I've looked through settings of iot hub but found nothing.
There is no such as settings for always forwarding a single message to the storage in the IoT Hub routing mechanism. Basically this requirement can be implemented by azure function either in the stream pipeline consumer (IoTHubTrigger) or in the event grid subscriber (EventGridTrigger).
Update:
The following is an example of the IoTHubTrigger function with an output blob binding to the container of the Data Lake Storage Gen2:
run.csx:
#r "Microsoft.Azure.EventHubs"
#r "Newtonsoft.Json"
#r "Microsoft.WindowsAzure.Storage"
using System;
using System.IO;
using System.Text;
using System.Linq;
using Microsoft.Azure.EventHubs;
using Microsoft.WindowsAzure.Storage.Blob;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public static async Task Run(EventData ed, CloudBlockBlob outputBlob, ILogger log)
{
//log.LogInformation($"DeviceId = {ed.SystemProperties["iothub-connection-device-id"]}\r\n{JObject.Parse(Encoding.ASCII.GetString(ed.Body))}");
var msg = new {
EnqueuedTimeUtc = ed.SystemProperties["iothub-enqueuedtime"],
Properties = ed.Properties,
SystemProperties = new {
connectionDeviceId = ed.SystemProperties["iothub-connection-device-id"],
connectionAuthMethod = ed.SystemProperties["iothub-connection-auth-method"],
connectionDeviceGenerationId = ed.SystemProperties["iothub-connection-auth-generation-id"],
enqueuedTime = ed.SystemProperties["iothub-enqueuedtime"]
},
Body = JObject.Parse(Encoding.ASCII.GetString(ed.Body))
};
byte[] buffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(msg));
await outputBlob.UploadFromStreamAsync(new MemoryStream(buffer));
await Task.CompletedTask;
}
function.json:
{
"bindings": [
{
"name": "ed",
"connection": "rk2020iot_IOTHUB",
"eventHubName": "rk2020iot_IOTHUBNAME",
"consumerGroup": "function",
"cardinality": "one",
"direction": "in",
"type": "eventHubTrigger"
},
{
"name": "outputBlob",
"path": "iot/rk2020iot/{DateTime}.json",
"connection": "rk2020datalake2_STORAGE",
"direction": "out",
"type": "blob"
}
]
}

How to synchronize Azure Mobile Services Offline Sync from an Xamarin app

previously, there was the possibility of creating an app services in azure that allowed you to connect an SQL database through the creation of "Easy Tables" but this will be deleted on November 11 (https://aka.ms/easydeprecation), but you can no longer add more tables this way, but you have to do it by the App Service Editor (preliminary version).
I can create the table as the link attached says, the problem is that when I synchronize my data from my xamarin app it says that the resource does not exist or has been removed, that it has been changed or that it is temporarily unavailable.
I think the problem is some configuration or package or extension that I must install in this new app services but I cannot identify it.
My code C #
public async Task SyncAllAsync(bool SyncForce = false)
{
ReadOnlyCollection<MobileServiceTableOperationError> syncErrors = null;
long PendingChanges = CurrentClient.SyncContext.PendingOperations;
try
{
await CurrentClient.SyncContext.PushAsync();
await PatientTable.PullAsync("SyncPatientAsync", PatientTable.CreateQuery());
}
catch (MobileServicePushFailedException exc)
{
if (exc.PushResult != null)
{
syncErrors = exc.PushResult.Errors;
}
}
// Simple error/conflict handling. A real application would handle the various errors like network conditions,
// server conflicts and others via the IMobileServiceSyncHandler.
if (syncErrors != null)
{
foreach (MobileServiceTableOperationError error in syncErrors)
{
if (error.OperationKind == MobileServiceTableOperationKind.Update && error.Result != null)
{
//Update failed, reverting to server's copy.
await error.CancelAndUpdateItemAsync(error.Result);
}
else
{
// Discard local change.
await error.CancelAndDiscardItemAsync();
}
string message = "Error executing sync operation. Item: " + error.TableName + " (" + error.Item["id"] + "). Operation discarded.";
Debug.WriteLine(message);
}
}
}
Patient.json
{
"softDelete" : true,
"autoIncrement": false,
"insert": {
"access": "anonymous"
},
"update": {
"access": "anonymous"
},
"delete": {
"access": "anonymous"
},
"read": {
"access": "anonymous"
},
"undelete": {
"access": "anonymous"
}}
Patient.js
var table = module.exports = require('azure-mobile-apps').table();
// table.read(function (context) {
// return context.execute();
// });
// table.read.use(customMiddleware, table.operation);
The Easy Tables is just a Web API - each table is at https://yoursite.azurewebsites.net/tables/yourtable and the pull operation basically does something like GET https://yoursite.azurewebsites.net/tables/yourtable?filter=(UpdatedAt ge datetimeoffset'some-iso-date'). Enable logging on your Xamarin host (details here plus the end of the same file) to see the actual HTTP requests that are happening.
The error you are receiving is probably a 404. Common issues:
You specified http instead of https in the client
The name of the table is wrong

WebRTC - Failed to set remote answer sdp: Called in wrong state: STATE_INPROGRESS

I'm following the example here: https://www.w3.org/TR/webrtc/#simple-peer-to-peer-example
I've modified the code because I only need one-way streaming:
var configuration = null; //{ "iceServers": [{ "urls": "stuns:stun.example.org" }] };
var peerConnection;
var outboundPeerStream = null;
var outboundPeerStreamSessionId = null;
var createPeerConnection = function () {
if (peerConnection)
return;
peerConnection = new RTCPeerConnection(configuration);
// send any ice candidates to the other peer
peerConnection.onicecandidate = function (event) {
signalrModule.sendClientNotification(JSON.stringify({ "candidate": event.candidate }));
};
// let the "negotiationneeded" event trigger offer generation
peerConnection.onnegotiationneeded = peerStreamingModule.sendOfferToPeers;
// once remote track arrives, show it in the remote video element
peerConnection.ontrack = function (event) {
var inboundPeerStream = event.streams[0];
remoteStreamHelper.pushStreamToDom(inboundPeerStream, foo);
}
}
// this gets called either on negotiationNeeded and every 30s to ensure all peers have the offer from the stream originator
peerStreamingModule.sendOfferToPeers = function () {
peerConnection.createOffer().then(function (offer) {
return peerConnection.setLocalDescription(offer);
}).then(function () {
// send the offer to the other peer
signalrModule.sendClientNotification(JSON.stringify({ "desc": peerConnection.localDescription}));
}).catch(logger.internalLog);
};
// this gets called by the stream originator when the stream is available to initiate streaming to peers
peerStreamingModule.initializeWithStream = function (outboundStream, sessionId) {
outboundPeerStream = outboundStream;
outboundPeerStreamSessionId = sessionId;
createPeerConnection();
peerConnection.addStream(outboundStream);
//peerStreamingModule.sendOfferToPeers(); I don't think I need this...
}
peerStreamingModule.handleP2PEvent = function (notification) {
if (!peerConnection)
createPeerConnection();
if (notification.desc) {
var desc = notification.desc;
// if we get an offer, we need to reply with an answer
if (desc.type == "offer") {
peerConnection.setRemoteDescription(desc).then(function () {
return peerConnection.createAnswer();
}).then(function (answer) {
return peerConnection.setLocalDescription(answer);
}).then(function () {
signalrModule.sendClientNotification(JSON.stringify({ "desc": peerConnection.localDescription, "sessionId": sessionManager.thisSession().deviceSessionId() }), app.username());
}).catch(logger.internalLog);
} else if (desc.type == "answer") {
peerConnection.setRemoteDescription(desc).catch(logger.internalLog);
} else {
logger.internalLog("Unsupported SDP type. Your code may differ here.");
}
} else
pc.addIceCandidate(notification.candidate).catch(logger.internalLog);
}
This seems to be working, but I'm stumped with two parts:
1) WebRTC - Failed to set remote answer sdp: Called in wrong state: STATE_INPROGRESS - this is appearing in my logs from time to time - am I doing something wrong in the above that is causing this?
2) Am I correctly implementing sendOfferToPeers and initializeWithStream? I'm afraid that the sendOfferToPeers getting triggered on interval from the originator isn't how the spec is intended to be used; my goal is to ensure that all peers eventually receive an offer no matter when they join or whether or not they're facing connectivity issues that drop the original offer / negotiation.
// this gets called either on negotiationNeeded and every 30s to ensure all peers have the offer
You can't send the same offer to multiple peers. It's peer-to-peer, not peer-to-peers. One-to-many requires at minimum a connection per participant, and probably a media server to scale.
Also, SDP is not for discovery. The offer/answer exchange is a fragile state-machine negotiation between two end-points only, to set up a single connection.
You should solve who's connecting with whom ahead of establishing the WebRTC connection.