Use the MASA family bucket to build an IoT platform from scratch (2) Device registration

foreword

We don't want any device to be able to access our IoT platform, so the normal access process of a device is as follows: 1. The host computer software reads the unique identification code UUID of the device through the serial port or other methods . 2. The host computer calls the IoT background interface and sends UUID and ProductID . 3. The background interface judges whether the device has been registered. If not, the DeviceName and Password are generated according to the ProductID and according to certain rules and returned to the host computer software through the interface. 4. The upper computer software writes the data returned by the interface into the device through the serial port.

1. Device registration process

Here mainly involves four concepts 1. UUID (unique ID of the device, generally the number of the main control board of the device) 2. ProductID (the product ID of the device, defined in the IoT background) 3. DeviceName (the name of the device on the IoT platform or MQTT, the Most of the names are related to the product) 4. Password (the password for the device to connect to MQTT)

2. MQTT registration

1. Add authentication method in EMQX

Select the Built-in Database method, and the built-in database is used for password authentication

Select username for the account type, and keep the default encryption method and salt addition method.

After clicking Create, you can see the newly created authentication method in the authentication menu, and the status is: connected.

We click User Management -> Add to manually create users

In the scenario here, we use the host computer to call the IoT backend, and the IoT interface internally calls the EMQX interface to automatically create users

2. Create API Key

Calling the interface requires authentication. Here we use the Api key method. We create an API key in System Settings -> API Key

The Secret Key will only display the plain text when it is created, we need to record the API Key and Secret Key

3. Call the interface to create a user

We open the RestAPI swagger of EMQX in the browser

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

We can create users through this interface. The Authenticator ID here is the ID of the built-in database Password Based we created above .

This ID is obtained through the following authentication method

We directly use API Key and Secret Key in authentication, and the interface returns Id: password_based:built_in_database

Call the Post interface of authentication, enter: password_based:built_in_database in the id field, and enter the user_id and password of the device in the Request body to successfully create a user.

We can also see the newly created user in the Deshboard interface

3. Test equipment connection

We use MQTTX to simulate the client device connecting to EMQX through the mqtt protocol, create a new connection, fill in the address, port, and the username and password just created through the Api.

Click Connect and find that the device can connect to mqtt normally.

You can also see information such as the currently connected client ID in the Dashboard.

4. Write code

Add the DeviceController controller in the MASA.IoT.WebApi project and add the DeviceRegAsync method for device registration. If the device has not been registered (the UUID database does not exist), the device name will be generated according to the ProductCode in accordance with the law, and the name will be the product supplier number. Begins with time and sequence number. Then add the device to EMQX and store it in the database at the same time. If the device has already been registered, then directly fetch the device registration information from the database and return it. The code writing is relatively simple, so I won't repeat it here.

//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;
        }
    }
}

A simple algorithm is used to generate the device name here

// 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());
            }
        }
    }
}

Summarize

The above is the content of this article. This article introduces the process of creating a user in EMQX through the interface through the account password and connecting to EMQX. There are many other account recognition methods supported by EMQX. For example, the JWT authentication method can authorize one-time password authentication. The validity period of the certification can be controlled, which will be explained in the specific application in the following chapters.

The full code is here: https://github.com/sunday866/MASA.IoT-Training-Demos


If you are interested in our MASA, whether it is code contribution, use, or issue, please contact us

WeChat:MasaStackTechOps

QQ:7424099

{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/5447363/blog/8707487