This commit is contained in:
Đăng Nguyễn 2025-11-03 10:29:18 +07:00
parent aea55d52f1
commit 70e27da4a2
14 changed files with 883 additions and 71 deletions

View File

@ -1,5 +1,6 @@
@implements IDisposable
<div class="d-flex w-100 h-100 flex-column">
<EditForm EditContext="EditContext">
<DataAnnotationsValidator />
@ -71,12 +72,27 @@
<ValidationMessage For="@(() => Model.VDA5050EnableTls)" />
</div>
<div class="row g-0 mb-2">
<div class="mb-2">
<label class="form-label" for="caFile">CA File</label>
<InputFile class="form-control" id="caFile" OnChange="e => OnFileSelected(e, FileSlot.Ca)" accept=".crt,.pem,.cer,.pfx,.key,.jks" />
@if (!string.IsNullOrEmpty(CaFileName))
<div class="custom-file-input-wrapper position-relative">
<InputFile class="d-none" id="caFile" OnChange="e => OnFileSelected(e, FileSlot.Ca)" accept=".crt,.pem,.cer,.pfx,.key,.jks" />
<div class="form-control d-flex align-items-center gap-2 ps-0">
<label for="caFile" class="upload-btn d-flex align-items-center gap-1">
<i class="mdi mdi-attachment"></i>
</label>
<span id="fileNameDisplay" class="text-muted flex-grow-1 text-truncate" style="max-width: 200px;">
@Model.VDA5050CA
</span>
<button class="password-toggle-btn position-absolute rounded-end-2 top-50 translate-middle-y" type="button" @onclick="() => RemoveFile(FileSlot.Ca)" aria-label="Remove client certificate file">
<i class="mdi mdi-close" aria-hidden="true"></i>
</button>
</div>
</div>
@if (!string.IsNullOrEmpty(CaFileInfo))
{
<div class="small text-muted mt-1 mb-1">@CaFileName (@FormatSize(CaFileSize))</div>
<div class="small text-muted mt-1 mb-1">@CaFileInfo</div>
}
@if (!string.IsNullOrEmpty(CaFileError))
{
@ -84,12 +100,27 @@
}
</div>
<div class="row g-0 mb-2">
<div class="mb-2">
<label class="form-label" for="clientCertFile">Client Certificate File</label>
<InputFile class="form-control" id="clientCertFile" OnChange="e => OnFileSelected(e, FileSlot.Cert)" accept=".crt,.pem,.cer,.pfx,.key,.jks" />
@if (!string.IsNullOrEmpty(CertFileName))
<div class="custom-file-input-wrapper position-relative">
<InputFile class="d-none" id="clientCertFile" OnChange="e => OnFileSelected(e, FileSlot.Cert)" accept=".crt,.pem,.cer,.pfx,.key,.jks" />
<div class="form-control d-flex align-items-center gap-2 ps-0">
<label for="clientCertFile" class="upload-btn d-flex align-items-center gap-1">
<i class="mdi mdi-attachment"></i>
</label>
<span id="fileNameDisplay" class="text-muted flex-grow-1 text-truncate" style="max-width: 200px;">
@Model.VDA5050Cer
</span>
<button class="password-toggle-btn position-absolute rounded-end-2 top-50 translate-middle-y" type="button" @onclick="() => RemoveFile(FileSlot.Cert)" aria-label="Remove client certificate file">
<i class="mdi mdi-close" aria-hidden="true"></i>
</button>
</div>
</div>
@if (!string.IsNullOrEmpty(CertFileInfo))
{
<div class="small text-muted mt-1 mb-1">@CertFileName (@FormatSize(CertFileSize))</div>
<div class="small text-muted mt-1 mb-1">@CertFileInfo</div>
}
@if (!string.IsNullOrEmpty(CertFileError))
{
@ -97,12 +128,27 @@
}
</div>
<div class="row g-0 mb-2">
<div class="mb-2">
<label class="form-label" for="clientKeyFile">Client Key File</label>
<InputFile class="form-control" id="clientKeyFile" OnChange="e => OnFileSelected(e, FileSlot.Key)" accept=".crt,.pem,.cer,.pfx,.key,.jks" />
@if (!string.IsNullOrEmpty(KeyFileName))
<div class="custom-file-input-wrapper position-relative">
<InputFile class="d-none" id="clientKeyFile" OnChange="e => OnFileSelected(e, FileSlot.Key)" accept=".crt,.pem,.cer,.pfx,.key,.jks" />
<div class="form-control d-flex align-items-center gap-2 ps-0">
<label for="clientKeyFile" class="upload-btn d-flex align-items-center gap-1">
<i class="mdi mdi-attachment"></i>
</label>
<span id="fileNameDisplay" class="text-muted flex-grow-1 text-truncate" style="max-width: 200px;">
@Model.VDA5050Key
</span>
<button class="password-toggle-btn position-absolute rounded-end-2 top-50 translate-middle-y" type="button" @onclick="() => RemoveFile(FileSlot.Key)" aria-label="Remove client key file">
<i class="mdi mdi-close" aria-hidden="true"></i>
</button>
</div>
</div>
@if (!string.IsNullOrEmpty(KeyFileInfo))
{
<div class="small text-muted mt-1 mb-1">@KeyFileName (@FormatSize(KeyFileSize))</div>
<div class="small text-muted mt-1 mb-1">@KeyFileInfo</div>
}
@if (!string.IsNullOrEmpty(KeyFileError))
{
@ -128,7 +174,6 @@
</div>
</div>
@code {
[Parameter]
public RobotVDA5050ConfigDto Model { get; set; } = new();
@ -136,6 +181,11 @@
[Parameter]
public EventCallback<RobotVDA5050ConfigDto> ModelChanged { get; set; }
public IBrowserFile? CaFile { get; set; }
public IBrowserFile? CertFile { get; set; }
public IBrowserFile? KeyFile { get; set; }
public long MaxFileSize { get; set; } = 10 * 1024 * 1024;
private EditContext? EditContext;
private bool showPassword;
private string PasswordInputType => showPassword ? "text" : "password";
@ -143,22 +193,14 @@
private enum FileSlot { Ca, Cert, Key }
private string? CaFileName;
private long CaFileSize;
private string? CaFileInfo;
private string? CaFileError;
private byte[]? CaFileData;
private string? CertFileName;
private long CertFileSize;
private string? CertFileInfo;
private string? CertFileError;
private byte[]? CertFileData;
private string? KeyFileName;
private long KeyFileSize;
private string? KeyFileInfo;
private string? KeyFileError;
private byte[]? KeyFileData;
private const long MaxFileSize = 10 * 1024 * 1024; // 10 MB
private async Task OnFileSelected(InputFileChangeEventArgs e, FileSlot slot)
{
@ -169,7 +211,7 @@
if (file.Size > MaxFileSize)
{
SetFileError(slot, $"File too large (max {FormatSize(MaxFileSize)})");
SetFileInfo(slot, null, 0, null);
SetFileInfo(slot, string.Empty, 0);
return;
}
@ -180,30 +222,25 @@
await stream.CopyToAsync(ms);
var data = ms.ToArray();
SetFileData(slot, data, file.Name, file.Size);
ClearFileError(slot);
// Optionally propagate change to parent via ModelChanged if required
// _ = ModelChanged.InvokeAsync(Model);
SetFileError(slot, null);
SetFileInfo(slot, file.Name, file.Size);
SetFileName(slot, file.Name);
SetBrowserFile(slot, file);
_ = ModelChanged.InvokeAsync(Model);
}
catch (Exception ex)
catch
{
SetFileError(slot, "Failed to read file");
Console.Error.WriteLine(ex);
}
}
private void SetFileData(FileSlot slot, byte[] data, string name, long size)
private void RemoveFile(FileSlot slot)
{
switch (slot)
{
case FileSlot.Ca:
CaFileData = data; CaFileName = name; CaFileSize = size; break;
case FileSlot.Cert:
CertFileData = data; CertFileName = name; CertFileSize = size; break;
case FileSlot.Key:
KeyFileData = data; KeyFileName = name; KeyFileSize = size; break;
}
SetFileInfo(slot, string.Empty, 0);
SetFileError(slot, null);
SetFileName(slot, string.Empty);
_ = ModelChanged.InvokeAsync(Model);
}
private void SetFileError(FileSlot slot, string? error)
@ -216,15 +253,33 @@
}
}
private void ClearFileError(FileSlot slot) => SetFileError(slot, null);
private void SetFileInfo(FileSlot slot, string? name, long size, byte[]? data)
private void SetFileInfo(FileSlot slot, string? name, long size)
{
switch (slot)
{
case FileSlot.Ca: CaFileName = name; CaFileSize = size; CaFileData = data; break;
case FileSlot.Cert: CertFileName = name; CertFileSize = size; CertFileData = data; break;
case FileSlot.Key: KeyFileName = name; KeyFileSize = size; KeyFileData = data; break;
case FileSlot.Ca: CaFileInfo = string.IsNullOrEmpty(name) ? "" : $"{name} {FormatSize(size)}"; break;
case FileSlot.Cert: CertFileInfo = string.IsNullOrEmpty(name) ? "" : $"{name} {FormatSize(size)}"; break;
case FileSlot.Key: KeyFileInfo = string.IsNullOrEmpty(name) ? "" : $"{name} {FormatSize(size)}"; break;
}
}
private void SetFileName(FileSlot slot, string? name)
{
switch (slot)
{
case FileSlot.Ca: Model.VDA5050CA = name; break;
case FileSlot.Cert: Model.VDA5050Cer = name; break;
case FileSlot.Key: Model.VDA5050Key = name; break;
}
}
private void SetBrowserFile(FileSlot slot, IBrowserFile file)
{
switch (slot)
{
case FileSlot.Ca: CaFile = file; break;
case FileSlot.Cert: CertFile = file; break;
case FileSlot.Key: KeyFile = file; break;
}
}
@ -245,13 +300,10 @@
if (EditContext is not null) EditContext.OnFieldChanged -= EditContext_OnFieldChanged;
EditContext = new EditContext(Model);
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
CaFileInfo = string.Empty;
CertFileInfo = string.Empty;
KeyFileInfo = string.Empty;
}
CertFileName = string.Empty;
CertFileError = string.Empty;
KeyFileName = string.Empty;
KeyFileError = string.Empty;
CaFileName = string.Empty;
CaFileError = string.Empty;
}
private void TogglePasswordVisibility()

