This commit is contained in:
Đăng Nguyễn 2025-11-06 14:14:10 +07:00
parent 99716cc414
commit 8736bad3e7
28 changed files with 931 additions and 146 deletions

View File

@ -1,44 +1,64 @@
<EditForm Model="@Config" OnValidSubmit="OnSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="mb-2">
<label class="form-label">Navigation Type</label>
<InputSelect class="form-select" @bind-Value="Config.NavigationType" TValue="NavigationType">
@foreach (var t in NavigationTypes)
{
<option value="@t">@t</option>
}
</InputSelect>
</div>
@implements IDisposable
<div class="row g-2 mb-2">
<div class="col">
<label class="form-label">Radius (wheel)</label>
<InputNumber class="form-control" @bind-Value="Config.RadiusWheel" />
</div>
<div class="col">
<label class="form-label">Width (m)</label>
<InputNumber class="form-control" @bind-Value="Config.Width" />
</div>
</div>
<div class ="d-flex w-100 h-100 flex-column">
<EditForm EditContext="EditContext">
<DataAnnotationsValidator />
<div class="row g-2 mb-2">
<div class="col">
<label class="form-label">Length (m)</label>
<InputNumber class="form-control" @bind-Value="Config.Length" />
<div class="mb-2">
<label class="form-label">Navigation Type</label>
<InputSelect class="form-select" @bind-Value="Model.NavigationType" TValue="NavigationType">
@foreach (var t in NavigationTypes)
{
<option value="@t">@t</option>
}
</InputSelect>
<ValidationMessage For="@(() => Model.NavigationType)" />
</div>
<div class="col">
<label class="form-label">Height (m)</label>
<InputNumber class="form-control" @bind-Value="Config.Height" />
<div class="row g-2 mb-2">
<div class="col">
<label class="form-label">Radius (wheel)</label>
<InputNumber class="form-control" @bind-Value="Model.RadiusWheel" />
<ValidationMessage For="@(() => Model.RadiusWheel)" />
</div>
<div class="col">
<label class="form-label">Width (m)</label>
<InputNumber class="form-control" @bind-Value="Model.Width" />
<ValidationMessage For="@(() => Model.Width)" />
</div>
</div>
</div>
<div class="mb-2">
<label class="form-label">Description</label>
<InputTextArea class="form-control" @bind-Value="Config.Description" />
</div>
<div class="row g-2 mb-2">
<div class="col">
<label class="form-label">Length (m)</label>
<InputNumber class="form-control" @bind-Value="Model.Length" />
<ValidationMessage For="@(() => Model.Length)" />
</div>
<div class="col">
<label class="form-label">Height (m)</label>
<InputNumber class="form-control" @bind-Value="Model.Height" />
<ValidationMessage For="@(() => Model.Height)" />
</div>
</div>
<div class="mb-2">
<label class="form-label">Description</label>
<InputTextArea class="form-control" @bind-Value="Model.Description" />
<ValidationMessage For="@(() => Model.Description)" />
</div>
</EditForm>
<div class="flex-grow-1" />
<div>
@if (Model.CreatedAt != default || Model.UpdatedAt != default)
{
<div class="d-flex justify-content-end mt-2">
<small class="text-muted">Created: @Model.CreatedAt.ToString("dd/MM/yyyy HH:mm:ss")</small>
<small class="text-muted ms-3">Updated: @Model.UpdatedAt.ToString("dd/MM/yyyy HH:mm:ss")</small>
</div>
}
</div>
</div>
</EditForm>
@code {
[Parameter]
@ -47,17 +67,26 @@
[Parameter]
public EventCallback<RobotConfigDto> ModelChanged { get; set; }
private RobotConfigDto Config = new();
private EditContext? EditContext;
private IEnumerable<NavigationType> NavigationTypes => Enum.GetValues(typeof(NavigationType)).Cast<NavigationType>();
protected override void OnParametersSet()
{
Config = Model ?? new();
if (EditContext is null || !EditContext.Model!.Equals(Model))
{
if (EditContext is not null) EditContext.OnFieldChanged -= EditContext_OnFieldChanged;
EditContext = new EditContext(Model);
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
}
}
private async Task OnSubmit()
private void EditContext_OnFieldChanged(object? sender, FieldChangedEventArgs e)
{
Model = Config;
await ModelChanged.InvokeAsync(Model);
_ = ModelChanged.InvokeAsync(Model);
}
public void Dispose()
{
if (EditContext is not null) EditContext.OnFieldChanged -= EditContext_OnFieldChanged;
}
}

View File

