物聯網

服務匯流排上的智慧恒溫

Clemens Vasters

下載代碼示例

這是一個大膽的預測: 連接的設備都大生意,並瞭解這些設備將為開發人員不太遠了這條路非常重要。 "很明顯,"你說。 但我不是說,你可能會讀這篇文章的設備。 我的意思是那些會讓你很酷,今年夏天,可説明您洗衣服和盤子,那沖泡咖啡或工廠地板上放在一起的其他設備。

在 6 月發行的 MSDN 雜誌 (msdn.microsoft.com/magazine/jj133825),解釋了一系列的因素,並概述了如何管理事件和命令流動,從和嵌入式 (和移動) 設備使用 Windows Azure 服務匯流排體系結構。 在本文中,我借東西要還邁出的一步,看看代碼,創建並保護這些事件和命令的流動。 嵌入式設備真正瞭解確實需要看看,因為我會生成一個然後鋼絲它到 Windows Azure 服務匯流排因此它可以發送到其目前狀態與相關事件,並將遠端控制的消息 (通過 Windows Azure 雲。

直到幾年前,建設與電源、 微控制器和一套感應器的一種小型設備需要相當多的電子硬體設計中以及在放到一起,更不用提了烙鐵熟練掌握的技能。 我高興地承認我已經親自已相當挑戰硬體部門 — — 這麼多的這樣的一個朋友曾宣稱如果世界遭到外星人的機器人,他會把我送到前線,我只是在場會導致崩潰于電氣短褲盛大煙花的攻擊。 但由於樣機平臺如 Arduino/Netduino 或.net Gadgeteer 的興起,甚至可能會做傷害人和機器擺動烙鐵可以現在一起提出一個功能齊全的小型設備,利用現有的程式設計技巧的人。

要堅持建立中的最後一個問題的方案,我會建造"空調"形式的恒溫控制的風機、 風扇在哪裡佈線的角度最有趣的部分。 該專案的元件基於.net Gadgeteer 模型中,涉及與微控制器、 記憶體和各種可插拔模組的主機板。 為專案主機板是下面的擴展模組地質電子非斯蜘蛛板:

  • 從地質電子
    • 乙太網 J11D 模組提供有線的網路 (wi-fi 無線上網模組存在)
    • USB 用戶端 DP 模組作為電源和 USB 埠進行部署
    • 在設備的直接控制的搖桿
  • 從熔融工作室
    • 溫度和濕度感應器
    • 若要打開或關閉切換風扇的繼電器
    • OLED 顯示,以顯示目前狀態

在一起,這些部件的成本約 230 元。 這是顯然比焊一塊板,相當的元件,但提到這需要焊接嗎? 此外,這是一個剛剛開始走吧,所以預期價格漲到了這個基地擴大的市場。

若要使元件來活著您所需要的 Visual C# 2010年表示 (至少),.net 微框架 SDK 和從地質電子或熔融 Gadgeteer SDK。 一旦你有了這些安裝,發展經驗是 — — 如果您會允許比較級 — — 相當壯觀和為視覺,在 Visual Studio 中,想像可以得到的東西,正如您看到的圖 1

Designing the Device in the .NET Gadgeteer
圖 1 設計.net Gadgeteer 中的設備

圖 1 .NET Gadgeteer 程式的設計檢視顯示在 Visual Studio 中。 我認為這篇文章,與實際的設備的一張照片,包括但照片會做的就是確認該關係圖。 這正是它的外觀。

具有.gadgeteer 副檔名的檔包含 XML 模式,視覺化編輯器中。 從該 XML 檔,模具的 Gadgeteer 自動生成封套的部分程式類的每個模組插入到主機板。 您的代碼坐在 program.cs,然後從持有的程式類,就像你熟悉從其他.net Api 的隱藏模型的另一部分。

這些設備的情況下使用.net 微框架。 它是完全開放原始碼版本的 Microsoft.net 框架已專門創建于小型設備與有限的計算能力和不多的記憶體。 .NET 微框架包含許多熟悉的.net 框架類,但大部分已經通過特色飲食以減少整體代碼足跡。 因為框架是一個圖層上本機硬體的設備和設備並不是處理 (那裡真的是這裡沒有 OS) 的所有硬體抽象的 OS 的通用電腦,您可以使用設備的框架版本取決於支援的前提條件,這顯然非常不同于常規的 pc 體驗的主機板製造商凡遠從東西作為.net 框架為高級別上刪除硬體的特殊性。

還有幾個其他差異與常規的.net Framework 中和一般的 PC 平臺相比,— — 來自 PC 背景 — — 最初令人驚訝。 例如,該設備在這裡沒有一個板載電池。 沒有電池意味著無緩衝的時鐘,因此該設備具有正確的掛鐘時間它醒來的時候不知道。 缺乏 OS 中,使用協力廠商擴展顯示,該設備也不會有板載的字體,您可以使用繪製顯示的字串。 如果您想要顯示的字串,你得要添加的字體來這樣做。

同樣,該設備沒有 prepopulated、 Windows 更新維護憑證存放區區。 如果您想要驗證 SSL/TLS 證書,你必須至少部署到設備的根 CA 的證書 — — 當然你還得有目前時間檢查證書的有效性。 正如你可能已經猜到,證書的處理表示有點障礙對於這些設備,和 SSL/TLS 加密要求計算的努力、 記憶體消耗和代碼並不是所有的設備可以支援它們的占地面積太大。 然而,因為安全顯然變得越來越重要,即使在這個空間設備需要跨互聯網、 通信微.net 4.2 版帶來重大 SSL/TLS 的改進支援設備有足夠的資源來處理它。 我將討論這個問題有點後來更深入。

恒溫功能

實施此示例的本地調溫功能是相當簡單的。 我檢查溫度和濕度對計畫使用感應器和交換器通過的中繼埠關閉或打開一個連接,當溫度低於或高於某一閾值時,風扇。 OLED 螢幕上顯示的目前狀態和操縱杆允許手動調整目標溫度。

當在開始該設備時,我就會事件絲計時器觸發溫度讀數,並從操縱杆讀取事件。 當按下操縱杆時,我暫停計時器、 檢查目標溫度操縱杆位置、 立即請求新的溫度從感應器讀數和恢復計時器。 溫度讀數結束時,TemperatureHumidity­MeasurementComplete 事件獲取提出的感應器。 我然後存儲當前的讀數和調整中繼切換風扇,必要時的狀態。 這就是恒溫的邏輯,而會顯示部分的程度圖 2

圖 2 閱讀溫度和濕度

void WireEvents()
{
  this.InitializeTemperatureSensor();
  this.InitializeJoystick();
}
void InitializeTemperatureSensor()
{
  this.temperatureCheckTimer = new Timer(5000);
  this.temperatureCheckTimer.Tick += (t) =>
    this.temperatureHumidity.RequestMeasurement();
  this.temperatureCheckTimer.Start();
    this.temperatureHumidity.MeasurementComplete 
    += this.TemperatureHumidityMeasurementComplete;
}
void InitializeJoystick()
{
  this.joystick.JoystickPressed += this.JoystickPressed;
}
void JoystickPressed(Joystick sender, Joystick.JoystickState state)
{
  this.temperatureCheckTimer.Stop();
  var jStick = this.joystick.GetJoystickPostion();
  if (jStick.Y < .3 || jStick.X < .3)
  {
    settings.TargetTemperature -= .5;
    StoreSettings(settings);
  }
  else if (jStick.Y > .7 || jStick.X > .7)
  {
    settings.TargetTemperature += .5;
    StoreSettings(settings);
  }
  this.RedrawDisplay();
  this.temperatureHumidity.RequestMeasurement();
  this.temperatureCheckTimer.Start();
}
void TemperatureHumidityMeasurementComplete(TemperatureHumidity sender, 
  double temperature, double relativeHumidity)
{
  var targetTemp = settings.TargetTemperature;
  this.lastTemperatureReading = temperature;
  this.lastHumidityReading = relativeHumidity;
  this.relays.Relay1 = (lastTemperatureReading > targetTemp);
  this.RedrawDisplay();
}

每當我調整目標溫度在 JoystickPressed 方法中的,我在程式類的設置欄位中存儲的新值,並調用 StoreSettings。 設置欄位是 ApplicationSettings 的類型,可序列化的類所擁有的一切設備的設備代碼中需要記住整個重置和電源週期。 要堅持存儲資料,.net 微儲備設備的非易失性記憶體中的某些存儲頁,並提供對此存儲通過 ExtendedWeakReference 類的訪問。 這可能不是直觀直到你承認它是主要是一種機制來交換的資料從主記憶體的壓力下,它方便可兼作存儲功能。 該類擁有弱引用的物件,就像定期 WeakReference 在.net Framework 中,但將交換資料的非易失性存儲而不是放棄它,垃圾回收器一旦。 因為資料獲取換出主記憶體,它需要被序列化存儲,這解釋了為什麼 ApplicationSettings 類 (您將看到用於以後當我們討論資源調配) 需要是可序列化的。

恢復物件從其存儲位置或使用 RecoverOrCreate 方法創建一個新的存儲插槽時,需要指定一個唯一的識別碼。 我只有一個物件來存儲,因此,我將使用一個固定的識別碼 (零)。 存儲該物件並將其恢復一旦被迫回到存儲在 ExtendedWeakReference 實例上使用 PushBackIntoRecoveryList 方法所需要的任何更新後,這就是我做什麼 StoreSettings 沖出來,變化中所示圖 3

圖 3 更新存儲的資料

static ApplicationSettings GetSettings()
{
  var data = ExtendedWeakReference.RecoverOrCreate(
    typeof(ApplicationSettings),
    0,
    ExtendedWeakReference.c_SurviveBoot | 
    ExtendedWeakReference.c_SurvivePowerdown);
  var settings = data.Target as ApplicationSettings;
  if (settings == null)
  {
    data.Target = settings = ApplicationSettings.Defaults;
  }
  return settings;
}
static void StoreSettings(ApplicationSettings settings)
{
  var data = ExtendedWeakReference.RecoverOrCreate(
    typeof(ApplicationSettings),
    0,
    ExtendedWeakReference.c_SurviveBoot | 
    ExtendedWeakReference.c_SurvivePowerdown);
  data.Target = settings;
  data.PushBackIntoRecoverList();
}

提供

一開始,該設備處於"工廠新"狀態 — — 已部署的設備代碼但設備尚未未初始化,因此不會有任何的當前設置。 你可以看到這種狀態時設置物件仍為空,因此已初始化為預設設置,在 GetSettings 方法中反映出來。

因為我想讓溝通並通過互聯網基礎設施的設備 — — Windows Azure 服務匯流排 — — 我需要配備一組憑據以交談的基礎設施,還告訴它跟哪些資源的設備。 這第一步設置廠新設備所需的網路設定和設置匹配的資源,在伺服器端的被稱為資源調配 ; 在上一篇文章中,我為它討論的基本體系結構模型。

設備的代碼中,我將會得到妥善調配的設備相當嚴格,將啟動資源調配的步驟,每次在設備連接到網路時,並沒有一個有效的配置。 為此,我把一個布林值,在設置來告訴我是否過上一個成功的標誌。 如果未設置標誌,我發出對 Windows Azure 中承載的資源調配服務的調用。

負責核查時它產生由服務維護標識的使用其唯一的設備識別碼,在允許清單中註冊該設備的資源調配服務。 一旦啟動設備,它獲取從允許清單中刪除。 要保持相當簡單的這篇文章的事情,不過,我跳過允許清單管理的實施。

一旦設備被認為是合法的資源調配服務,之後在上一篇文章中,建立的模型分配到特定的刻度單位和該刻度單位內的特定的扇出主題的設備。 對於此示例,我要保持簡單,並創建訂閱,為單個固定主題命名為充當從雲變成該設備,命令通道的設備和命名事件要從設備收集的資訊的主題。 除了創建訂閱和將該設備與該主題相關聯,我還在存取控制服務 (Windows Azure Active directory 功能) 中創建的設備的服務標識和授予該標識所需的許可權,將消息發送到事件主題,主題的設備從新創建的訂閱接收消息。 該設備可以執行正是這兩個操作 Windows Azure 服務匯流排上的 — — 而已。

圖 4 顯示的資源調配服務的核心。 該服務依賴于 Windows Azure 服務匯流排管理 API (NamespaceManager) 在核心船舶作為一部分的 Windows Azure sdk 或通過 NuGet 的 Microsoft.ServiceBus.dll 大會中找到。 它還依賴于一個用於管理存取控制帳戶的傭工庫和許可權可作為零件及服務匯流排的授權採樣,當然,也包括在這篇文章的下載代碼。

圖 4 的資源調配服務

namespace BackendWebRole
{
  using System;
  using System.Configuration;
  using System.Linq;
  using System.Net;
  using System.ServiceModel;
  using System.ServiceModel.Web;
  using Microsoft.ServiceBus;
  using Microsoft.ServiceBus.AccessControlExtensions;
  using Microsoft.ServiceBus.Messaging;
  [ServiceContract(Namespace = "")]
  public class ProvisioningService
  {
    const string DevicesTopicPath = "devices";
    const string EventsTopicPath = "events";
    static readonly AccessControlSettings AccessControlSettings;
    static readonly string ManagementKey;
    static readonly string NamespaceName;
    static Random rnd = new Random();
      static ProvisioningService()
      {
        NamespaceName = ConfigurationManager.AppSettings["serviceBusNamespace"];
        ManagementKey = ConfigurationManager.AppSettings["managementKey"];
        AccessControlSettings = new AccessControlSettings(
          NamespaceName, ManagementKey);
      }
      [OperationContract, WebInvoke(Method = "POST", UriTemplate = "/setup")]
      public void SetupDevice()
      {
        var rcx = WebOperationContext.Current.OutgoingResponse;
        var qcx = WebOperationContext.Current.IncomingRequest;
        var id = qcx.Headers["P-DeviceId"];
        if (this.CheckAllowList(id))
        {
          try
          {
            var deviceConfig = new DeviceConfig();
            CreateServiceIdentity(ref deviceConfig);
            CreateAndSecureEntities(ref deviceConfig);
            rcx.Headers["P-DeviceAccount"] = deviceConfig.DeviceAccount;
            rcx.Headers["P-DeviceKey"] = deviceConfig.DeviceKey;
            rcx.Headers["P-DeviceSubscriptionUri"] =
              deviceConfig.DeviceSubscriptionUri;
            rcx.Headers["P-EventSubmissionUri"] = deviceConfig.EventSubmissionUri;
            rcx.StatusCode = HttpStatusCode.OK;
            rcx.SuppressEntityBody = true;
          }
          catch (Exception)
          {
            rcx.StatusCode = HttpStatusCode.InternalServerError;
            rcx.SuppressEntityBody = true;
          }
        }
        else
        {
          rcx.StatusCode = HttpStatusCode.Forbidden;
          rcx.SuppressEntityBody = true;
        }
      }
      static void CreateAndSecureEntities(ref DeviceConfig deviceConfig)
      {
        var namespaceUri = ServiceBusEnvironment.CreateServiceUri(
          Uri.UriSchemeHttps, NamespaceName, string.Empty);
        var nsMgr = new NamespaceManager(namespaceUri,
          TokenProvider.CreateSharedSecretTokenProvider("owner", ManagementKey));
        var ruleDescription = new SqlFilter(
          string.Format("DeviceId='{0}' OR Broadcast=true",
            deviceConfig.DeviceAccount));
        var subscription = nsMgr.CreateSubscription(
          DevicesTopicPath, deviceConfig.DeviceAccount, ruleDescription);
        deviceConfig.EventSubmissionUri = new Uri(
          namespaceUri, EventsTopicPath).AbsoluteUri;
        deviceConfig.DeviceSubscriptionUri =
          new Uri(namespaceUri,
            SubscriptionClient.FormatSubscriptionPath(
              subscription.TopicPath,
              subscription.Name)).AbsoluteUri;
        GrantSendOnEventTopic(deviceConfig);
        GrantListenOnDeviceSubscription(deviceConfig);
      }
      static void GrantSendOnEventTopic(DeviceConfig deviceConfig)
      {
        var settings = new AccessControlSettings(NamespaceName, ManagementKey);
        var topicUri = ServiceBusEnvironment.CreateServiceUri(
          Uri.UriSchemeHttp, NamespaceName, EventsTopicPath);
        var list = NamespaceAccessControl.GetAccessControlList(topicUri, settings);
        var identityReference =
          IdentityReference.CreateServiceIdentityReference(
            deviceConfig.DeviceAccount);
        var existing = list.FirstOrDefault((r) =>
          r.Condition.Equals(identityReference) &&
          r.Right.Equals(ServiceBusRight.Send));
        if (existing == null)
        {
          list.AddRule(identityReference, ServiceBusRight.Send);
          list.SaveChanges();
        }
      }
      static void GrantListenOnDeviceSubscription(DeviceConfig deviceConfig)
      {
        var settings = new AccessControlSettings(NamespaceName, ManagementKey);
        var subscriptionUri = ServiceBusEnvironment.CreateServiceUri(
          Uri.UriSchemeHttp,
          NamespaceName,
          SubscriptionClient.FormatSubscriptionPath(
            DevicesTopicPath, deviceConfig.DeviceAccount));
        var list = NamespaceAccessControl.GetAccessControlList(
          subscriptionUri, settings);
        var identityReference = IdentityReference.CreateServiceIdentityReference(
          deviceConfig.DeviceAccount);
        var existing = list.FirstOrDefault((r) =>
          r.Condition.Equals(identityReference) &&
          r.Right.Equals(ServiceBusRight.Listen));
        if (existing == null)
        {
          list.AddRule(identityReference, ServiceBusRight.Listen);
          list.SaveChanges();
        }
      }
      static void CreateServiceIdentity(ref DeviceConfig deviceConfig)
      {
        var name = Guid.NewGuid().ToString("N");
        var identity =
          AccessControlServiceIdentity.Create(AccessControlSettings, name);
        identity.Save();
        deviceConfig.DeviceAccount = identity.Name;
        deviceConfig.DeviceKey = identity.GetKeyAsBase64();
      }
        bool CheckAllowList(string id)
      {
        return true;
      }
  }
}

該服務包括單個 HTTP 資源,名為 /setup,使用 Windows 通信基礎 (WCF) Web 操作 SetupDevice,它接受的 POST 請求來實現。 您會注意到方法是無參數,也不會返回實體有效載荷。 這就是無事故。 而不是使用 HTTP 實體主體在 XML、 JSON 或表單編碼進行請求和回應的資訊,我製作非常簡單的設備,將有效載荷放在自訂 HTTP 標頭。 這消除了需要特定的分析器分析有效載荷,並使代碼佔用空間小。 HTTP 用戶端已經知道如何分析標頭,並為我想在這裡做的這是很多。

調用的 HTTP 資源的匹配設備代碼所示圖 5,很難想像調用任何比這更簡單的製作。 設備識別碼發送標頭中並通過接頭同樣返回的 post-provisioning 的配置設置。 沒有戲法流,沒有解析,只是簡單的鍵/值對 HTTP 用戶端很容易理解。

圖 5 配置設備

bool PerformProvisioning()
{
  [ ... display status ... ]
  try
  {
    var wr = WebRequest.Create(
      "http://cvdevices.cloudapp.net/Provisioning.svc/setup");
    wr.Method = "POST";
    wr.ContentLength = 0;
    wr.Headers.Add("P-DeviceId", this.deviceId);
    using (var wq = (HttpWebResponse)wr.GetResponse())
    {
      if (wq.StatusCode == HttpStatusCode.OK)
      {
        settings.DeviceAccount = wq.Headers["P-DeviceAccount"];
        settings.DeviceKey = wq.Headers["P-DeviceKey"];
        settings.DeviceSubscriptionUri = new Uri(
          wq.Headers["P-DeviceSubscriptionUri"]);
        settings.EventSubmissionUri = new Uri(
          wq.Headers["P-EventSubmissionUri"]);
        settings.NetworkProvisioningCompleted = true;
        StoreSettings(settings);
        return true;
      }
    }
  }
  catch (Exception e)
  {
    return false;
  }
  return false;
}
void NetworkAvailable(Module.NetworkModule sender,
  Module.NetworkModule.NetworkState state)
{
  ConvertBase64.ToBase64String(ethernet.NetworkSettings.PhysicalAddress);
  if (state == Module.NetworkModule.NetworkState.Up)
  {
    try
    {
      Utility.SetLocalTime(NtpClient.GetNetworkTime());
    }
    catch
    {
      // Swallow any timer exceptions
    }
    if (!settings.NetworkProvisioningCompleted)
    {
      if (!this.PerformProvisioning())
      {
        return;
      }
    }
    if (settings.NetworkProvisioningCompleted)
    {
      this.tokenProvider = new TokenProvider(
        settings.DeviceAccount, settings.DeviceKey);
      this.messagingClient = new MessagingClient(
        settings.EventSubmissionUri, tokenProvider);
    }
  }
}

如果設置指示資源調配是必要的執行­從它的 NetworkAvailable 的功能,當網路是否正常,該設備指派一個 IP 位址,通過 DHCP 獲取觸發資源調配方法被調用。 資源調配完成後,設置用於配置的權杖提供程式和郵件用戶端,談到 Windows Azure 服務匯流排。 您還會注意 NTP 用戶端調用。 NTP 主張"網路時間協定",並簡單、 BSD 許可證 NTP 客戶寫的邁克爾 · 施瓦茨,使該示例獲取目前時間,如果您想要檢查 SSL 憑證的過期日期,則需要借來。

正如您看到的回圖 4,SetupDevices 在允許清單 CheckAllowList 調用類比檢查,並且,如果這就是成功的然後調用 CreateServiceIdentity 和 CreateAndSecureEntities。 CreateServiceIdentity 方法在與為應用程式佈建的 Windows Azure 服務匯流排命名空間關聯的存取控制命名空間中創建一個新的服務標識,以及一個金鑰。 CreateAndSecureEntities 方法通過包括具有布林值 true 的廣播屬性創建新的訂閱該主題,使用 SQL 的規則,允許將消息發送到要通過包括 DeviceId 屬性設置為該設備的帳戶名稱,目標或特定訂閱的主題配置訂閱的設備上的實體或所有訂閱。 已創建訂閱後,該方法調用的授予所需的許可權,對使用存取控制庫的新服務標識實體的 GrantSendOnEventTopic 和 GrantListenOnDeviceSubscription 的方法。

一旦所有的已成功完成,資源調配操作的結果是映射到該請求的 HTTP 回應中的標頭和 OK 狀態碼,返回和設備將結果存儲在非易失性記憶體中 NetworkProvisioningCompleted 設置的標誌。

將事件發送和接收的命令

資源調配完成,現在準備將事件發送到 Windows Azure 服務匯流排事件主題並接收命令從其訂閱主題的設備裝置。 但我到那裡去之前,我要討論的一個敏感問題: 安全。

正如我剛才所說,SSL/TLS 是適用于小型設備昂貴協定套件。 即是說,某些設備不會永遠能夠支援 SSL/TLS,或者他們可能由於計算能力或記憶體限制支援只在有限的時尚。 實際上,雖然在寫這篇文章的時候地質電子非斯蜘蛛主機板基於在這裡使用的.net 微框架 4.1 名義上可以說話 SSL/TLS,因此 HTTPS,其 SSL/TLS 固件顯然不能處理向它提交的 Windows Azure 服務匯流排或存取控制服務的憑證連結。 隨著這些設備的固件獲取更新到新 4.2 版本的.net 微框架,這些限制將消失為此特定的設備,但有些設備是簡單的問題太約束處理 SSL/TLS 仍然是真實的原則,並且有嵌入式的設備社會上不是很像重量級的相應協定選擇積極的討論。

因此,即使設備現在有一個適當的帳戶,它不能得到一個權杖從存取控制服務因為使用 HTTPS 是這樣做的一個先決條件。 同樣是如此入 Windows Azure 服務匯流排,對於要求傳遞一個訪問權杖包括使用佇列和主題的所有交交互操作的所有請求的任務 HTTPS 發送一條消息。 此外,如果此示例是生產代碼,我想,當然,要公開通過 HTTPS 來保護金鑰,它將返回到該設備的資源調配的終結點。

現在該怎麼辦? 好吧,"現在"是最終作出適當權衡,這肯定包括錢 — — 生產過程中的幾個美分的價格差異加起來數以百萬計的特定種類的設備正在進行時。 如果該設備不能處理所需的安全協定,問題是多大的損害,可能引起不具有該協定以及如何關閉該設備缺乏所需的功能和基礎設施的需求之間的差距。

什麼應該清楚的是任何流不是加密和簽名的資料是容易被竊聽和操縱。 當設備報告只感應器資料時,這是值得考慮的網路路徑上的男子在中間操縱是任何人都可以想像有價值還是可以分析檢測到。 結果有時可能發送中清除的資料確定。 指揮和控制路徑是另一回事 ; 作為行為的一種設備可以通過網路遠端控制,我不能認為一宗案件,我不想有保護至少具有完整簽名的通訊路徑。 私隱的指揮和控制的值取決於使用案例。 如果有針對操縱的地方的緩解,設定恒溫目標溫度似乎並不是值得花加密功夫。

配上這篇文章的示例代碼包括到雲計算的通信流的從設備的兩個變種。 第一次是多簡化 Windows Azure 服務匯流排 API,要求 HTTPS 和不會獲取一個存取控制權杖和直接交談 Windows Azure 服務匯流排定期握手。

第二個路徑使用相同的一般形狀的 Windows Azure 服務匯流排 HTTP 協定來發送和接收郵件,但它通過使用金鑰的消息,它包含創建 HMACSHA256 簽名。 這不會偷聽,從保護郵件,但它不會從操縱保護郵件,並允許檢測重播攻擊,包括一個唯一的郵件 id 時。 答覆將使用相同的金鑰簽名。 因為服務匯流排還暫時不支援這種身份驗證模式 — — 儘管這篇文章是一個很好的指標,微軟正在積極思考這個問題 — — 則路徑會使用自訂閘道服務與資源調配服務一起主持。 自訂的閘道檢查帶簽名和 Windows Azure 服務匯流排上的剩餘消息傳遞。 使用自訂的閘道協定翻譯也通常是合適的模型如果您需要的雲計算系統發言的無數的專有設備協定之一。 但需要牢記的自訂閘道方法的一件事是它需要擴展到同時發送的郵件,以便使閘道層非常薄和無國籍人士是個好主意的設備的數量。

要麼密密麻麻 HTTP 或 HTTPS Uri 的資源調配服務最終使得兩個路徑之間的區別。 在設備代碼中,由 TokenProvider 處理差異。 在 HTTPS 案例中,設備談到 Windows Azure 服務匯流排,直,而在 HTTP 案例中,資源調配服務會談到自訂的閘道。 這裡的假設是 HTTP 情況,設備獲取 preprovisioned 不暴露在未受保護的互聯網通訊路徑上的機密金鑰的情況下。 換言之,資源調配服務運行在工廠裡,不是在 Windows Azure。

設備的代碼具有 Windows Azure 服務匯流排的兩個交互: 將事件發送和接收的命令。 我要寄出事件每分鐘一次後取得了新的溫度讀數,和我還將使用這種機會抓取任何掛起的命令,並執行它們。 要做我將會修訂中所示的 TemperatureHumidityMeasurementComplete 方法圖 2,並添加到 SendEvent 和 ProcessCommands 的調用來處理每分鐘一次,如中所示 圖 6

圖 6 將事件發送和接收命令

void TemperatureHumidityMeasurementComplete(TemperatureHumidity sender,
  double temperature, double relativeHumidity)
{
  [...] (see Figure 2)
  if (settings.NetworkProvisioningCompleted &&
    DateTime.UtcNow - settings.LastServerUpdate >
      TimeSpan.FromTicks(TimeSpan.TicksPerMinute))
  {
    settings.LastServerUpdate = DateTime.UtcNow;
    SendEvent(this.lastTemperatureReading, this.lastHumidityReading);
    ProcessCommands();
  }
}
void SendEvent(double d, double lastHumidityReading1)
{
  try
  {
    messagingClient.Send(new SimpleMessage()
      {
        Properties = {
          {"Temperature",d},
          {"Humidity", lastHumidityReading1},
          {"DeviceId", settings.DeviceAccount}
        }
      });
  }
  catch (Exception e)
  {
    Debug.Print(ethernet.ToString());
  }
}
void ProcessCommands()
{
  SimpleMessage cmd = null;
  try
  {
    do
    {
      cmd = messagingClient.Receive(TimeSpan.Zero, ReceiveMode.ReceiveAndDelete);
      if (cmd != null && cmd.Properties.Contains("Command"))
      {
        var commandType = (string)cmd.Properties["Command"];
        switch (commandType)
        {
          case "SetTemperature":
            if (cmd.Properties.Contains("Parameter"))
            {
              this.settings.TargetTemperature =
                double.Parse((string)cmd.Properties["Parameter"]);
              this.RedrawDisplay();
              this.temperatureHumidity.RequestMeasurement();
              StoreSettings(this.settings);
            }
            break;
        }
      }
    }
    while (cmd != null);
  }
  catch (Exception e)
  {
    Debug.Print(e.ToString());
  }
}

SendEvent 方法使用郵件用戶端,獲取初始化後可用的網路連接。 郵件用戶端是一個小版本的 Windows Azure 服務匯流排 API 能夠發送和接收郵件並從服務匯流排佇列、 主題和訂閱。 方法使用同一用戶端讀取的 ProcessCommands 命令從該設備的訂閱並處理它們。 現在,該設備只瞭解 SetTemperature 命令同一個參數,指示絕對溫度,將設置為一個數值 (在攝氏溫度,順便說一下)。 介意 ProcessCommands 指定 TimeSpan.Zero 超時屬性從 Windows Azure 服務匯流排訂閱中,接收消息,表明它不願意等待消息到達。 我想要抓住一條消息,只是如果有一個可用和立即回。 這減少了自訂的閘道的通信 (應使用 HTTP 和有一個地方),並且不需要我繼續接收迴圈設備上打開。 這種權衡是延隔時間。 在最壞的情況下,命令有一分鐘的延遲。 如果這是一個問題,您可以使用的較長超時,導致長時間輪詢 (該圖書館支援),並命令延遲到幾個毫秒,壓低。

匹配的伺服器端,用於接收事件,並將命令發送到已註冊的所有設備,通過將消息放到該主題,只需遵循 Windows Azure 服務匯流排 API 的常規規則,然後是你可以下載,示例代碼的一部分,所以我會省略該代碼在這裡。

總結

這一系列事情互聯網的目標是提供一些洞察種技術我們這裡在微軟啟用連接設備樣機和發展工作。 我們還想要顯示 Windows Azure 服務匯流排和分析技術 (StreamInsight 可以如雲技術如何説明您管理資料流量,從和連接的設備 ; 如何創建大型雲架構處理大的很多設備 ; 和如何從他們聚合和摘要資訊。

我謹此陳的路上,建立一種嵌入式的設備,您可以放在任何家用網路和遠端控制從任何其他網路,這是很酷,如果你問我。

我相信我們在這次旅行非常早期階段。 在談到 Microsoft 客戶從各地,我見過,一個大浪的連接和定制設備是的路上,這是一個很大機會.net 開發人員和創新公司尋求建立雲服務來連接到這些設備,並將它們與其他連接的資產組合以創造性的方式。 讓我們看看你能做什麼。

Clemens Vasters 是在 Windows Azure 服務匯流排團隊的主要技術負責人。Vasters 已從最早的孵化階段和 Windows Azure 服務匯流排,其中包括推式通知和高規模信號為 Web 和設備上的技術特點路線圖工程團隊。他也是一個經常會議議長和體系結構課件作者。跟隨他在 Twitter 上 twitter.com/clemensv

由於以下技術專家,檢討這篇文章: Elio Damaggio, Todd Holmquist-Sutherland, Abhishek Lal, Zach Libby, Colin Miller and Lorenzo Tessiore