View File

@ -40,4 +40,54 @@
.password-toggle-btn .mdi {
font-size: 1.15rem;
pointer-events: none; /* let clicks hit the button, not the icon */
}
}
.file-input-wrapper {
max-width: 100%;
}
.custom-file-input-wrapper {
max-width: 100%;
}
.custom-file-input-wrapper .form-control {
height: 38px;
padding-right: 50px;
background-color: #f8f9fa;
border: 1px solid #ced4da;
}
.text-truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* === NÚT LABEL GIỐNG BUTTON THẬT === */
.upload-btn {
cursor: pointer;
transition: all 0.2s ease;
user-select: none;
min-width: 50px;
font-size: 23px;
justify-content: center;
border-right: 1px solid silver;
border-radius: 0.25rem;
}
/* HOVER: đổi màu nền + viền */
.upload-btn:hover {
background-color: #e7f3ff !important;
}
/* FOCUS: khi tab đến (accessibility) */
.upload-btn:focus {
outline: 2px solid transparent;
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.5) !important;
}
/* ACTIVE: khi nhấn */
.upload-btn:active {
transform: translateY(0);
background-color: #d0e8ff !important;
}

View File

@ -83,7 +83,7 @@
@switch (SelectedType)
{
case RobotConfigType.VDA5050:
<RobotApp.Client.Pages.Components.Config.RobotVDA5050Config @bind-Model="SelectedVda" />
<RobotApp.Client.Pages.Components.Config.RobotVDA5050Config @ref="@RobotVDA5050ConfigRef" @bind-Model="SelectedVda" />
break;
case RobotConfigType.Safety:
<RobotApp.Client.Pages.Components.Config.RobotSafetyConfig @bind-Model="SelectedSafety" />
@ -204,6 +204,8 @@
private RobotConfigType SelectedType = RobotConfigType.VDA5050;
private RobotApp.Client.Pages.Components.Config.RobotVDA5050Config RobotVDA5050ConfigRef = default!;
private int SelectedIndex = -1;
private bool HasSelection => SelectedIndex >= 0;
private bool IsLoading = false;

View File

@ -2,6 +2,7 @@
using RobotApp.Common.Shares;
using RobotApp.Common.Shares.Dtos;
using RobotApp.Common.Shares.Enums;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Xml.Schema;
@ -305,6 +306,33 @@ public partial class RobotConfigManager
}
}
private async Task<bool> SaveCertificates(Guid id)
{
using var content = new MultipartFormDataContent();
if (RobotVDA5050ConfigRef.CaFile is not null)
{
var fileContent = new StreamContent(RobotVDA5050ConfigRef.CaFile.OpenReadStream(maxAllowedSize: RobotVDA5050ConfigRef.MaxFileSize));
content.Add(fileContent, "CaFile", RobotVDA5050ConfigRef.CaFile.Name);
}
if (RobotVDA5050ConfigRef.CertFile is not null)
{
var fileContent = new StreamContent(RobotVDA5050ConfigRef.CertFile.OpenReadStream(maxAllowedSize: RobotVDA5050ConfigRef.MaxFileSize));
content.Add(fileContent, "CertFile", RobotVDA5050ConfigRef.CertFile.Name);
}
if (RobotVDA5050ConfigRef.KeyFile is not null)
{
var fileContent = new StreamContent(RobotVDA5050ConfigRef.KeyFile.OpenReadStream(maxAllowedSize: RobotVDA5050ConfigRef.MaxFileSize));
content.Add(fileContent, "KeyFile", RobotVDA5050ConfigRef.KeyFile.Name);
}
var response = await (await Http.PostAsync($"api/File/certificates/{id}", content)).Content.ReadFromJsonAsync<MessageResult>();
if (response is null) Snackbar.Add("Failed to update certificates", Severity.Warning);
else if (!response.IsSuccess) Snackbar.Add(response.Message ?? "Failed to update certificates config", Severity.Warning);
else return true;
return false;
}
private async Task SaveConfig()
{
try
@ -344,9 +372,14 @@ public partial class RobotConfigManager
SelectedVda.VDA5050PublishRepeat,
SelectedVda.VDA5050EnablePassword,
SelectedVda.VDA5050EnableTls,
SelectedVda.VDA5050CA,
SelectedVda.VDA5050Cer,
SelectedVda.VDA5050Key,
SelectedVda.Description
};
result = await (await Http.PutAsJsonAsync($"api/RobotConfigs/vda5050/{id}", updateDto)).Content.ReadFromJsonAsync<MessageResult>();
var saveCer = await SaveCertificates(SelectedVda.Id);
if (saveCer) result = await (await Http.PutAsJsonAsync($"api/RobotConfigs/vda5050/{id}", updateDto)).Content.ReadFromJsonAsync<MessageResult>();
else return;
break;
}