@ -1,32 +1,47 @@
@using RobotApp.Common.Shares.Dtos
@implements IDisposable
<div class="d-flex w-100 h-100 flex-column">
<EditForm EditContext="EditContext">
<DataAnnotationsValidator />
<EditForm Model="@Local" OnValidSubmit="OnSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="row g-2 mb-2">
<div class="col-md-8">
<label class="form-label">PLC Address</label>
<InputText class="form-control" @bind-Value="Local.PLCAddress" />
<div class="row g-2 mb-2">
<div class="col-md-8">
<label class="form-label">PLC Address</label>
<InputText class="form-control" @bind-Value="Model.PLCAddress" />
<ValidationMessage For="@(() => Model.PLCAddress)" />
</div>
<div class="col-md-4">
<label class="form-label">Port</label>
<InputNumber class="form-control" @bind-Value="Model.PLCPort" />
<ValidationMessage For="@(() => Model.PLCPort)" />
</div>
</div>
<div class="col-md-4">
<label class="form-label">Port</label>
<InputNumber class="form-control" @bind-Value="Local.PLCPort" />
<div class="row g-2 mb-2">
<div class="col-md-4">
<label class="form-label">Unit Id</label>
<InputNumber class="form-control" @bind-Value="Model.PLCUnitId" />
<ValidationMessage For="@(() => Model.PLCUnitId)" />
</div>
</div>
</div>
<div class="row g-2 mb-2">
<div class="col-md-4">
<label class="form-label">Unit Id</label>
<InputNumber class="form-control" @bind-Value="Local.PLCUnitId" />
<div class="mb-2">
<label class="form-label">Description</label>
<InputTextArea class="form-control" @bind-Value="Model.Description" />
<ValidationMessage For="@(() => Model.Description)" />
</div>
</div>
<div class="mb-2">
<label class="form-label">Description</label>
<InputTextArea class="form-control" @bind-Value="Local.Description" />
</EditForm>
<div class="flex-grow-1" />
<div>
@if (Model.CreatedAt != default || Model.UpdatedAt != default)
{
<div class="d-flex justify-content-end mt-2">
<small class="text-muted">Created: @Model.CreatedAt.ToString("dd/MM/yyyy HH:mm:ss")</small>
<small class="text-muted ms-3">Updated: @Model.UpdatedAt.ToString("dd/MM/yyyy HH:mm:ss")</small>
</div>
}
</div>
</EditForm>
</div>
@code {
[Parameter]
@ -35,16 +50,25 @@
[Parameter]
public EventCallback<RobotPlcConfigDto> ModelChanged { get; set; }
private RobotPlcConfigDto Local = new();
private EditContext? EditContext;
protected override void OnParametersSet()
{
Local = Model is not null ? Model with { } : new RobotPlcConfigDto();
if (EditContext is null || !EditContext.Model!.Equals(Model))
{
if (EditContext is not null) EditContext.OnFieldChanged -= EditContext_OnFieldChanged;
EditContext = new EditContext(Model);
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
}
}
private async Task OnSubmit()
private void EditContext_OnFieldChanged(object? sender, FieldChangedEventArgs e)
{
Model = Local;
await ModelChanged.InvokeAsync(Model);
_ = ModelChanged.InvokeAsync(Model);
}
public void Dispose()
{
if (EditContext is not null) EditContext.OnFieldChanged -= EditContext_OnFieldChanged;
}
}

View File

@ -1,50 +1,70 @@
<EditForm Model="@Local" OnValidSubmit="OnSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="row g-2 mb-2">
<div class="col-6">
<label class="form-label">Very Slow (m/s)</label>
<InputNumber class="form-control" @bind-Value="Local.SafetySpeedVerySlow" />
@implements IDisposable
<div class="d-flex w-100 h-100 flex-column">
<EditForm EditContext="EditContext">
<DataAnnotationsValidator />
<div class="row g-2 mb-2">
<div class="col-6">
<label class="form-label">Very Slow (m/s)</label>
<InputNumber class="form-control" @bind-Value="Model.SafetySpeedVerySlow" />
<ValidationMessage For="@(() => Model.SafetySpeedVerySlow)" />
</div>
<div class="col-6">
<label class="form-label">Slow (m/s)</label>
<InputNumber class="form-control" @bind-Value="Model.SafetySpeedSlow" />
<ValidationMessage For="@(() => Model.SafetySpeedSlow)" />
</div>
</div>
<div class="col-6">
<label class="form-label">Slow (m/s)</label>
<InputNumber class="form-control" @bind-Value="Local.SafetySpeedSlow" />
</div>
</div>
<div class="row g-2 mb-2">
<div class="col-6">
<label class="form-label">Normal (m/s)</label>
<InputNumber class="form-control" @bind-Value="Local.SafetySpeedNormal" />
<div class="row g-2 mb-2">
<div class="col-6">
<label class="form-label">Normal (m/s)</label>
<InputNumber class="form-control" @bind-Value="Model.SafetySpeedNormal" />
<ValidationMessage For="@(() => Model.SafetySpeedNormal)" />
</div>
<div class="col-6">
<label class="form-label">Medium (m/s)</label>
<InputNumber class="form-control" @bind-Value="Model.SafetySpeedMedium" />
<ValidationMessage For="@(() => Model.SafetySpeedMedium)" />
</div>
</div>
<div class="col-6">
<label class="form-label">Medium (m/s)</label>
<InputNumber class="form-control" @bind-Value="Local.SafetySpeedMedium" />
<div class="row g-2 mb-2">
<div class="col-6">
<label class="form-label">Optimal (m/s)</label>
<InputNumber class="form-control" @bind-Value="Model.SafetySpeedOptimal" />
<ValidationMessage For="@(() => Model.SafetySpeedOptimal)" />
</div>
<div class="col-6">
<label class="form-label">Fast (m/s)</label>
<InputNumber class="form-control" @bind-Value="Model.SafetySpeedFast" />
<ValidationMessage For="@(() => Model.SafetySpeedFast)" />
</div>
</div>
</div>
<div class="row g-2 mb-2">
<div class="col-6">
<label class="form-label">Optimal (m/s)</label>
<InputNumber class="form-control" @bind-Value="Local.SafetySpeedOptimal" />
<div class="mb-2">
<label class="form-label">Very Fast (m/s)</label>
<InputNumber class="form-control" @bind-Value="Model.SafetySpeedVeryFast" />
<ValidationMessage For="@(() => Model.SafetySpeedVeryFast)" />
</div>
<div class="col-6">
<label class="form-label">Fast (m/s)</label>
<InputNumber class="form-control" @bind-Value="Local.SafetySpeedFast" />
<div class="mb-2">
<label class="form-label">Description</label>
<InputTextArea class="form-control" @bind-Value="Model.Description" />
<ValidationMessage For="@(() => Model.Description)" />
</div>
</EditForm>
<div class="flex-grow-1" />
<div>
@if (Model.CreatedAt != default || Model.UpdatedAt != default)
{
<div class="d-flex justify-content-end mt-2">
<small class="text-muted">Created: @Model.CreatedAt.ToString("dd/MM/yyyy HH:mm:ss")</small>
<small class="text-muted ms-3">Updated: @Model.UpdatedAt.ToString("dd/MM/yyyy HH:mm:ss")</small>
</div>
}
</div>
</div>
<div class="mb-2">
<label class="form-label">Very Fast (m/s)</label>
<InputNumber class="form-control" @bind-Value="Local.SafetySpeedVeryFast" />
</div>
<div class="mb-2">
<label class="form-label">Description</label>
<InputTextArea class="form-control" @bind-Value="Local.Description" />
</div>
</EditForm>
@code {
[Parameter]
@ -53,17 +73,25 @@
[Parameter]
public EventCallback<RobotSafetyConfigDto> ModelChanged { get; set; }
private RobotSafetyConfigDto Local = new();
private EditContext? EditContext;
protected override void OnParametersSet()
{
// Work on a shallow copy (record) so parent instance isn't mutated until submit
Local = Model is not null ? Model with { } : new RobotSafetyConfigDto();
if (EditContext is null || !EditContext.Model!.Equals(Model))
{
if (EditContext is not null) EditContext.OnFieldChanged -= EditContext_OnFieldChanged;
EditContext = new EditContext(Model);
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
}
}
private async Task OnSubmit()
private void EditContext_OnFieldChanged(object? sender, FieldChangedEventArgs e)
{
Model = Local;
await ModelChanged.InvokeAsync(Model);
_ = ModelChanged.InvokeAsync(Model);
}
public void Dispose()
{
if (EditContext is not null) EditContext.OnFieldChanged -= EditContext_OnFieldChanged;
}
}

View File

@ -1,6 +1,4 @@
@using RobotApp.Common.Shares.Dtos
@implements IDisposable
@implements IDisposable
<div class="d-flex w-100 h-100 flex-column">
<EditForm EditContext="EditContext">

View File

@ -1,14 +1,20 @@
@implements IDisposable
<div class="d-flex w-100 h-100 flex-column">
<EditForm EditContext="EditContext">
<DataAnnotationsValidator />
<div class="mb-2">
<label class="form-label" for="serialNumber">Serial Number</label>
<InputText id="serialNumber" class="form-control" @bind-Value="Model.SerialNumber" />
<ValidationMessage For="@(() => Model.SerialNumber)" />
<div class="row g-2 mb-2">
<div class="col-md-6">
<label class="form-label" for="serialNumber">Serial Number</label>
<InputText id="serialNumber" class="form-control" @bind-Value="Model.SerialNumber" />
<ValidationMessage For="@(() => Model.SerialNumber)" />
</div>
<div class="col-md-6">
<label class="form-label" for="prefix">Topic Prefix</label>
<InputText id="prefix" class="form-control" @bind-Value="Model.VDA5050TopicPrefix"/>
<ValidationMessage For="@(() => Model.VDA5050TopicPrefix)" />
</div>
</div>
<div class="row g-2 mb-2">

View File

@ -201,6 +201,7 @@ public partial class RobotConfigManager
template.VDA5050Password,
template.VDA5050Manufacturer,
template.VDA5050Version,
template.VDA5050TopicPrefix,
template.VDA5050PublishRepeat,
template.VDA5050EnablePassword,
template.VDA5050EnableTls
@ -373,6 +374,7 @@ public partial class RobotConfigManager
SelectedVda.VDA5050Password,
SelectedVda.VDA5050Manufacturer,
SelectedVda.VDA5050Version,
SelectedVda.VDA5050TopicPrefix,
SelectedVda.VDA5050PublishRepeat,
SelectedVda.VDA5050EnablePassword,
SelectedVda.VDA5050EnableTls,

View File

