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

news2024/12/22 21:00:37

前言

我们不希望任何设备都可以接入我们的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.coloradmin.cn/o/494142.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JavaEE进阶】——第五节.SpringMVC学习介绍(上)(获取参数,传递参数——关于前后端传参交互的总结、from表单、Ajax数据提交)

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;JavaEE进阶 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01; 目录 文章目…

车载红外夜视「升温」

红外夜视赛道&#xff0c;正在升温。 本周&#xff0c;全球车载后视镜头部供应商Gentex宣布&#xff0c;领投以色列热成像技术初创公司ADASKY&#xff0c;后者在B轮融资中拿到了3000万美元。按照计划&#xff0c;Gentex将协助ADASKY将红外夜视技术推向汽车市场。 事实上&#x…

基于3D网格模型的加密域可逆信息隐藏文献学习

————————————————————————————————————————————— 文献学习&#xff1a; 题目&#xff1a;Separable Reversible Data Hiding Based on Integer Mapping and MSB Prediction for Encrypted 3D Mesh Models. 作者&#xff1a;Na Xu…

系统分析师之需求工程(十四)

目录 一、概述 二、需求获取 三、需求分析 3.1 需求分类 3.2 结构化需求分析&#xff08;SA&#xff09; 3.3 面向对象的需求分析OOA 四、需求定义 五、需求验证 六、需求管理 6.1 软件需求基线 6.2 需求跟踪 6.3 需求风险管理 一、概述 软件需求是指用户对系统在功…

产品经理进阶:一份为创业者准备的商业模式作战指南(加餐)

目录 大环境 精益画布 专栏一&#xff1a;产品经理进阶指南 CSDN学院课程地址 课程目录 专栏二&#xff1a;华为流程体系课程 课程地址 课程目录 专栏三&#xff1a;华为BLM战略管理课 目录 大环境 今天来谈谈商业模式这个话题。 2023 年很快就要过半了&#xff0c…

堆(什么是堆以及怎样自己创建堆)

&#x1f381;**“我们不必非常聪明才能改变世界。我们只需要比绝大多数人更早地认识到问题&#xff0c;并付诸行动。”- 毛里茨萨克斯** &#x1f3c0;作者&#xff1a;不能再留遗憾了 &#x1f4f1;关于作者&#xff1a;博主正在学习C和Java&#xff0c;目前有些关于leetcode…

【Halcon】找到设备上的 标识牌

如图&#xff0c;找到设备上的 标识牌 。 标识牌最明显的特征是比其他区域亮&#xff0c; 二值化选择出亮区域&#xff0c;再通过面积选择出目标区域。 先显示图片 *获取图片的大小 get_image_size(Image,Width,Height)*关闭窗口 dev_close_window()*打开窗口 dev_open_win…

let‘s encrypt免费证书配置https

#sudo add-apt-repository ppa:certbot/certbot #sudo apt-get update sudo apt-get install certbot 这里要停止nginx certbot certonly --agree-tos --email xxxqq.com --standalone -d 域名1 -------------------------------------------------------------------------…

MTK8788 安卓智能模块 安卓核心板方案定制

MT8768核心板是一款高性能的芯片板&#xff0c;具备以下特点&#xff1a; 1.强大的处理器&#xff1a;MT8768核心板采用联发科MTK8768平台&#xff0c;搭载八核A53处理器&#xff0c;最高主频可达2.3GHz。除此之外&#xff0c;MT8768核心板还支持多种操作系统&#xff0c;包括A…

SDMTSP:星雀优化算法NOA求解单仓库多旅行商问题(提供MATLAB代码,可更改起点及旅行商个数)

一、单仓库多旅行商问题 单仓库多旅行商问题&#xff08;Single-Depot Multiple Travelling Salesman Problem, SD-MTSP&#xff09;&#xff1a;&#x1d45a;个推销员从同一座中心城市出发&#xff0c;访问其中一定数量的城市并且每座城市只能被某一个推销员访问一次&#x…

webpack loader原理以及自定义loader

