本文介绍:

Azure Function和SignalR Service 向 Web 前端推送IoT 报警信息 (C#)

 

 

视频介绍:

 

图文介绍:

注意,本文在  《实现Azure Function 通过IoT Hub Trigger将遥测消息写入SQL数据库(C#)》的基础上继续进行。

 

本文参照案例:https://docs.microsoft.com/zh-cn/azure/azure-signalr/signalr-quickstart-azure-functions-csharp

本文使用的示例代码:https://github.com/Azure-Samples/signalr-service-quickstart-serverless-chat

 

创建SignalR Service:

 

填写信息,完成创建:

 

找到SignalR 连接字符串:

 

修改上一讲的示例代码如下:

using IoTHubTrigger = Microsoft.Azure.WebJobs.EventHubTriggerAttribute;

using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Azure.EventHubs;
using System.Text;
using System.Net.Http;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

using System.Data;
using System.Data.SqlClient;
using System;
using Newtonsoft.Json;

using System.Net;
using System.IO;


using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;

namespace Company.Function
{
    public static class iothubtriggertodb
    {
        private static HttpClient client = new HttpClient();

        [FunctionName("iothubtriggertodb")]
        public static void Run(
            [IoTHubTrigger("messages/events", Connection = "IotHubEventHubString")]EventData message,
            [SignalR(HubName = "chat")] IAsyncCollector<SignalRMessage> signalRMessages,
            ILogger log)
        {
            log.LogInformation($"C# IoT Hub trigger function processed a message: {Encoding.UTF8.GetString(message.Body.Array)}");

            var deviceid=message.SystemProperties["iothub-connection-device-id"].ToString();

            
            var dbstring=System.Environment.GetEnvironmentVariable("SQLConn");
            
            Telemetry tmsg = JsonConvert.DeserializeObject<Telemetry>(Encoding.UTF8.GetString(message.Body.Array));



            tmsg.funcsavedt = DateTime.Now;
            tmsg.deviceid = deviceid;

            try
            {

                //blow code are using to save data to sql db

                using (SqlConnection con = new SqlConnection(dbstring))
                {
                    con.Open();
                    if (con.State == ConnectionState.Open)
                    {
                        string strCmd = $"insert into dbo.Telemetry(temperature,humidity,funcsavedt,deviceid) values ({tmsg.temperature},{tmsg.humidity},'{System.DateTime.Now}','{deviceid}' )";


                        SqlCommand sqlcmd = new SqlCommand(strCmd, con);
                        int n = sqlcmd.ExecuteNonQuery();
                        if (n > 0)
                        {
                            log.LogInformation("save to db successfully");
                        }
                        else
                        {
                            log.LogError("save to db error");
                        }

                    }
                    con.Close();
                }
            }
            catch (Exception ex)
            {
                log.LogInformation(ex.Message);
            }



           // azure functions output binding, send iot data to signalr, then push to frontend web.
            signalRMessages.AddAsync(
                 new SignalRMessage
                 {
                     // newMessage is a function, web client should handle.
                     Target = "newMessage",
                     Arguments = new[] { new { sender=$"iot hub function from cloud-{DateTime.Now}", text=$"deviceid-{deviceid.Substring(0,10)},temperature:{tmsg.temperature},humidity:{tmsg.humidity}"} }
                 });
        }



        # region these two functions just want to show your how signalr service work 
         
        
        [FunctionName("negotiate")]
        public static SignalRConnectionInfo GetSignalRInfo(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
            [SignalRConnectionInfo(HubName = "chat")] SignalRConnectionInfo connectionInfo)
        {
            return connectionInfo;
        }

        [FunctionName("messages")]
        public static Task SendMessage(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post")] object message,
            [SignalR(HubName = "chat")] IAsyncCollector<SignalRMessage> signalRMessages)
        {
            return signalRMessages.AddAsync(
                new SignalRMessage
                {
                    Target = "newMessage",
                    Arguments = new[] { message }
                });
        }

        #endregion 
    }    



    public class Telemetry
        {
            public string deviceid{get;set;}

            public DateTime funcsavedt{get;set; }
            public double temperature { get; set; }

            public double humidity { get; set; }
    }
}

 

local.setting.json:

其中,如果将HTML部署到本地,则需要将CORS中增加:http://localhost:80或者对应的端口的URL

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "{your webjob storage connection string}",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "AzureSignalRConnectionString": "{your signalr connection string}",
    "SQLConn": "Data Source =XXX.database.windows.net;Initial Catalog =DBName;User Id =xxx;Password =xxx;"
  },
  "Host": {
    "LocalHttpPort": 7071,
    "CORS": "http://localhost:8080,https://azure-samples.github.io",
    "CORSCredentials": true
  }
}

 

ctrl+shift+p,将Functions 发布,

发布完成后,将SignalR的连接字符串配置到Functions中:

如果前端页面采用 https://azure-samples.github.io/signalr-service-quickstart-serverless-chat/demo/chat-v2/ 的示例页面,则需要将其添加到Functions 的CORS配置中:

 

将HTML页面托管到Storage中(本步骤是可选的,也可以部署到您自己的服务器上)

创建一个Storage Account

 

开启Storage Account 的静态网站功能:

注意,其中的主终结点名称即为 要访问的URL。

 

将示例Html页面上传到 Storage Account 中自动生成的$WEB 容器中:

 

配置Functions 的CORS:

将Storage 静态站点的主终结点添加到CORS中,将末尾的"/"去掉,勾选 启用Allow-control-allow-credentials

 

至此,配置部分完成,打开Storage Account 的主终结点,开启IOT 设备,可以看到数据正常推送到也web页面上:

填写Functions 的 URL:

 

如下图,可以看到遥测的温湿度在页面上刷新: