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 @implements IDisposable
<div class="d-flex w-100 h-100 flex-column"> <div class="d-flex w-100 h-100 flex-column">
<EditForm EditContext="EditContext"> <EditForm EditContext="EditContext">
<DataAnnotationsValidator /> <DataAnnotationsValidator />
@ -71,12 +72,27 @@
<ValidationMessage For="@(() => Model.VDA5050EnableTls)" /> <ValidationMessage For="@(() => Model.VDA5050EnableTls)" />
</div> </div>
<div class="row g-0 mb-2"> <div class="mb-2">
<label class="form-label" for="caFile">CA File</label> <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" /> <div class="custom-file-input-wrapper position-relative">
@if (!string.IsNullOrEmpty(CaFileName)) <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)) @if (!string.IsNullOrEmpty(CaFileError))
{ {
@ -84,12 +100,27 @@
} }
</div> </div>
<div class="row g-0 mb-2"> <div class="mb-2">
<label class="form-label" for="clientCertFile">Client Certificate File</label> <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" /> <div class="custom-file-input-wrapper position-relative">
@if (!string.IsNullOrEmpty(CertFileName)) <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)) @if (!string.IsNullOrEmpty(CertFileError))
{ {
@ -97,12 +128,27 @@
} }
</div> </div>
<div class="row g-0 mb-2"> <div class="mb-2">
<label class="form-label" for="clientKeyFile">Client Key File</label> <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" /> <div class="custom-file-input-wrapper position-relative">
@if (!string.IsNullOrEmpty(KeyFileName)) <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)) @if (!string.IsNullOrEmpty(KeyFileError))
{ {
@ -128,7 +174,6 @@
</div> </div>
</div> </div>
@code { @code {
[Parameter] [Parameter]
public RobotVDA5050ConfigDto Model { get; set; } = new(); public RobotVDA5050ConfigDto Model { get; set; } = new();
@ -136,6 +181,11 @@
[Parameter] [Parameter]
public EventCallback<RobotVDA5050ConfigDto> ModelChanged { get; set; } 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 EditContext? EditContext;
private bool showPassword; private bool showPassword;
private string PasswordInputType => showPassword ? "text" : "password"; private string PasswordInputType => showPassword ? "text" : "password";
@ -143,22 +193,14 @@
private enum FileSlot { Ca, Cert, Key } private enum FileSlot { Ca, Cert, Key }
private string? CaFileName; private string? CaFileInfo;
private long CaFileSize;
private string? CaFileError; private string? CaFileError;
private byte[]? CaFileData;
private string? CertFileName; private string? CertFileInfo;
private long CertFileSize;
private string? CertFileError; private string? CertFileError;
private byte[]? CertFileData;
private string? KeyFileName; private string? KeyFileInfo;
private long KeyFileSize;
private string? KeyFileError; private string? KeyFileError;
private byte[]? KeyFileData;
private const long MaxFileSize = 10 * 1024 * 1024; // 10 MB
private async Task OnFileSelected(InputFileChangeEventArgs e, FileSlot slot) private async Task OnFileSelected(InputFileChangeEventArgs e, FileSlot slot)
{ {
@ -169,7 +211,7 @@
if (file.Size > MaxFileSize) if (file.Size > MaxFileSize)
{ {
SetFileError(slot, $"File too large (max {FormatSize(MaxFileSize)})"); SetFileError(slot, $"File too large (max {FormatSize(MaxFileSize)})");
SetFileInfo(slot, null, 0, null); SetFileInfo(slot, string.Empty, 0);
return; return;
} }
@ -180,30 +222,25 @@
await stream.CopyToAsync(ms); await stream.CopyToAsync(ms);
var data = ms.ToArray(); var data = ms.ToArray();
SetFileData(slot, data, file.Name, file.Size); SetFileError(slot, null);
ClearFileError(slot); SetFileInfo(slot, file.Name, file.Size);
SetFileName(slot, file.Name);
// Optionally propagate change to parent via ModelChanged if required SetBrowserFile(slot, file);
// _ = ModelChanged.InvokeAsync(Model); _ = ModelChanged.InvokeAsync(Model);
} }
catch (Exception ex) catch
{ {
SetFileError(slot, "Failed to read file"); 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) SetFileInfo(slot, string.Empty, 0);
{ SetFileError(slot, null);
case FileSlot.Ca: SetFileName(slot, string.Empty);
CaFileData = data; CaFileName = name; CaFileSize = size; break;
case FileSlot.Cert: _ = ModelChanged.InvokeAsync(Model);
CertFileData = data; CertFileName = name; CertFileSize = size; break;
case FileSlot.Key:
KeyFileData = data; KeyFileName = name; KeyFileSize = size; break;
}
} }
private void SetFileError(FileSlot slot, string? error) 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)
private void SetFileInfo(FileSlot slot, string? name, long size, byte[]? data)
{ {
switch (slot) switch (slot)
{ {
case FileSlot.Ca: CaFileName = name; CaFileSize = size; CaFileData = data; break; case FileSlot.Ca: CaFileInfo = string.IsNullOrEmpty(name) ? "" : $"{name} {FormatSize(size)}"; break;
case FileSlot.Cert: CertFileName = name; CertFileSize = size; CertFileData = data; break; case FileSlot.Cert: CertFileInfo = string.IsNullOrEmpty(name) ? "" : $"{name} {FormatSize(size)}"; break;
case FileSlot.Key: KeyFileName = name; KeyFileSize = size; KeyFileData = data; 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; if (EditContext is not null) EditContext.OnFieldChanged -= EditContext_OnFieldChanged;
EditContext = new EditContext(Model); EditContext = new EditContext(Model);
EditContext.OnFieldChanged += EditContext_OnFieldChanged; 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() private void TogglePasswordVisibility()

View File

@ -40,4 +40,54 @@
.password-toggle-btn .mdi { .password-toggle-btn .mdi {
font-size: 1.15rem; font-size: 1.15rem;
pointer-events: none; /* let clicks hit the button, not the icon */ 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) @switch (SelectedType)
{ {
case RobotConfigType.VDA5050: case RobotConfigType.VDA5050:
<RobotApp.Client.Pages.Components.Config.RobotVDA5050Config @bind-Model="SelectedVda" /> <RobotApp.Client.Pages.Components.Config.RobotVDA5050Config @ref="@RobotVDA5050ConfigRef" @bind-Model="SelectedVda" />
break; break;
case RobotConfigType.Safety: case RobotConfigType.Safety:
<RobotApp.Client.Pages.Components.Config.RobotSafetyConfig @bind-Model="SelectedSafety" /> <RobotApp.Client.Pages.Components.Config.RobotSafetyConfig @bind-Model="SelectedSafety" />
@ -204,6 +204,8 @@
private RobotConfigType SelectedType = RobotConfigType.VDA5050; private RobotConfigType SelectedType = RobotConfigType.VDA5050;
private RobotApp.Client.Pages.Components.Config.RobotVDA5050Config RobotVDA5050ConfigRef = default!;
private int SelectedIndex = -1; private int SelectedIndex = -1;
private bool HasSelection => SelectedIndex >= 0; private bool HasSelection => SelectedIndex >= 0;
private bool IsLoading = false; private bool IsLoading = false;

View File

@ -2,6 +2,7 @@
using RobotApp.Common.Shares; using RobotApp.Common.Shares;
using RobotApp.Common.Shares.Dtos; using RobotApp.Common.Shares.Dtos;
using RobotApp.Common.Shares.Enums; using RobotApp.Common.Shares.Enums;
using System.Net.Http.Headers;
using System.Net.Http.Json; using System.Net.Http.Json;
using System.Xml.Schema; 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() private async Task SaveConfig()
{ {
try try
@ -344,9 +372,14 @@ public partial class RobotConfigManager
SelectedVda.VDA5050PublishRepeat, SelectedVda.VDA5050PublishRepeat,
SelectedVda.VDA5050EnablePassword, SelectedVda.VDA5050EnablePassword,
SelectedVda.VDA5050EnableTls, SelectedVda.VDA5050EnableTls,
SelectedVda.VDA5050CA,
SelectedVda.VDA5050Cer,
SelectedVda.VDA5050Key,
SelectedVda.Description 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; break;
} }

View File

@ -0,0 +1 @@


View File

@ -23,7 +23,7 @@ public record RobotVDA5050ConfigDto
public bool VDA5050EnableTls { get; set; } public bool VDA5050EnableTls { get; set; }
public string VDA5050CA { get; set; } public string VDA5050CA { get; set; }
public string VDA5050Cer { 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 CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; } public DateTime UpdatedAt { get; set; }
public bool IsActive { get; set; } public bool IsActive { get; set; }
@ -45,7 +45,7 @@ public record UpdateRobotVDA5050ConfigDto
public bool VDA5050EnableTls { get; set; } public bool VDA5050EnableTls { get; set; }
public string VDA5050CA { get; set; } public string VDA5050CA { get; set; }
public string VDA5050Cer { get; set; } public string VDA5050Cer { get; set; }
public string VDA5050_Key { get; set; } public string VDA5050Key { get; set; }
public string Description { get; set; } public string Description { get; set; }
} }
@ -63,7 +63,7 @@ public record CreateRobotVDA5050ConfigDto
public bool VDA5050EnableTls { get; set; } public bool VDA5050EnableTls { get; set; }
public string VDA5050CA { get; set; } public string VDA5050CA { get; set; }
public string VDA5050Cer { 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 ConfigName { get; set; }
public string Description { 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["lib/bootstrap/js/bootstrap.min.js"]"></script>
<script src="@Assets["_content/MudBlazor/MudBlazor.min.js"]"></script> <script src="@Assets["_content/MudBlazor/MudBlazor.min.js"]"></script>
<script src="@Assets["js/canvas.js"]"></script> <script src="@Assets["js/canvas.js"]"></script>
<script src="@Assets["js/app.js"]"></script>
</body> </body>
</html> </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.Authorization;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using RobotApp.Common.Shares; using RobotApp.Common.Shares;
@ -196,7 +195,7 @@ public class RobotConfigsController(Services.Logger<RobotConfigsController> Logg
VDA5050EnableTls = config.VDA5050EnableTls, VDA5050EnableTls = config.VDA5050EnableTls,
VDA5050CA = config.VDA5050CA, VDA5050CA = config.VDA5050CA,
VDA5050Cer = config.VDA5050Cer, VDA5050Cer = config.VDA5050Cer,
VDA5050_Key = config.VDA5050_Key, VDA5050Key = config.VDA5050Key,
CreatedAt = config.CreatedAt, CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt, UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive, IsActive = config.IsActive,
@ -234,7 +233,7 @@ public class RobotConfigsController(Services.Logger<RobotConfigsController> Logg
config.VDA5050EnableTls = updateDto.VDA5050EnableTls; config.VDA5050EnableTls = updateDto.VDA5050EnableTls;
config.VDA5050CA = updateDto.VDA5050CA; config.VDA5050CA = updateDto.VDA5050CA;
config.VDA5050Cer = updateDto.VDA5050Cer; config.VDA5050Cer = updateDto.VDA5050Cer;
config.VDA5050_Key = updateDto.VDA5050_Key; config.VDA5050Key = updateDto.VDA5050Key;
config.Description = updateDto.Description ?? config.Description; config.Description = updateDto.Description ?? config.Description;
config.UpdatedAt = DateTime.Now; config.UpdatedAt = DateTime.Now;
@ -276,7 +275,7 @@ public class RobotConfigsController(Services.Logger<RobotConfigsController> Logg
VDA5050EnableTls = createDto.VDA5050EnableTls, VDA5050EnableTls = createDto.VDA5050EnableTls,
VDA5050CA = createDto.VDA5050CA, VDA5050CA = createDto.VDA5050CA,
VDA5050Cer = createDto.VDA5050Cer, VDA5050Cer = createDto.VDA5050Cer,
VDA5050_Key = createDto.VDA5050_Key, VDA5050Key = createDto.VDA5050Key,
CreatedAt = DateTime.Now, CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now, UpdatedAt = DateTime.Now,
IsActive = false IsActive = false
@ -304,7 +303,7 @@ public class RobotConfigsController(Services.Logger<RobotConfigsController> Logg
VDA5050EnableTls = config.VDA5050EnableTls, VDA5050EnableTls = config.VDA5050EnableTls,
VDA5050CA = config.VDA5050CA, VDA5050CA = config.VDA5050CA,
VDA5050Cer = config.VDA5050Cer, VDA5050Cer = config.VDA5050Cer,
VDA5050_Key = config.VDA5050_Key, VDA5050Key = config.VDA5050Key,
CreatedAt = config.CreatedAt, CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt, UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive, 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."); 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)") .HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_HostServer"); .HasColumnName("VDA5050_HostServer");
b.Property<string>("VDA5050Key")
.HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_Key");
b.Property<string>("VDA5050Manufacturer") b.Property<string>("VDA5050Manufacturer")
.HasMaxLength(50) .HasMaxLength(50)
.HasColumnType("nvarchar(64)") .HasColumnType("nvarchar(64)")
@ -509,10 +513,6 @@ namespace RobotApp.Migrations
.HasColumnType("nvarchar(64)") .HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_Version"); .HasColumnName("VDA5050_Version");
b.Property<string>("VDA5050_Key")
.HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_Key");
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("RobotVDA5050Config"); b.ToTable("RobotVDA5050Config");

View File

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

Binary file not shown.