IoT的裝置,除了發送訊息至Azure IoT Hub上之外,後端的管理系統也可以透過取得IoT Hub上裝置的清單,判斷裝置目前是否還有連線並取得最後連線的時間
要取得IoT Hub上裝置的清單功能很簡單,但是因為判斷裝置的連線狀態與發送告警通知是屬於監控行為,所以我的作法是將這個程式作成WebJob,並安排每隔幾分鐘執行一次,這樣就能夠有效的監控裝置目前的連線狀態
由於是後端的監控管理,所以接下來的內容程式碼的部份會比較多一些,不過照著步驟就可以完整的作出監控功能
首先,建立一個Azure WebJob的專案,並且命名為[JobHeartBeatProcessor]
在這個WebJob的專案中,加入[Microsoft.Azure.Devices]的Nuget套件
接著在專案中建立一個Models的資料夾,並在該資料夾下建立一個DeviceEntity.cs的類別庫
將下面的程式碼放入至DeviceEntity.cs的類別庫中
public class DeviceEntity : IComparable<DeviceEntity>
{
public string Id { get; set; }
public string PrimaryKey { get; set; }
public string SecondaryKey { get; set; }
public string ConnectionString { get; set; }
public string ConnectionState { get; set; }
public DateTime LastActivityTime { get; set; }
public DateTime LastConnectionStateUpdatedTime { get; set; }
public DateTime LastStateUpdatedTime { get; set; }
public int MessageCount { get; set; }
public string State { get; set; }
public string SuspensionReason { get; set; }
public int CompareTo(DeviceEntity other)
{
return string.Compare(this.Id, other.Id, StringComparison.OrdinalIgnoreCase);
}
public override string ToString()
{
return $"Device ID = {this.Id}, Primary Key = {this.PrimaryKey}, Secondary Key = {this.SecondaryKey}, ConnectionString = {this.ConnectionString}, ConnState = {this.ConnectionState}, ActivityTime = {this.LastActivityTime}, LastConnState = {this.LastConnectionStateUpdatedTime}, LastStateUpdatedTime = {this.LastStateUpdatedTime}, MessageCount = {this.MessageCount}, State = {this.State}, SuspensionReason = {this.SuspensionReason}\r\n";
}
}
這段程式碼主要是在定義從IoT Hub上取回的裝置清單結構內容,也就是說這些屬性將會是從IoT Hub上取回的裝置清單欄位
接著,在專案中建立一個Processors的資料夾,並在資料夾下建立一個[DevicesProcessor.cs]的類別庫
將下面的程式碼,放入至DeviceProcessor.cs的類別庫中
class DevicesProcessor
{
private List<DeviceEntity> listOfDevices;
private RegistryManager registryManager;
private String iotHubConnectionString;
private int maxCountOfDevices;
private String protocolGatewayHostName;
public DevicesProcessor(string iotHubConnenctionString, int devicesCount, string protocolGatewayName)
{
this.listOfDevices = new List<DeviceEntity>();
this.iotHubConnectionString = iotHubConnenctionString;
this.maxCountOfDevices = devicesCount;
this.protocolGatewayHostName = protocolGatewayName;
this.registryManager = RegistryManager.CreateFromConnectionString(iotHubConnectionString);
}
public async Task<List<DeviceEntity>> GetDevices()
{
try
{
DeviceEntity deviceEntity;
var devices = await registryManager.GetDevicesAsync(maxCountOfDevices);
if (devices != null)
{
foreach (var device in devices)
{
deviceEntity = new DeviceEntity()
{
Id = device.Id,
ConnectionState = device.ConnectionState.ToString(),
LastActivityTime = device.LastActivityTime,
LastConnectionStateUpdatedTime = device.ConnectionStateUpdatedTime,
LastStateUpdatedTime = device.StatusUpdatedTime,
MessageCount = device.CloudToDeviceMessageCount,
State = device.Status.ToString(),
SuspensionReason = device.StatusReason
};
if (device.Authentication != null &&
device.Authentication.SymmetricKey != null)
{
deviceEntity.PrimaryKey = device.Authentication.SymmetricKey.PrimaryKey;
deviceEntity.SecondaryKey = device.Authentication.SymmetricKey.SecondaryKey;
}
listOfDevices.Add(deviceEntity);
}
}
}
catch (Exception ex)
{
throw ex;
}
return listOfDevices;
}
}
這段程式碼主要的功能在於GetDevices這個副程式,建立了一個DeviceProcessor的類別物件後,呼叫GetDevices的副程式並取得所有裝置的清單,而裝置清單的Entity就是放在Models資料夾下的DeviceEntity.cs類別庫定義的物件
接著,在專案目錄下建立一個[HeartBeatProcessor.cs]的類別庫
並將下面的程式碼放入至HeartBeatProcessor.cs的類別庫中
public class HeartBeatProcessor
{
static int intMaxCountOfDevices = int.Parse(ConfigurationManager.AppSettings["Microsoft.Azure.IoT.MaxDeviceCount"].ToString());
static string strIoTHubConnectionString = ConfigurationManager.ConnectionStrings["Microsoft.Azure.IoT.ConnectionString"].ToString();
public static async Task Process()
{
// 找出裝置的清單
DevicesProcessor devicesProcessor = new DevicesProcessor(strIoTHubConnectionString, intMaxCountOfDevices, "");
List<DeviceEntity> devicesList = await devicesProcessor.GetDevices();
Console.WriteLine("All Devices Count:" + devicesList.Count.ToString());
// 過濾出離線的裝置資料
List<DeviceEntity> offlineDevices = devicesList.FindAll(
delegate (DeviceEntity device)
{
return device.ConnectionState == "Disconnected" && device.State == "Enabled";
}
);
Console.WriteLine("Offline Devices Count:" + offlineDevices.Count.ToString());
// 取出離線的裝置客戶資料
for (int i = 0; i < offlineDevices.Count; i++)
{
string strDeviceId = offlineDevices[i].Id;
DateTime dtLastStateTime = offlineDevices[i].LastConnectionStateUpdatedTime;
Console.WriteLine($"Device [{offlineDevices[i].Id}] is offline, Last updated time :[{dtLastStateTime.ToString()}]");
/*
在這邊加入通知或是告警的機制,如透過SnedGrid寄發EMail通知
*/
}
}
}
在HeartBeatProcessor.cs的類別庫中,當執行了Process()這個程序後,會呼叫先前建立的DeviceProcessor中的GetDevice()副程式,並在取得裝置清單後先過濾裝置目前的狀態,如果說裝置的[State]欄位為[Enabled],且[ConnectionState]的值為[Disconnected],那麼就必須發送告警的訊息,通知管理者該裝置已經離線了
然後在Program.cs中,將程式碼更改為呼叫HeartBeatProcessor.Process()作為執行的進入點
static void Main()
{
Console.WriteLine("Start Time:" + DateTime.UtcNow.ToString());
HeartBeatProcessor.Process().Wait();
Console.WriteLine("Stop Time:" + DateTime.UtcNow.ToString());
}
程式碼的部份,最後在App.Config檔案裡,加上下面的設定內容
<appSettings>
<!-- // TODO:在這裡修改取得IoT Hub上裝置的最大數 -->
<add key="Microsoft.Azure.IoT.MaxDeviceCount" value="1000000" />
</appSettings>
<connectionStrings>
<!-- // TODO:在這裡修改連線資訊 -->
<add name="AzureWebJobsDashboard" connectionString="[儲存體的連線字串]" />
<add name="AzureWebJobsStorage" connectionString="[儲存體的連線字串]" />
<add name="Microsoft.Azure.IoT.ConnectionString" connectionString="[IoT Hub的連線字串]" />
</connectionStrings>
- Microsoft.Azure.IoT.MaxDeviceCount:這個設定,表示要一次取得IoT Hub上裝置的最大數量,若是要一次取得很大的數量的話可能會影響效率,這部份需要斟酌一下
- Microsoft.Azure.IoT.ConnectionString:在這個連線字串的設定中,請給予有取得裝置清單的IoT Hub連線字串,如iothubowner的角色之類的,取得的方式如下圖所示
程式碼製作完成後,就可以透過發佈的方式,將這個WebJob放上至WebApp中了
由於這個WebJob並不是連續執行,所以可以將設定更改為[依排程執行]或是[視需要執行]
發行完成後,可以在WebApp上看到這一個WebJob
查看執行記錄,可以看到執行過程使用Console.WriteLine所顯示的資訊,代表該工作排程確實有被執行,離線的裝置也有被取得了
透過WebJob搭配從IoT Hub取得離線裝置清單的功能,可以讓系統管理者很清楚的知道哪些裝置目前是在離線狀態,即使將這些裝置用在重要的環境中,也可以馬上得知這個裝置的連線狀態,輕鬆的達成監控的目的