使用MASA全家桶从零开始搭建IoT平台(二)设备注册

news/2024/11/2 2:24:41/

前言

我们不希望任何设备都可以接入我们的IoT平台,所以一个设备正常的接入流程是这样的,
1、上位机软件通过串口或其他方式读取设备的唯一标识码UUID
2、上位机调用IoT后台接口,发送UUIDProductID
3、后台接口判断设备是否注册过,如果没有注册过,就根据ProductID并按照一定规律生成DeviceNamePassword通过接口返回给上位机软件。
4、上位机软件通过串口将接口返回的数据写入设备。

一、设备注册流程

这里主要涉及四个概念
1、UUID(设备唯一ID,一般为设备主控板编号)
2、ProductID(设备所属产品ID,在IoT后台定义)
3、DeviceName(设备在IoT平台或MQTT的名称,该名称大多与产品相关)
4、Password(设备连接MQTT的密码)

二、MQTT注册

1.在EMQX中添加认证方式

选择Built-in Database方式,内置数据库进行密码认证

账号类型选择username,加密方式和加盐方式可以保持默认。

点击创建后可以在认证菜单中看到新建的认证方式,状态为:已连接。

我们点击用户管理->添加 可以手动创建用户

这里的场景我们是通过上位机调用IoT后端,IoT接口内部调用EMQX接口来实现自动创建用户的

2.创建Api Key

调用接口需要认证,这里我们使用Api key的方式,我们在系统设置->API密钥中创建一个API密钥

Secret Key 只有创建的时候才会显示明文,我们需要记录下API Key 和 Secret Key

3.调用接口创建用户

我们在浏览器打开EMQX 的RestAPI swagger

http://localhost:18083/api-docs/index.html

我们可以通过这个接口来创建用户,这里的Authenticator ID 就是我们上面创建的内置数据库 Password Based的ID,

这个ID的获取通过下面的authentication方法获取

我们在认证中直接使用API Key 和 Secret Key,接口返回Id:password_based:built_in_database

调用authentication的Post接口,在 id字段输入:password_based:built_in_database,Request body中输入设备的user_id和password即可成功创建用户。

我们在Deshboard的界面中也可以看到刚刚创建的用户

三、测试设备连接

我们使用MQTTX来模拟客户端设备通过mqtt协议连接到EMQX,新建连接,填写地址、端口、和刚刚通过Api创建用户名密码。

点击连接、发现设备已经可以正常连接mqtt了。

在Dashboard中也可以看到当前连接的客户端ID等信息。

四、编写代码

在MASA.IoT.WebApi项目种添加DeviceController控制器并添加DeviceRegAsync方法用于设备注册,设备如果没有注册过(UUID 数据库不存在),那么会根据ProductCode按照规律生成设备名称,名称以该产品供应商编号开头,后跟时间和序号。然后向EMQX添加设备,并同时存储到数据库中。
如果设备已经注册过,那么直接从数据库取出设备注册信息返回。
代码编写相对简单,不过多赘述。

