diff --git a/src/AzureIoTHub.Portal.Infrastructure/ConfigHandlerBase.cs b/src/AzureIoTHub.Portal.Infrastructure/ConfigHandlerBase.cs index efc729b54..044d1d558 100644 --- a/src/AzureIoTHub.Portal.Infrastructure/ConfigHandlerBase.cs +++ b/src/AzureIoTHub.Portal.Infrastructure/ConfigHandlerBase.cs @@ -39,6 +39,8 @@ internal abstract class ConfigHandlerBase : ConfigHandler internal const string MetricExporterRefreshIntervalKey = "Metrics:ExporterRefreshIntervalInSeconds"; internal const string MetricLoaderRefreshIntervalKey = "Metrics:LoaderRefreshIntervalInMinutes"; + internal const string SyncDevicesJobRefreshIntervalKey = "Job:SyncDeviceJobRefreshIntervalInSeconds"; + internal const string IdeasEnabledKey = "Ideas:Enabled"; internal const string IdeasUrlKey = "Ideas:Url"; internal const string IdeasAuthenticationHeaderKey = "Ideas:Authentication:Header"; diff --git a/src/AzureIoTHub.Portal.Infrastructure/DevelopmentConfigHandler.cs b/src/AzureIoTHub.Portal.Infrastructure/DevelopmentConfigHandler.cs index 1b0b52708..2c671a617 100644 --- a/src/AzureIoTHub.Portal.Infrastructure/DevelopmentConfigHandler.cs +++ b/src/AzureIoTHub.Portal.Infrastructure/DevelopmentConfigHandler.cs @@ -16,6 +16,8 @@ internal DevelopmentConfigHandler(IConfiguration config) public override string PortalName => this.config[PortalNameKey]; + public override int SyncDevicesJobRefreshIntervalInSeconds => this.config.GetValue(SyncDevicesJobRefreshIntervalKey, 300); + public override int MetricExporterRefreshIntervalInSeconds => this.config.GetValue(MetricExporterRefreshIntervalKey, 30); public override int MetricLoaderRefreshIntervalInMinutes => this.config.GetValue(MetricLoaderRefreshIntervalKey, 10); diff --git a/src/AzureIoTHub.Portal.Infrastructure/Migrations/20220922094312_Add Device and LorawanDevice.Designer.cs b/src/AzureIoTHub.Portal.Infrastructure/Migrations/20220922094312_Add Device and LorawanDevice.Designer.cs new file mode 100644 index 000000000..4ff1a743c --- /dev/null +++ b/src/AzureIoTHub.Portal.Infrastructure/Migrations/20220922094312_Add Device and LorawanDevice.Designer.cs @@ -0,0 +1,345 @@ +// +using System; +using System.Collections.Generic; +using AzureIoTHub.Portal.Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace AzureIoTHub.Portal.Infrastructure.Migrations +{ + [DbContext(typeof(PortalDbContext))] + [Migration("20220922094312_Add Device and LorawanDevice")] + partial class AddDeviceandLorawanDevice + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Device", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DeviceModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsConnected") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StatusUpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property>("Tags") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("Devices"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceModel", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ABPRelaxMode") + .HasColumnType("boolean"); + + b.Property("AppEUI") + .HasColumnType("text"); + + b.Property("Deduplication") + .HasColumnType("integer"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Downlink") + .HasColumnType("boolean"); + + b.Property("IsBuiltin") + .HasColumnType("boolean"); + + b.Property("KeepAliveTimeout") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PreferredWindow") + .HasColumnType("integer"); + + b.Property("RXDelay") + .HasColumnType("integer"); + + b.Property("SensorDecoder") + .HasColumnType("text"); + + b.Property("SupportLoRaFeatures") + .HasColumnType("boolean"); + + b.Property("UseOTAA") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("DeviceModels"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceModelCommand", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Confirmed") + .HasColumnType("boolean"); + + b.Property("DeviceModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Frame") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsBuiltin") + .HasColumnType("boolean"); + + b.Property("Port") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("DeviceModelCommands"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceModelProperty", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsWritable") + .HasColumnType("boolean"); + + b.Property("ModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("PropertyType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("DeviceModelProperties"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceTag", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("Searchable") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("DeviceTags"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModel", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("EdgeDeviceModels"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModelCommand", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("EdgeDeviceModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("EdgeDeviceModelCommands"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ABPRelaxMode") + .HasColumnType("boolean"); + + b.Property("AlreadyLoggedInOnce") + .HasColumnType("boolean"); + + b.Property("AppEUI") + .HasColumnType("text"); + + b.Property("AppKey") + .HasColumnType("text"); + + b.Property("AppSKey") + .HasColumnType("text"); + + b.Property("ClassType") + .HasColumnType("integer"); + + b.Property("DataRate") + .HasColumnType("text"); + + b.Property("Deduplication") + .HasColumnType("integer"); + + b.Property("DevAddr") + .HasColumnType("text"); + + b.Property("DeviceModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Downlink") + .HasColumnType("boolean"); + + b.Property("FCntDownStart") + .HasColumnType("integer"); + + b.Property("FCntResetCounter") + .HasColumnType("integer"); + + b.Property("FCntUpStart") + .HasColumnType("integer"); + + b.Property("GatewayID") + .HasColumnType("text"); + + b.Property("IsConnected") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("KeepAliveTimeout") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("NbRep") + .HasColumnType("text"); + + b.Property("NwkSKey") + .HasColumnType("text"); + + b.Property("PreferredWindow") + .HasColumnType("integer"); + + b.Property("RX1DROffset") + .HasColumnType("integer"); + + b.Property("RX2DataRate") + .HasColumnType("integer"); + + b.Property("RXDelay") + .HasColumnType("integer"); + + b.Property("ReportedRX1DROffset") + .HasColumnType("text"); + + b.Property("ReportedRX2DataRate") + .HasColumnType("text"); + + b.Property("ReportedRXDelay") + .HasColumnType("text"); + + b.Property("SensorDecoder") + .HasColumnType("text"); + + b.Property("StatusUpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Supports32BitFCnt") + .HasColumnType("boolean"); + + b.Property>("Tags") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("TxPower") + .HasColumnType("text"); + + b.Property("UseOTAA") + .HasColumnType("boolean"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("LorawanDevices"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/AzureIoTHub.Portal.Infrastructure/Migrations/20220922094312_Add Device and LorawanDevice.cs b/src/AzureIoTHub.Portal.Infrastructure/Migrations/20220922094312_Add Device and LorawanDevice.cs new file mode 100644 index 000000000..8158e9abb --- /dev/null +++ b/src/AzureIoTHub.Portal.Infrastructure/Migrations/20220922094312_Add Device and LorawanDevice.cs @@ -0,0 +1,90 @@ +// Copyright (c) CGI France. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#nullable disable + +namespace AzureIoTHub.Portal.Infrastructure.Migrations +{ + using System; + using System.Collections.Generic; + using Microsoft.EntityFrameworkCore.Migrations; + + public partial class AddDeviceandLorawanDevice : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + _ = migrationBuilder.CreateTable( + name: "Devices", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: false), + DeviceModelId = table.Column(type: "text", nullable: false), + IsConnected = table.Column(type: "boolean", nullable: false), + IsEnabled = table.Column(type: "boolean", nullable: false), + Version = table.Column(type: "integer", nullable: false), + StatusUpdatedTime = table.Column(type: "timestamp with time zone", nullable: false), + Tags = table.Column>(type: "jsonb", nullable: false) + }, + constraints: table => + { + _ = table.PrimaryKey("PK_Devices", x => x.Id); + }); + + _ = migrationBuilder.CreateTable( + name: "LorawanDevices", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: false), + DeviceModelId = table.Column(type: "text", nullable: false), + IsConnected = table.Column(type: "boolean", nullable: false), + IsEnabled = table.Column(type: "boolean", nullable: false), + Version = table.Column(type: "integer", nullable: false), + StatusUpdatedTime = table.Column(type: "timestamp with time zone", nullable: false), + Tags = table.Column>(type: "jsonb", nullable: false), + UseOTAA = table.Column(type: "boolean", nullable: false), + AppKey = table.Column(type: "text", nullable: true), + AppEUI = table.Column(type: "text", nullable: true), + AppSKey = table.Column(type: "text", nullable: true), + NwkSKey = table.Column(type: "text", nullable: true), + DevAddr = table.Column(type: "text", nullable: true), + AlreadyLoggedInOnce = table.Column(type: "boolean", nullable: false), + DataRate = table.Column(type: "text", nullable: true), + TxPower = table.Column(type: "text", nullable: true), + NbRep = table.Column(type: "text", nullable: true), + ReportedRX2DataRate = table.Column(type: "text", nullable: true), + ReportedRX1DROffset = table.Column(type: "text", nullable: true), + ReportedRXDelay = table.Column(type: "text", nullable: true), + GatewayID = table.Column(type: "text", nullable: true), + Downlink = table.Column(type: "boolean", nullable: true), + ClassType = table.Column(type: "integer", nullable: false), + PreferredWindow = table.Column(type: "integer", nullable: false), + Deduplication = table.Column(type: "integer", nullable: false), + RX1DROffset = table.Column(type: "integer", nullable: true), + RX2DataRate = table.Column(type: "integer", nullable: true), + RXDelay = table.Column(type: "integer", nullable: true), + ABPRelaxMode = table.Column(type: "boolean", nullable: true), + FCntUpStart = table.Column(type: "integer", nullable: true), + FCntDownStart = table.Column(type: "integer", nullable: true), + FCntResetCounter = table.Column(type: "integer", nullable: true), + Supports32BitFCnt = table.Column(type: "boolean", nullable: true), + KeepAliveTimeout = table.Column(type: "integer", nullable: true), + SensorDecoder = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + _ = table.PrimaryKey("PK_LorawanDevices", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + _ = migrationBuilder.DropTable( + name: "Devices"); + + _ = migrationBuilder.DropTable( + name: "LorawanDevices"); + } + } +} diff --git a/src/AzureIoTHub.Portal.Infrastructure/Migrations/PortalDbContextModelSnapshot.cs b/src/AzureIoTHub.Portal.Infrastructure/Migrations/PortalDbContextModelSnapshot.cs index ee7f83f1e..4dbfd7205 100644 --- a/src/AzureIoTHub.Portal.Infrastructure/Migrations/PortalDbContextModelSnapshot.cs +++ b/src/AzureIoTHub.Portal.Infrastructure/Migrations/PortalDbContextModelSnapshot.cs @@ -1,5 +1,6 @@ // using System; +using System.Collections.Generic; using AzureIoTHub.Portal.Infrastructure; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -17,11 +18,45 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "6.0.8") + .HasAnnotation("ProductVersion", "6.0.9") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Device", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DeviceModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsConnected") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StatusUpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property>("Tags") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("Devices"); + }); + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceModel", b => { b.Property("Id") @@ -188,6 +223,124 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("EdgeDeviceModelCommands"); }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ABPRelaxMode") + .HasColumnType("boolean"); + + b.Property("AlreadyLoggedInOnce") + .HasColumnType("boolean"); + + b.Property("AppEUI") + .HasColumnType("text"); + + b.Property("AppKey") + .HasColumnType("text"); + + b.Property("AppSKey") + .HasColumnType("text"); + + b.Property("ClassType") + .HasColumnType("integer"); + + b.Property("DataRate") + .HasColumnType("text"); + + b.Property("Deduplication") + .HasColumnType("integer"); + + b.Property("DevAddr") + .HasColumnType("text"); + + b.Property("DeviceModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Downlink") + .HasColumnType("boolean"); + + b.Property("FCntDownStart") + .HasColumnType("integer"); + + b.Property("FCntResetCounter") + .HasColumnType("integer"); + + b.Property("FCntUpStart") + .HasColumnType("integer"); + + b.Property("GatewayID") + .HasColumnType("text"); + + b.Property("IsConnected") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("KeepAliveTimeout") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("NbRep") + .HasColumnType("text"); + + b.Property("NwkSKey") + .HasColumnType("text"); + + b.Property("PreferredWindow") + .HasColumnType("integer"); + + b.Property("RX1DROffset") + .HasColumnType("integer"); + + b.Property("RX2DataRate") + .HasColumnType("integer"); + + b.Property("RXDelay") + .HasColumnType("integer"); + + b.Property("ReportedRX1DROffset") + .HasColumnType("text"); + + b.Property("ReportedRX2DataRate") + .HasColumnType("text"); + + b.Property("ReportedRXDelay") + .HasColumnType("text"); + + b.Property("SensorDecoder") + .HasColumnType("text"); + + b.Property("StatusUpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Supports32BitFCnt") + .HasColumnType("boolean"); + + b.Property>("Tags") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("TxPower") + .HasColumnType("text"); + + b.Property("UseOTAA") + .HasColumnType("boolean"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("LorawanDevices"); + }); #pragma warning restore 612, 618 } } diff --git a/src/AzureIoTHub.Portal.Infrastructure/ProductionConfigHandler.cs b/src/AzureIoTHub.Portal.Infrastructure/ProductionConfigHandler.cs index b2e423dd2..04235edd2 100644 --- a/src/AzureIoTHub.Portal.Infrastructure/ProductionConfigHandler.cs +++ b/src/AzureIoTHub.Portal.Infrastructure/ProductionConfigHandler.cs @@ -16,6 +16,8 @@ internal ProductionConfigHandler(IConfiguration config) public override string PortalName => this.config[PortalNameKey]; + public override int SyncDevicesJobRefreshIntervalInSeconds => this.config.GetValue(SyncDevicesJobRefreshIntervalKey, 300); + public override int MetricExporterRefreshIntervalInSeconds => this.config.GetValue(MetricExporterRefreshIntervalKey, 30); public override int MetricLoaderRefreshIntervalInMinutes => this.config.GetValue(MetricLoaderRefreshIntervalKey, 10); diff --git a/src/AzureIoTHub.Portal/Server/Mappers/DeviceProfile.cs b/src/AzureIoTHub.Portal/Server/Mappers/DeviceProfile.cs index 4a7096d0e..b470a47c1 100644 --- a/src/AzureIoTHub.Portal/Server/Mappers/DeviceProfile.cs +++ b/src/AzureIoTHub.Portal/Server/Mappers/DeviceProfile.cs @@ -14,6 +14,7 @@ public class DeviceProfile : Profile { public DeviceProfile() { + _ = CreateMap(); _ = CreateMap() .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.DeviceId)) .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Tags["deviceName"])) @@ -23,6 +24,8 @@ public DeviceProfile() .ForMember(dest => dest.IsEnabled, opts => opts.MapFrom(src => src.Status == Microsoft.Azure.Devices.DeviceStatus.Enabled)) .ForMember(dest => dest.Tags, opts => opts.MapFrom(src => GetTags(src))); + + _ = CreateMap(); _ = CreateMap() .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.DeviceId)) .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Tags["deviceName"])) diff --git a/src/AzureIoTHub.Portal/Server/Startup.cs b/src/AzureIoTHub.Portal/Server/Startup.cs index 9136d7cd4..98800d0ab 100644 --- a/src/AzureIoTHub.Portal/Server/Startup.cs +++ b/src/AzureIoTHub.Portal/Server/Startup.cs @@ -326,8 +326,8 @@ Specify the authorization token got from your IDP as a header. .WithIdentity($"{nameof(SyncDevicesJob)}") .ForJob(nameof(SyncDevicesJob)) .WithSimpleSchedule(s => s - //.WithIntervalInSeconds(configuration.MetricExporterRefreshIntervalInSeconds) - .WithIntervalInSeconds(3000) + .WithIntervalInSeconds(configuration.SyncDevicesJobRefreshIntervalInSeconds) + //.WithIntervalInSeconds(3000) .RepeatForever())); }); diff --git a/src/AzureIoTHubPortal.Domain/ConfigHandler.cs b/src/AzureIoTHubPortal.Domain/ConfigHandler.cs index b4cc474bc..fe87e5d04 100644 --- a/src/AzureIoTHubPortal.Domain/ConfigHandler.cs +++ b/src/AzureIoTHubPortal.Domain/ConfigHandler.cs @@ -51,6 +51,8 @@ public abstract class ConfigHandler public abstract string PortalName { get; } + public abstract int SyncDevicesJobRefreshIntervalInSeconds { get; } + public abstract int MetricExporterRefreshIntervalInSeconds { get; } public abstract int MetricLoaderRefreshIntervalInMinutes { get; }