@ -1,4 +1,5 @@
using RobotApp.Common.Shares.Enums;
using System.ComponentModel.DataAnnotations;
namespace RobotApp.Common.Shares.Dtos;
@ -8,13 +9,18 @@ public record RobotConfigDto
{
public Guid Id { get; set; }
public NavigationType NavigationType { get; set; }
[Range(0.1, 10, ErrorMessage = "Value must be from 0.1 to 10")]
public double RadiusWheel { get; set; }
[Range(0.1, 10, ErrorMessage = "Value must be from 0.1 to 10")]
public double Width { get; set; }
[Range(0.1, 10, ErrorMessage = "Value must be from 0.1 to 10")]
public double Length { get; set; }
[Range(0.1, 10, ErrorMessage = "Value must be from 0.1 to 10")]
public double Height { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public bool IsActive { get; set; }
[Required]
public string ConfigName { get; set; }
public string Description { get; set; }
}

View File

@ -1,16 +1,22 @@
namespace RobotApp.Common.Shares.Dtos;
using System.ComponentModel.DataAnnotations;
namespace RobotApp.Common.Shares.Dtos;
#nullable disable
public record RobotPlcConfigDto
{
public Guid Id { get; set; }
[Required]
public string PLCAddress { get; set; }
[Range(1, 65535, ErrorMessage = "Value must be from 1 to 65535")]
public int PLCPort { get; set; }
[Range(1, 65535, ErrorMessage = "Value must be from 1 to 65535")]
public int PLCUnitId { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public bool IsActive { get; set; }
[Required]
public string ConfigName { get; set; }
public string Description { get; set; }
}

View File

@ -1,4 +1,6 @@
namespace RobotApp.Common.Shares.Dtos;
using System.ComponentModel.DataAnnotations;
namespace RobotApp.Common.Shares.Dtos;
#nullable disable
@ -6,15 +8,22 @@ public record RobotSafetyConfigDto
{
public Guid Id { get; set; }
public double SafetySpeedVerySlow { get; set; }
[Range(0, 10, ErrorMessage = "Value must be from 0 to 10")]
public double SafetySpeedSlow { get; set; }
[Range(0, 10, ErrorMessage = "Value must be from 0 to 10")]
public double SafetySpeedNormal { get; set; }
[Range(0, 10, ErrorMessage = "Value must be from 0 to 10")]
public double SafetySpeedMedium { get; set; }
[Range(0, 10, ErrorMessage = "Value must be from 0 to 10")]
public double SafetySpeedOptimal { get; set; }
[Range(0, 10, ErrorMessage = "Value must be from 0 to 10")]
public double SafetySpeedFast { get; set; }
[Range(0, 10, ErrorMessage = "Value must be from 0 to 10")]
public double SafetySpeedVeryFast { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public bool IsActive { get; set; }
[Required]
public string ConfigName { get; set; }
public string Description { get; set; }
}

View File

@ -14,11 +14,11 @@ public record RobotVDA5050ConfigDto
[Required]
[Range(1, 65535, ErrorMessage = "Value must be from 1 to 65535")]
public int VDA5050Port { get; set; }
[Required]
public string VDA5050UserName { get; set; }
public string VDA5050Password { get; set; }
public string VDA5050Manufacturer { get; set; }
public string VDA5050Version { get; set; }
public string VDA5050TopicPrefix { get; set; }
[Range(1, 65535, ErrorMessage = "Value must be from 1 to 65535")]
public int VDA5050PublishRepeat { get; set; }
public bool VDA5050EnablePassword { get; set; }
@ -29,6 +29,7 @@ public record RobotVDA5050ConfigDto
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public bool IsActive { get; set; }
[Required]
public string ConfigName { get; set; }
public string Description { get; set; }
}
@ -42,6 +43,7 @@ public record UpdateRobotVDA5050ConfigDto
public string VDA5050Password { get; set; }
public string VDA5050Manufacturer { get; set; }
public string VDA5050Version { get; set; }
public string VDA5050TopicPrefix { get; set; }
public int VDA5050PublishRepeat { get; set; }
public bool VDA5050EnablePassword { get; set; }
public bool VDA5050EnableTls { get; set; }
@ -60,6 +62,7 @@ public record CreateRobotVDA5050ConfigDto
public string VDA5050Password { get; set; }
public string VDA5050Manufacturer { get; set; }
public string VDA5050Version { get; set; }
public string VDA5050TopicPrefix { get; set; }
public int VDA5050PublishRepeat { get; set; }
public bool VDA5050EnablePassword { get; set; }
public bool VDA5050EnableTls { get; set; }

View File

@ -1,6 +1,4 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace RobotApp.VDA5050;
namespace RobotApp.VDA5050;
public class VDA5050Setting
@ -17,4 +15,5 @@ public class VDA5050Setting
public string? CAFile { get; set; }
public string? CerFile { get; set; }
public string? KeyFile { get; set; }
public string? TopicPrefix { get; set; }
}

View File

@ -186,6 +186,7 @@ public class RobotConfigsController(Services.Logger<RobotConfigsController> Logg
VDA5050Password = config.VDA5050Password,
VDA5050Manufacturer = config.VDA5050Manufacturer,
VDA5050Version = config.VDA5050Version,
VDA5050TopicPrefix = config.VDA5050TopicPrefix,
VDA5050PublishRepeat = config.VDA5050PublishRepeat,
VDA5050EnablePassword = config.VDA5050EnablePassword,
VDA5050EnableTls = config.VDA5050EnableTls,
@ -220,10 +221,11 @@ public class RobotConfigsController(Services.Logger<RobotConfigsController> Logg
config.SerialNumber = updateDto.SerialNumber ?? config.SerialNumber;
config.VDA5050HostServer = updateDto.VDA5050HostServer ?? config.VDA5050HostServer;
config.VDA5050Port = updateDto.VDA5050Port;
config.VDA5050UserName = updateDto.VDA5050UserName ?? config.VDA5050UserName;
config.VDA5050Password = updateDto.VDA5050Password ?? config.VDA5050Password;
config.VDA5050UserName = updateDto.VDA5050UserName;
config.VDA5050Password = updateDto.VDA5050Password;
config.VDA5050Manufacturer = updateDto.VDA5050Manufacturer ?? config.VDA5050Manufacturer;
config.VDA5050Version = updateDto.VDA5050Version ?? config.VDA5050Version;
config.VDA5050TopicPrefix = updateDto.VDA5050TopicPrefix;
config.VDA5050PublishRepeat = updateDto.VDA5050PublishRepeat;
config.VDA5050EnablePassword = updateDto.VDA5050EnablePassword;
config.VDA5050EnableTls = updateDto.VDA5050EnableTls;
@ -264,6 +266,7 @@ public class RobotConfigsController(Services.Logger<RobotConfigsController> Logg
VDA5050Password = createDto.VDA5050Password,
VDA5050Manufacturer = createDto.VDA5050Manufacturer,
VDA5050Version = createDto.VDA5050Version,
VDA5050TopicPrefix = createDto.VDA5050TopicPrefix,
VDA5050PublishRepeat = createDto.VDA5050PublishRepeat,
VDA5050EnablePassword = createDto.VDA5050EnablePassword,
VDA5050EnableTls = createDto.VDA5050EnableTls,
@ -292,6 +295,7 @@ public class RobotConfigsController(Services.Logger<RobotConfigsController> Logg
VDA5050Password = config.VDA5050Password,
VDA5050Manufacturer = config.VDA5050Manufacturer,
VDA5050Version = config.VDA5050Version,
VDA5050TopicPrefix = config.VDA5050TopicPrefix,
VDA5050PublishRepeat = config.VDA5050PublishRepeat,
VDA5050EnablePassword = config.VDA5050EnablePassword,
VDA5050EnableTls = config.VDA5050EnableTls,

View File

@ -0,0 +1,582 @@
// <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("20251106025457_UpdateVDA5050St")]
partial class UpdateVDA5050St
{
/// <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>("VDA5050TopicPrefix")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_TopicPrefix");
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,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace RobotApp.Data.Migrations
{
/// <inheritdoc />
public partial class UpdateVDA5050St : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "VDA5050_TopicPrefix",
table: "RobotVDA5050Config",
type: "nvarchar(64)",
maxLength: 64,
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "VDA5050_TopicPrefix",
table: "RobotVDA5050Config");
}
}
}

View File

@ -503,6 +503,11 @@ namespace RobotApp.Migrations
.HasColumnType("int")
.HasColumnName("VDA5050_PublishRepeat");
b.Property<string>("VDA5050TopicPrefix")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)")
.HasColumnName("VDA5050_TopicPrefix");
b.Property<string>("VDA5050UserName")
.HasMaxLength(50)
.HasColumnType("nvarchar(64)")

View File

@ -42,6 +42,10 @@ public class RobotVDA5050Config
[MaxLength(20)]
public string VDA5050Version { get; set; }
[Column("VDA5050_TopicPrefix", TypeName = "nvarchar(64)")]
[MaxLength(64)]
public string VDA5050TopicPrefix { get; set; }
[Column("VDA5050_PublishRepeat", TypeName = "int")]
public int VDA5050PublishRepeat { get; set; }

View File

@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using MudBlazor.Services;
using NLog.Web;
using RobotApp.Components;
using RobotApp.Components.Account;
using RobotApp.Data;
@ -9,6 +10,7 @@ using RobotApp.Services;
using RobotApp.Services.Robot.Simulation;
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseNLog();
// Add services to the container.
builder.Services.AddRazorComponents()

View File

@ -33,6 +33,8 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MQTTnet" Version="5.0.1.1416" />
<PackageReference Include="NLog" Version="6.0.5" />
<PackageReference Include="NLog.Web.AspNetCore" Version="6.0.5" />
</ItemGroup>
<ItemGroup>

View File

@ -24,6 +24,9 @@ public class MQTTClient : IAsyncDisposable
public event Action<string>? InstanceActionsChanged;
public bool IsConnected => !IsDisposed && MqttClient is not null && MqttClient.IsConnected;
private string OrderTopic => $"{VDA5050Setting.TopicPrefix}/{ClientId}/{VDA5050Topic.ORDER.ToTopicString()}";
private string InstanceActionsTopic => $"{VDA5050Setting.TopicPrefix}/{ClientId}/{VDA5050Topic.INSTANTACTIONS.ToTopicString()}";
public MQTTClient(string clientId, VDA5050Setting setting, Logger<MQTTClient> logger)
{
VDA5050Setting = setting;
@ -38,8 +41,8 @@ public class MQTTClient : IAsyncDisposable
.WithCleanSession(true)
.Build();
MqttClientSubscribeOptions = MqttClientFactory.CreateSubscribeOptionsBuilder()
.WithTopicFilter(f => f.WithTopic(VDA5050Topic.ORDER.ToTopicString()))
.WithTopicFilter(f => f.WithTopic(VDA5050Topic.INSTANTACTIONS.ToTopicString()))
.WithTopicFilter(f => f.WithTopic(OrderTopic))
.WithTopicFilter(f => f.WithTopic(InstanceActionsTopic))
.Build();
}
@ -238,10 +241,10 @@ public class MQTTClient : IAsyncDisposable
if (IsDisposed) return Task.CompletedTask;
var stringData = Encoding.UTF8.GetString(args.ApplicationMessage.Payload);
VDA5050Topic topic = EnumExtensions.ToTopic(args.ApplicationMessage.Topic);
//VDA5050Topic topic = EnumExtensions.ToTopic(args.ApplicationMessage.Topic);
if (topic == VDA5050Topic.ORDER) OrderChanged?.Invoke(stringData);
else if (topic == VDA5050Topic.INSTANTACTIONS) InstanceActionsChanged?.Invoke(stringData);
if (args.ApplicationMessage.Topic == OrderTopic) OrderChanged?.Invoke(stringData);
else if (args.ApplicationMessage.Topic == InstanceActionsTopic) InstanceActionsChanged?.Invoke(stringData);
}
catch (Exception ex)
{

View File

@ -6,13 +6,13 @@ public class RobotBattery(RobotConfiguration RobotConfiguration) : IBattery
{
public bool IsReady { get; private set; } = RobotConfiguration.IsSimulation;
public double Voltage { get; private set; }
public double Voltage { get; private set; } = RobotConfiguration.IsSimulation ? 24 : 0;
public double Current { get; private set; }
public double SOC { get; private set; }
public double SOC { get; private set; } = RobotConfiguration.IsSimulation ? 100 : 0;
public double SOH { get; private set; }
public double SOH { get; private set; } = RobotConfiguration.IsSimulation ? 100 : 0;
public BatteryStatus Status { get; private set; }

View File

@ -46,6 +46,7 @@ public class RobotConfiguration(IServiceProvider ServiceProvider, Logger<RobotCo
VDA5050Setting.Password = config.VDA5050Password;
VDA5050Setting.Manufacturer = config.VDA5050Manufacturer;
VDA5050Setting.Version = config.VDA5050Version;
VDA5050Setting.TopicPrefix = config.VDA5050TopicPrefix;
VDA5050Setting.PublishRepeat = config.VDA5050PublishRepeat;
VDA5050Setting.EnablePassword = config.VDA5050EnablePassword;
VDA5050Setting.EnableTls = config.VDA5050EnableTls;

View File

@ -22,6 +22,7 @@ public class RobotConnection(RobotConfiguration RobotConfiguration,
{
try
{
Logger.Debug($"Nhận Order: {data}");
var msg = JsonSerializer.Deserialize<OrderMsg>(data, JsonOptionExtends.Read);
if (msg is null || string.IsNullOrEmpty(msg.SerialNumber) || msg.SerialNumber != RobotConfiguration.SerialNumber) return;
OrderUpdated?.Invoke(msg);
@ -36,6 +37,7 @@ public class RobotConnection(RobotConfiguration RobotConfiguration,
{
try
{
Logger.Debug($"Nhận InstanceActions: {data}");
var msg = JsonSerializer.Deserialize<InstantActionsMsg>(data, JsonOptionExtends.Read);
if (msg is null || string.IsNullOrEmpty(msg.SerialNumber) || msg.SerialNumber != RobotConfiguration.SerialNumber) return;
ActionUpdated?.Invoke(msg);
@ -48,7 +50,7 @@ public class RobotConnection(RobotConfiguration RobotConfiguration,
public async Task<MessageResult> Publish(string topic, string data)
{
if (MqttClient is not null && MqttClient.IsConnected) return await MqttClient.PublishAsync(topic, data);
if (MqttClient is not null && MqttClient.IsConnected) return await MqttClient.PublishAsync($"{VDA5050Setting.TopicPrefix}/{RobotConfiguration.SerialNumber}/{topic}", data);
return new(false, "Chưa có kết nối tới broker");
}

View File

@ -42,6 +42,8 @@ public class RobotFactsheet(RobotConnection RobotConnection, RobotConfiguration
FactSheetMsg factSheet = new()
{
SerialNumber = RobotConfiguration.SerialNumber,
Manufacturer = RobotConfiguration.VDA5050Setting.Manufacturer,
Version = RobotConfiguration.VDA5050Setting.Version,
ProtocolFeatures = new()
{
AgvActions = [..AgvActions.Values],
@ -59,7 +61,7 @@ public class RobotFactsheet(RobotConnection RobotConnection, RobotConfiguration
if (RobotConnection.IsConnected) break;
await Task.Delay(1000);
}
await PubFactsheet();
//await PubFactsheet();
}
public readonly static AgvAction StartPause = new()

View File

@ -1,12 +1,14 @@
using RobotApp.Common.Shares.Enums;
using RobotApp.Interfaces;
using RobotApp.Services.Exceptions;
using RobotApp.Services.Robot.Actions;
using RobotApp.Services.State;
using RobotApp.VDA5050.InstantAction;
using RobotApp.VDA5050.Order;
using RobotApp.VDA5050.State;
using System.Collections.Concurrent;
using System.Data;
using System.Threading.Tasks;
using Action = RobotApp.VDA5050.InstantAction.Action;
namespace RobotApp.Services.Robot;
@ -154,6 +156,8 @@ public class RobotOrderController(INavigation NavigationManager,
if (NavigationManager.State != NavigationState.Idle) throw new OrderException(RobotErrors.Error1012(NavigationManager.State));
OrderActions.Clear();
LastNode = null;
for (int i = 0; i < order.Nodes.Length; i++)
{
if (order.Nodes[i].Actions is not null && order.Nodes[i].Actions.Length > 0)
@ -184,11 +188,11 @@ public class RobotOrderController(INavigation NavigationManager,
if (i < order.Nodes.Length - 1 && order.Edges[i].SequenceId != i) throw new OrderException(RobotErrors.Error1011(order.Edges[i].EdgeId, order.Edges[i].SequenceId, i));
if (order.Nodes[i].Released) CurrentBaseNode = order.Nodes[i];
}
ActionManager.ClearInstantActions();
SafetyManager.OnSafetySpeedChanged += OnSafetySpeedChanged;
ActionManager.ClearInstantActions();
if (OrderActions.Count > 0) ActionManager.AddOrderActions([.. OrderActions.Values.SelectMany(a => a)]);
NavigationManager.Move(order.Nodes, order.Edges);
NavigationManager.OnNavigationFinished += NavigationFinished;
OrderId = order.OrderId;
@ -267,7 +271,8 @@ public class RobotOrderController(INavigation NavigationManager,
if (ActionHard is not null)
{
var robotAction = ActionManager[ActionHard.ActionId];
if (robotAction is null || (robotAction is not null && robotAction.IsCompleted))
if (robotAction is null) return;
if (robotAction is not null && robotAction.IsCompleted)
{
NavigationManager.Resume();
ActionHard = null;
@ -282,6 +287,12 @@ public class RobotOrderController(INavigation NavigationManager,
{
if (ActionWaitingRunning.TryDequeue(out Action? action) && action is not null)
{
var robotAction = ActionManager[action.ActionId];
if (robotAction is null)
{
ActionWaitingRunning.Enqueue(action);
return;
}
ActionManager.StartOrderAction(action.ActionId);
ActionHard = action.BlockingType == BlockingType.HARD.ToString() ? action : null;
}

View File

@ -20,7 +20,7 @@ public class RobotStates(RobotConfiguration RobotConfiguration,
ILocalization LocalizationManager,
IBattery BatteryManager,
ILoad LoadManager,
IDriver DriverManager) : BackgroundService
INavigation NavigationManager) : BackgroundService
{
private uint HeaderId = 0;
@ -41,6 +41,8 @@ public class RobotStates(RobotConfiguration RobotConfiguration,
return new StateMsg
{
HeaderId = HeaderId++,
Manufacturer = RobotConfiguration.VDA5050Setting.Manufacturer,
Version = RobotConfiguration.VDA5050Setting.Version,
SerialNumber = RobotConfiguration.SerialNumber,
Maps = [],
OrderId = OrderManager.OrderId,
@ -72,16 +74,16 @@ public class RobotStates(RobotConfiguration RobotConfiguration,
{
Charging = BatteryManager.IsCharging,
BatteryHealth = BatteryManager.SOH,
Reach = BatteryManager.RemainingCapacity,
Reach = 0,
BatteryVoltage = BatteryManager.Voltage,
BatteryCharge = BatteryManager.SOC,
},
Loads = LoadManager.Load,
Velocity = new()
{
Vx = DriverManager.LinearVelocity,
Vy = 0,
Omega = DriverManager.AngularVelocity,
Vx = NavigationManager.VelocityX,
Vy = NavigationManager.VelocityY,
Omega = NavigationManager.Omega,
},
SafetyState = new()
{

View File

@ -16,6 +16,8 @@ public class RobotVisualization(ILocalization Localization, INavigation Navigati
return new VisualizationMsg()
{
HeaderId = HeaderId++,
Manufacturer = RobotConfiguration.VDA5050Setting.Manufacturer,
Version = RobotConfiguration.VDA5050Setting.Version,
SerialNumber = RobotConfiguration.SerialNumber,
MapId = Localization.CurrentActiveMap,
MapDescription = string.Empty,

24
RobotApp/nlog.config Normal file
View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true">
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<targets>
<target xsi:type="File" name="logFile" fileName="${basedir}/logs/${shortdate}.log" maxArchiveFiles="90" archiveEvery="Day" >
<layout type='JsonLayout'>
<attribute name='time' layout='${date:format=HH\:mm\:ss.ffff}' />
<attribute name='level' layout='${level:upperCase=true}'/>
<attribute name='logger' layout='${logger}' />
<attribute name='message' layout='${message}' />
<attribute name='exception' layout='${exception:format=tostring}' />
</layout>
</target>
</targets>
<rules>
<logger name="RobotApp.*" minlevel="Debug" writeto="logFile" />
</rules>
</nlog>

Binary file not shown.