//DeviceController
namespace MASA.IoT.WebApi.Controllers
{[Route("api/[controller]")][ApiController]public class DeviceController : ControllerBase{private readonly IDeviceHandler _deviceHandler;public DeviceController(IDeviceHandler deviceHandler){_deviceHandler = deviceHandler;}[HttpPost]public async Task<DeviceRegResponse> DeviceRegAsync(DeviceRegRequest request){return await _deviceHandler.DeviceRegAsync(request);}}
}
//DeviceHandler
using MASA.IoT.WebApi.Contract;
using MASA.IoT.WebApi.IHandler;
using MASA.IoT.WebApi.Models.Models;
using Microsoft.EntityFrameworkCore;namespace MASA.IoT.WebApi.Handler
{public class DeviceHandler : IDeviceHandler{private readonly MASAIoTContext _ioTDbContext;private readonly IMqttHandler _mqttHandler;public DeviceHandler(MASAIoTContext ioTDbContext, IMqttHandler mqttHandler){_ioTDbContext = ioTDbContext;_mqttHandler = mqttHandler;}/// <summary>/// 注册设备/// </summary>/// <param name="request"></param>/// <returns>/// 设备注册信息/// </returns>public async Task<DeviceRegResponse> DeviceRegAsync(DeviceRegRequest request){var productInfo =await _ioTDbContext.IoTProductInfo.FirstOrDefaultAsync(o => o.ProductCode == request.ProductCode);if (productInfo == null){return new DeviceRegResponse{Succeed = false,ErrMsg = "ProductCode not found"};}var deviceRegInfo = await GetDeviceRegInfoAsync(request);if (deviceRegInfo != null) //已经注册过{return deviceRegInfo;}else //没有注册过{var deviceName = await GenerateDeviceNameAsync(productInfo.SupplyNo, request.ProductCode, request.UUID);var password = Guid.NewGuid().ToString("N");var addDeviceResponse = await _mqttHandler.DeviceRegAsync(deviceName, password);if (addDeviceResponse.user_id == deviceName) //注册成功{deviceRegInfo = new DeviceRegResponse{DeviceName = deviceName,Password = password,Succeed = true,ErrMsg = string.Empty};await _ioTDbContext.IoTDeviceInfo.AddAsync(new IoTDeviceInfo{Id = Guid.NewGuid(),DeviceName = deviceName,Password = password,ProductInfoId = productInfo.Id,});await _ioTDbContext.SaveChangesAsync();return deviceRegInfo;}return new DeviceRegResponse{Succeed = false,ErrMsg = addDeviceResponse.message};}}/// <summary>/// 获取设备注册信息/// </summary>/// <param name="request"></param>/// <returns>/// 设备已经注册返回设备注册信息,没有注册过返回null/// </returns>private async Task<DeviceRegResponse?> GetDeviceRegInfoAsync(DeviceRegRequest request){var deviceware = await _ioTDbContext.IoTDevicewares.FirstOrDefaultAsync(o => o.ProductCode == request.ProductCode && o.UUID == request.UUID);if (deviceware == null){return null;}else{var deviceInfo = await _ioTDbContext.IoTDeviceInfo.FirstAsync(o => o.DeviceName == deviceware.DeviceName);return new DeviceRegResponse{DeviceName = deviceInfo.DeviceName,Password = deviceInfo.Password,Succeed = true,ErrMsg = string.Empty};}}/// <summary>/// 生成设备名称/// </summary>/// <param name="supplyNo"></param>/// <param name="productCode"></param>/// <param name="uuid"></param>/// <returns>/// 设备Mqtt名称/// </returns>private async Task<string> GenerateDeviceNameAsync(string supplyNo, string productCode, string uuid){var lastDeviceware = await _ioTDbContext.IoTDevicewares.Where(o => o.ProductCode == productCode).OrderByDescending(o => o.CreationTime).FirstOrDefaultAsync();var newDeviceware = new IoTDevicewares{Id = Guid.NewGuid(),UUID = uuid,ProductCode = productCode,CreationTime = DateTime.Now};if (lastDeviceware != null && lastDeviceware.DeviceName.StartsWith(supplyNo + DateTime.Today.ToString("yyyyMMdd"))){newDeviceware.DeviceName = (long.Parse(lastDeviceware.DeviceName) + 1).ToString();}else{newDeviceware.DeviceName = supplyNo + DateTime.Today.ToString("yyyyMMdd") + "0001";}await _ioTDbContext.IoTDevicewares.AddAsync(newDeviceware);await _ioTDbContext.SaveChangesAsync();return newDeviceware.DeviceName;}}
}

这里生成设备名称用了一个简单的算法

// MqttHandler
using Flurl.Http;
using MASA.IoT.WebApi.Contract.Mqtt;
using MASA.IoT.WebApi.IHandler;
using Microsoft.Extensions.Options;
using System.Net;namespace MASA.IoT.WebApi.Handler
{public class MqttHandler : IMqttHandler{private readonly AppSettings _appSettings;public MqttHandler(IOptions<AppSettings> settings){_appSettings = settings.Value;}public async Task<AddDeviceResponse> DeviceRegAsync(string deviceName,string password){var url = $"{_appSettings.MqttSetting.Url}/api/v5/authentication/password_based:built_in_database/users";var response = await url.WithBasicAuth(_appSettings.MqttSetting.ApiKey, _appSettings.MqttSetting.SecretKey).AllowAnyHttpStatus().PostJsonAsync(new AddDeviceRequest{user_id = deviceName,password = password,});if (response.StatusCode is (int)HttpStatusCode.Created or (int)HttpStatusCode.BadRequest or (int)HttpStatusCode.NotFound){return await response.GetJsonAsync<AddDeviceResponse>();}else{throw new UserFriendlyException(await response.GetStringAsync());}}}
}

总结

以上就是本文要讲的内容,本文介绍了通过账号密码的方式通过接口在EMQX中创建用户,并连接EMQX的过程,EMQX支持的认账方式还有很多,例如JWT认证方式可以授权一次性密码认证,可以控制认证的有效期,我们在后面的章节具体应用中会进行说明。

完整代码在这里:https://github.com/sunday866/MASA.IoT-Training-Demos

如果你对我们的 MASA 感兴趣,无论是代码贡献、使用、提 Issue,欢迎联系我们

WeChat:MasaStackTechOps
QQ:7424099


http://www.ppmy.cn/news/60485.html

相关文章

Envoy控制面实践

简介 Envoy 是一款由 Lyft 开源的&#xff0c;使用 C 编写的 L7 代理和通信总线&#xff0c;目前是 CNCF 旗下的开源项目且已经毕业&#xff0c;代码托管在 GitHub 上&#xff0c;它也是 Istio 服务网格中默认的数据平面。关于 Envoy 的详情请阅读 Envoy 中文文档。Envoy 本身无…

PyTorch:深度学习框架的优雅演进与设计理念

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

JavaScript (七) -- JavaScript 事件(需要了解的事件的运用)

目录 具体方法: 1. onmouseover事件的使用(鼠标悬浮事件) onmouseover事件的代码演示:

人脉社交社群运营系统源码

人脉社交社群运营需要注意以下几个方面&#xff1a; 社群定位&#xff1a;要明确社群的目标人群、服务内容和特点&#xff0c;以便吸引到符合社群需求的用户。 内容策划&#xff1a;需要提供丰富、有趣、有价值的内容&#xff0c;如文章、图片、视频等&#xff0c;以增…

协程实现原理

大家好&#xff0c;我是易安&#xff01;今天我们来探讨一个问题&#xff0c;Go 协程的实现原理。此“协程”非彼”携程“。 线程实现模型 讲协程之前&#xff0c;我们先看下线程的模型。 实现线程主要有三种方式&#xff1a;轻量级进程和内核线程一对一相互映射实现的1:1线程…

『python爬虫』异常错误:request状态码是200,但是使用full xpath路径解析返回得到是空列表(保姆级图文)

目录 xpath 与 full xpath错因分析解决办法总结 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 xpath 与 full xpath xpath&#xff1a;相对路径full xpath &#xff1a;绝对路径 错因分析 可能是因为某些网页有特…

STM32F4_十进制和BCD码的转换

目录 前言 1. BCD码 2. BCD码和十进制转换的算法 前言 最近在学习STM32单片机&#xff08;不仅仅是32&#xff09;的RTC实时时钟系统的过程中&#xff0c;需要配置时钟的时间、日期&#xff1b;这些都需要实现BCD码和十进制之间进行转换。这里和大家一起学习BCD码和十进制之…

怎么成为一名架构师?架构师第一步。基层开发人员逆袭成为架构师真的很难吗?

文章目录 写在前面一、企业需要什么样的架构师1、从招聘软件上了解2、架构师的主要职责与能力 二、成为一名架构师很难吗1、架构师的定义2、当前大部分开发团队的现状3、为什么要有架构师4、技术人员如何自我突破 三、晨钟暮鼓的几句话 写在前面 一个团队中&#xff0c;每个人…