View File

@ -0,0 +1 @@


View File

@ -23,7 +23,7 @@ public record RobotVDA5050ConfigDto
public bool VDA5050EnableTls { get; set; }
public string VDA5050CA { get; set; }
public string VDA5050Cer { get; set; }
public string VDA5050_Key { get; set; }
public string VDA5050Key { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public bool IsActive { get; set; }
@ -45,7 +45,7 @@ public record UpdateRobotVDA5050ConfigDto
public bool VDA5050EnableTls { get; set; }
public string VDA5050CA { get; set; }
public string VDA5050Cer { get; set; }
public string VDA5050_Key { get; set; }
public string VDA5050Key { get; set; }
public string Description { get; set; }
}
@ -63,7 +63,7 @@ public record CreateRobotVDA5050ConfigDto
public bool VDA5050EnableTls { get; set; }
public string VDA5050CA { get; set; }
public string VDA5050Cer { get; set; }
public string VDA5050_Key { get; set; }
public string VDA5050Key { get; set; }
public string ConfigName { get; set; }
public string Description { get; set; }
}

View File

@ -41,6 +41,7 @@
<script src="@Assets["lib/bootstrap/js/bootstrap.min.js"]"></script>
<script src="@Assets["_content/MudBlazor/MudBlazor.min.js"]"></script>
<script src="@Assets["js/canvas.js"]"></script>
<script src="@Assets["js/app.js"]"></script>
</body>
</html>