loader主要是帮助webpac将不同类型的文件转换为webpack可识别的模块。 分类&#xff1a;enforce属性 pre 前置loader&#xff0c;normal 普通loader&#xff0c;inline&#xff1a;内联loader&#xff0c;post&#xff1a;后置loader 如果不写默认是 normal类型 执行顺序&a…

ChatGPT会代替数据分析师吗?

大家好&#xff0c;我是朱小五。 最近一个多月以来&#xff0c;ChatGPT已经成为了“家喻户晓”的一个词。3月15日&#xff0c;OpenAI 开发布会宣布GPT-4&#xff0c;紧接着百度发布了自己AI产品文心一言。3月23日&#xff0c;OpenAI 宣布推出插件功能&#xff0c;赋予 ChatGPT …

【Linux Network】网络编程套接字(代码练习)—TCP

目录 1. 常用接口 2. 服务器和客户端的简单流程 3. C/S 回声通信 4. 创建子进程完成 C/S 回声通信 5. 创建孙子进程完成 C/S 回声通信 6. 创建线程完成 C/S 回声通信 7. 使用线程池完成 C/S 回声通信 Linux网络编程在✨ 本篇博文的代码虽然多&#xff0c;但都是修改一点点tcp_s…

动态规划--最长公共子序列

最长公共子序列 动态规划算法思想最长公共子序列题目最优解结构性质递归方程递归实现核心函数测试测试结果 非递归实现(画表)核心函数测试测试结果 求出具体的子序列 动态规划算法思想 动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题﹐即将大规模变成…

通付盾携数智反欺诈应用防护解决方案亮相2023金融展

精彩亮相 银行数字化转型需求背景 数据驱动发展 数字经济时代&#xff0c;数据成为发展的重要资产&#xff0c;以数据驱动决策智能已是未来发展的必然趋势&#xff0c;智能化的决策将是重塑核心竞争力的关键抓手。 人工转向智能 银行的监测管理在一般业务场景中&#xff0c;…

Kyligence Zen 产品体验----设备销量商业数据

介绍 Kyligence Zen 是基于 Kyligence 核心 OLAP能力打造的一站式指标平台。凭借集业务模型、指标管理、指标加工、数据服务等于一体的解决方案,Kyligence 协助过多家金融、零售、制造企业客户搭建企业级指标平台。Kyligence Zen 是 Kyligence 基于丰富的指标平台建设实践打造…

MySQL优化二索引使用

1、索引分类 类型解释全局索引(FULLTEXT)全局索引&#xff0c;目前只有 MyISAM 引擎支持全局索引&#xff0c;它的出现是为了解决针对文本的模糊查询效率较低的问题&#xff0c;并且只限于 CHAR、VARCHAR 和 TEXT 列哈希索引(HASH)哈希索引是 MySQL 中用到的唯一 key-value 键…

《通过并行蒙特卡洛方法合成桡动脉的光电容积图(PPG),及其与体重指数(BMI)的相关性》阅读笔记

目录 一、论文摘要 二、论文十问 Q1&#xff1a;论文试图解决什么问题&#xff1f; Q2&#xff1a;这是否是一个新的问题&#xff1f; Q3&#xff1a;这篇文章要验证一个什么科学假设&#xff1f; Q4&#xff1a;有哪些相关研究&#xff1f;如何归类&#xff1f;谁是这一课…

界面控件DevExpress WPF富文本编辑器,让系统拥有Word功能(二)

DevExpress WPF控件的富文本编辑器允许开发者将文字处理功能集成到下一个WPF项目中&#xff0c;凭借其全面的文本格式选项、邮件合并以及丰富的终端用户选项集合&#xff0c;可以轻松地提供Microsoft Word功能。 DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足…

图片生成功能,ChatGPT和New Bing谁更厉害?

大家好&#xff0c;我是可夫小子&#xff0c;关注AIGC、读书和自媒体。解锁更多ChatGPT、AI绘画玩法。加我&#xff0c;备注&#xff1a;chatgpt&#xff0c;拉你进群。 ChatGPT和New Bing虽然是大语言模型&#xff0c;但也有「生成图」的能力&#xff0c;它们该如何调教&#…