View File

@ -0,0 +1,76 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using RobotApp.Common.Shares;
namespace RobotApp.Controllers;
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class FileController(Services.Logger<FileController> Logger) : ControllerBase
{
private readonly string certificatesPath = "MqttCertificates";
private readonly string caFilePath = "ca";
private readonly string cerFilePath = "cer";
private readonly string keyFilePath = "key";
[HttpPost]
[Route("certificates/{id:guid}")]
public async Task<MessageResult> UpdateMqttCertificates(Guid id, [FromForm(Name = "CaFile")] IFormFile? caFile,
[FromForm(Name = "CertFile")] IFormFile? certFile,
[FromForm(Name = "KeyFile")] IFormFile? keyFile)
{
try
{
if (!Directory.Exists(certificatesPath)) Directory.CreateDirectory(certificatesPath);
if (caFile is not null)
{
var caFolder = Path.Combine(certificatesPath, caFilePath);
if (!Directory.Exists(caFolder)) Directory.CreateDirectory(caFolder);
string caExtension = Path.GetExtension(caFile.FileName);
var caLocal = Path.Combine(caFolder, $"{id}{caExtension}");
if (System.IO.File.Exists($"{caLocal}.bk")) System.IO.File.Delete($"{caLocal}.bk");
if (System.IO.File.Exists(caLocal)) System.IO.File.Move(caLocal, $"{caLocal}.bk");
using Stream fileStream = new FileStream(caLocal, FileMode.Create);
await caFile.OpenReadStream().CopyToAsync(fileStream);
}
if (certFile is not null)
{
var certFolder = Path.Combine(certificatesPath, cerFilePath);
if (!Directory.Exists(certFolder)) Directory.CreateDirectory(certFolder);
string certExtension = Path.GetExtension(certFile.FileName);
var certLocal = Path.Combine(certFolder, $"{id}{certExtension}");
if (System.IO.File.Exists($"{certLocal}.bk")) System.IO.File.Delete($"{certLocal}.bk");
if (System.IO.File.Exists(certLocal)) System.IO.File.Move(certLocal, $"{certLocal}.bk");
using Stream fileStream = new FileStream(certLocal, FileMode.Create);
await certFile.OpenReadStream().CopyToAsync(fileStream);
}
if (keyFile is not null)
{
var keyFolder = Path.Combine(certificatesPath, keyFilePath);
if (!Directory.Exists(keyFolder)) Directory.CreateDirectory(keyFolder);
string keyExtension = Path.GetExtension(keyFile.FileName);
var keyLocal = Path.Combine(keyFolder, $"{id}{keyExtension}");
if (System.IO.File.Exists($"{keyLocal}.bk")) System.IO.File.Delete($"{keyLocal}.bk");
if (System.IO.File.Exists(keyLocal)) System.IO.File.Move(keyLocal, $"{keyLocal}.bk");
using Stream fileStream = new FileStream(keyLocal, FileMode.Create);
await keyFile.OpenReadStream().CopyToAsync(fileStream);
}
return new(true, "");
}
catch (Exception ex)
{
Logger.Error($"Update Mqtt Certificates is failed: {ex.Message}");
return new(false, "An error occurred while retrieving update Mqtt Certificates.");
}
}
}

View File

@ -1,5 +1,4 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using RobotApp.Common.Shares;
@ -196,7 +195,7 @@ public class RobotConfigsController(Services.Logger<RobotConfigsController> Logg
VDA5050EnableTls = config.VDA5050EnableTls,
VDA5050CA = config.VDA5050CA,
VDA5050Cer = config.VDA5050Cer,
VDA5050_Key = config.VDA5050_Key,
VDA5050Key = config.VDA5050Key,
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive,
@ -234,7 +233,7 @@ public class RobotConfigsController(Services.Logger<RobotConfigsController> Logg
config.VDA5050EnableTls = updateDto.VDA5050EnableTls;
config.VDA5050CA = updateDto.VDA5050CA;
config.VDA5050Cer = updateDto.VDA5050Cer;
config.VDA5050_Key = updateDto.VDA5050_Key;
config.VDA5050Key = updateDto.VDA5050Key;
config.Description = updateDto.Description ?? config.Description;
config.UpdatedAt = DateTime.Now;
@ -276,7 +275,7 @@ public class RobotConfigsController(Services.Logger<RobotConfigsController> Logg
VDA5050EnableTls = createDto.VDA5050EnableTls,
VDA5050CA = createDto.VDA5050CA,
VDA5050Cer = createDto.VDA5050Cer,
VDA5050_Key = createDto.VDA5050_Key,
VDA5050Key = createDto.VDA5050Key,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now,
IsActive = false
@ -304,7 +303,7 @@ public class RobotConfigsController(Services.Logger<RobotConfigsController> Logg
VDA5050EnableTls = config.VDA5050EnableTls,
VDA5050CA = config.VDA5050CA,
VDA5050Cer = config.VDA5050Cer,
VDA5050_Key = config.VDA5050_Key,
VDA5050Key = config.VDA5050Key,
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive,
@ -860,5 +859,4 @@ public class RobotConfigsController(Services.Logger<RobotConfigsController> Logg
return new(false, "An error occurred while updating the active Safety configuration.");
}
}
}

View File

@ -0,0 +1,577 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using RobotApp.Data;
#nullable disable
namespace RobotApp.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20251031080648_UpdateVDA5050Config1")]
partial class UpdateVDA5050Config1
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.9");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("RobotApp.Data.ApplicationRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("RobotApp.Data.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("RobotApp.Data.RobotConfig", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier")
.HasColumnName("Id");
b.Property<string>("ConfigName")
.HasMaxLength(100)
.HasColumnType("nvarchar(64)")
.HasColumnName("ConfigName");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2")
.HasColumnName("CreatedAt");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("ntext")
.HasColumnName("Description");
b.Property<double>("Height")
.HasColumnType("float")
.HasColumnName("Height");
b.Property<bool>("IsActive")
.HasColumnType("bit")
.HasColumnName("IsActive");
b.Property<double>("Length")
.HasColumnType("float")
.HasColumnName("Length");
b.Property<int>("NavigationType")
.HasColumnType("int")
.HasColumnName("NavigationType");
b.Property<double>("RadiusWheel")
.HasColumnType("float")
.HasColumnName("RadiusWheel");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime2")
.HasColumnName("UpdatedAt");
b.Property<double>("Width")
.HasColumnType("float")
.HasColumnName("Width");
b.HasKey("Id");
b.ToTable("RobotConfig");
});
modelBuilder.Entity("RobotApp.Data.RobotPlcConfig", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier")
.HasColumnName("Id");
b.Property<string>("ConfigName")
.HasMaxLength(100)
.HasColumnType("nvarchar(64)")
.HasColumnName("ConfigName");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2")
.HasColumnName("CreatedAt");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("ntext")
.HasColumnName("Description");
b.Property<bool>("IsActive")
.HasColumnType("bit")
.HasColumnName("IsActive");
b.Property<string>("PLCAddress")
.HasMaxLength(50)
.HasColumnType("nvarchar(64)")
.HasColumnName("PLCAddress");
b.Property<int>("PLCPort")
.HasColumnType("int")
.HasColumnName("PLCPort");
b.Property<byte>("PLCUnitId")
.HasColumnType("tinyint")
.HasColumnName("PLCUnitId");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime2")
.HasColumnName("UpdatedAt");
b.HasKey("Id");
b.ToTable("RobotPlcConfig");
});
modelBuilder.Entity("RobotApp.Data.RobotSafetyConfig", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier")
.HasColumnName("Id");
b.Property<string>("ConfigName")
.HasMaxLength(100)
.HasColumnType("nvarchar(64)")
.HasColumnName("ConfigName");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2")
.HasColumnName("CreatedAt");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("ntext")
.HasColumnName("Description");
b.Property<bool>("IsActive")
.HasColumnType("bit")
.HasColumnName("IsActive");
b.Property<double>("SafetySpeedFast")
.HasColumnType("float")
.HasColumnName("SafetySpeedFast");
b.Property<double>("SafetySpeedMedium")
.HasColumnType("float")
.HasColumnName("SafetySpeedMedium");
b.Property<double>("SafetySpeedNormal")
.HasColumnType("float")
.HasColumnName("SafetySpeedNormal");
b.Property<double>("SafetySpeedOptimal")
.HasColumnType("float")
.HasColumnName("SafetySpeedOptimal");
b.Property<double>("SafetySpeedSlow")
.HasColumnType("float")
.HasColumnName("SafetySpeedSlow");
b.Property<double>("SafetySpeedVeryFast")
.HasColumnType("float")
.HasColumnName("SafetySpeedVeryFast");
b.Property<double>("SafetySpeedVerySlow")
.HasColumnType("float")
.HasColumnName("SafetySpeedVerySlow");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime2")
.HasColumnName("UpdatedAt");
b.HasKey("Id");
b.ToTable("RobotSafetyConfig");
});
modelBuilder.Entity("RobotApp.Data.RobotSimulationConfig", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier")
.HasColumnName("Id");
b.Property<string>("ConfigName")
.HasMaxLength(100)
.HasColumnType("nvarchar(64)")
.HasColumnName("ConfigName");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2")
.HasColumnName("CreatedAt");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("ntext")
.HasColumnName("Description");
b.Property<bool>("EnableSimulation")
.HasColumnType("bit")
.HasColumnName("EnableSimulation");
b.Property<bool>("IsActive")
.HasColumnType("bit")
.HasColumnName("IsActive");
b.Property<double>("SimulationAcceleration")
.HasColumnType("float")
.HasColumnName("SimulationAcceleration");
b.Property<double>("SimulationDeceleration")
.HasColumnType("float")
.HasColumnName("SimulationDeceleration");
b.Property<double>("SimulationMaxAngularVelocity")
.HasColumnType("float")
.HasColumnName("SimulationMaxAngularVelocity");
b.Property<double>("SimulationMaxVelocity")
.HasColumnType("float")
.HasColumnName("SimulationMaxVelocity");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime2")
.HasColumnName("UpdatedAt");
b.HasKey("Id");
b.ToTable("RobotSimulationConfig");
});
modelBuilder.Entity("RobotApp.Data.RobotVDA5050Config", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier")
.HasColumnName("Id");
b.Property<string>("ConfigName")
.HasMaxLength(100)
.HasColumnType("nvarchar(64)")
.HasColumnName("ConfigName");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2")
.HasColumnName("CreatedAt");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("ntext")
.HasColumnName("Description");
b.Property<bool>("IsActive")
.HasColumnType("bit")
.HasColumnName("IsActive");
b.Property<string>("SerialNumber")
.HasMaxLength(50)
.HasColumnType("nvarchar(64)")
.HasColumnName("SerialNumber");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime2")
.HasColumnName("UpdatedAt");
b.Property<string>("VDA5050CA")
.HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_CA");
b.Property<string>("VDA5050Cer")
.HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_Cer");
b.Property<bool>("VDA5050EnablePassword")
.HasColumnType("bit")
.HasColumnName("VDA5050_EnablePassword");
b.Property<bool>("VDA5050EnableTls")
.HasColumnType("bit")
.HasColumnName("VDA5050_EnableTls");
b.Property<string>("VDA5050HostServer")
.HasMaxLength(100)
.HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_HostServer");
b.Property<string>("VDA5050Key")
.HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_Key");
b.Property<string>("VDA5050Manufacturer")
.HasMaxLength(50)
.HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_Manufacturer");
b.Property<string>("VDA5050Password")
.HasMaxLength(50)
.HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_Password");
b.Property<int>("VDA5050Port")
.HasColumnType("int")
.HasColumnName("VDA5050_Port");
b.Property<int>("VDA5050PublishRepeat")
.HasColumnType("int")
.HasColumnName("VDA5050_PublishRepeat");
b.Property<string>("VDA5050UserName")
.HasMaxLength(50)
.HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_UserName");
b.Property<string>("VDA5050Version")
.HasMaxLength(20)
.HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_Version");
b.HasKey("Id");
b.ToTable("RobotVDA5050Config");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("RobotApp.Data.ApplicationRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("RobotApp.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("RobotApp.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("RobotApp.Data.ApplicationRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("RobotApp.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("RobotApp.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace RobotApp.Data.Migrations
{
/// <inheritdoc />
public partial class UpdateVDA5050Config1 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

View File

@ -481,6 +481,10 @@ namespace RobotApp.Migrations
.HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_HostServer");
b.Property<string>("VDA5050Key")
.HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_Key");
b.Property<string>("VDA5050Manufacturer")
.HasMaxLength(50)
.HasColumnType("nvarchar(64)")
@ -509,10 +513,6 @@ namespace RobotApp.Migrations
.HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_Version");
b.Property<string>("VDA5050_Key")
.HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_Key");
b.HasKey("Id");
b.ToTable("RobotVDA5050Config");

View File

@ -58,7 +58,7 @@ public class RobotVDA5050Config
public string VDA5050Cer { get; set; }
[Column("VDA5050_Key", TypeName = "nvarchar(64)")]
public string VDA5050_Key { get; set; }
public string VDA5050Key { get; set; }
[Column("CreatedAt", TypeName = "datetime2")]
public DateTime CreatedAt { get; set; }

Binary file not shown.