This commit is contained in:
Đăng Nguyễn 2025-10-30 13:34:44 +07:00
parent 643a34a4b4
commit aa2146e383
43 changed files with 3637 additions and 96 deletions

View File

@ -71,6 +71,7 @@
public NavModel[] Navs = [
new(){Icon = "mdi-view-dashboard", Path="/", Label = "Dashboard", Match = NavLinkMatch.All},
new(){Icon = "mdi-map-legend", Path="/maps-manager", Label = "Mapping", Match = NavLinkMatch.All},
new(){Icon = "mdi-application-cog", Path="/robot-config", Label = "Config", Match = NavLinkMatch.All},
];
private bool collapseNavMenu = true;

View File

@ -0,0 +1,63 @@
<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>
<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="row g-2 mb-2">
<div class="col">
<label class="form-label">Length (m)</label>
<InputNumber class="form-control" @bind-Value="Config.Length" />
</div>
<div class="col">
<label class="form-label">Height (m)</label>
<InputNumber class="form-control" @bind-Value="Config.Height" />
</div>
</div>
<div class="mb-2">
<label class="form-label">Description</label>
<InputTextArea class="form-control" @bind-Value="Config.Description" />
</div>
</EditForm>
@code {
[Parameter]
public RobotConfigDto Model { get; set; } = new();
[Parameter]
public EventCallback<RobotConfigDto> ModelChanged { get; set; }
private RobotConfigDto Config = new();
private IEnumerable<NavigationType> NavigationTypes => Enum.GetValues(typeof(NavigationType)).Cast<NavigationType>();
protected override void OnParametersSet()
{
Config = Model ?? new();
}
private async Task OnSubmit()
{
Model = Config;
await ModelChanged.InvokeAsync(Model);
}
}

View File

@ -0,0 +1,50 @@
@using RobotApp.Common.Shares.Dtos
<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>
<div class="col-md-4">
<label class="form-label">Port</label>
<InputNumber class="form-control" @bind-Value="Local.PLCPort" />
</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>
</div>
<div class="mb-2">
<label class="form-label">Description</label>
<InputTextArea class="form-control" @bind-Value="Local.Description" />
</div>
</EditForm>
@code {
[Parameter]
public RobotPlcConfigDto Model { get; set; } = new();
[Parameter]
public EventCallback<RobotPlcConfigDto> ModelChanged { get; set; }
private RobotPlcConfigDto Local = new();
protected override void OnParametersSet()
{
Local = Model is not null ? Model with { } : new RobotPlcConfigDto();
}
private async Task OnSubmit()
{
Model = Local;
await ModelChanged.InvokeAsync(Model);
}
}

View File

@ -0,0 +1,69 @@
<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" />
</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>
<div class="col-6">
<label class="form-label">Medium (m/s)</label>
<InputNumber class="form-control" @bind-Value="Local.SafetySpeedMedium" />
</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>
<div class="col-6">
<label class="form-label">Fast (m/s)</label>
<InputNumber class="form-control" @bind-Value="Local.SafetySpeedFast" />
</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]
public RobotSafetyConfigDto Model { get; set; } = new();
[Parameter]
public EventCallback<RobotSafetyConfigDto> ModelChanged { get; set; }
private RobotSafetyConfigDto Local = new();
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();
}
private async Task OnSubmit()
{
Model = Local;
await ModelChanged.InvokeAsync(Model);
}
}

View File

@ -0,0 +1,59 @@
@using RobotApp.Common.Shares.Dtos
<EditForm Model="@Local" OnValidSubmit="OnSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="form-check mb-2">
<InputCheckbox class="form-check-input" @bind-Value="Local.EnableSimulation" />
<label class="form-check-label">Enable Simulation</label>
</div>
<div class="row g-2 mb-2">
<div class="col-md-6">
<label class="form-label">Max Velocity (m/s)</label>
<InputNumber class="form-control" @bind-Value="Local.SimulationMaxVelocity" />
</div>
<div class="col-md-6">
<label class="form-label">Max Angular Velocity (rad/s)</label>
<InputNumber class="form-control" @bind-Value="Local.SimulationMaxAngularVelocity" />
</div>
</div>
<div class="row g-2 mb-2">
<div class="col-md-6">
<label class="form-label">Acceleration (m/s²)</label>
<InputNumber class="form-control" @bind-Value="Local.SimulationAcceleration" />
</div>
<div class="col-md-6">
<label class="form-label">Deceleration (m/s²)</label>
<InputNumber class="form-control" @bind-Value="Local.SimulationDeceleration" />
</div>
</div>
<div class="mb-2">
<label class="form-label">Description</label>
<InputTextArea class="form-control" @bind-Value="Local.Description" />
</div>
</EditForm>
@code {
[Parameter]
public RobotSimulationConfigDto Model { get; set; } = new();
[Parameter]
public EventCallback<RobotSimulationConfigDto> ModelChanged { get; set; }
private RobotSimulationConfigDto Local = new();
protected override void OnParametersSet()
{
// Use record 'with' to create a shallow copy so parent isn't mutated until submit
Local = Model is not null ? Model with { } : new RobotSimulationConfigDto();
}
private async Task OnSubmit()
{
Model = Local;
await ModelChanged.InvokeAsync(Model);
}
}

View File

@ -0,0 +1,81 @@
<EditForm Model="@Local" OnValidSubmit="OnSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="row g-2 mb-2">
<div class="col-md-6">
<label class="form-label">Serial Number</label>
<InputText class="form-control" @bind-Value="Local.SerialNumber" />
</div>
<div class="col-md-6">
<label class="form-label">Manufacturer</label>
<InputText class="form-control" @bind-Value="Local.VDA5050Manufacturer" />
</div>
</div>
<div class="row g-2 mb-2">
<div class="col-md-6">
<label class="form-label">Host</label>
<InputText class="form-control" @bind-Value="Local.VDA5050HostServer" />
</div>
<div class="col-md-3">
<label class="form-label">Port</label>
<InputNumber class="form-control" @bind-Value="Local.VDA5050Port" />
</div>
<div class="col-md-3">
<label class="form-label">Publish Repeat</label>
<InputNumber class="form-control" @bind-Value="Local.VDA5050PublishRepeat" />
</div>
</div>
<div class="row g-2 mb-2">
<div class="col-md-6">
<label class="form-label">Username</label>
<InputText class="form-control" @bind-Value="Local.VDA5050UserName" />
</div>
<div class="col-md-6">
<label class="form-label">Password</label>
<InputText class="form-control" @bind-Value="Local.VDA5050Password" type="password" />
</div>
</div>
<div class="mb-2">
<label class="form-label">Version</label>
<InputText class="form-control" @bind-Value="Local.VDA5050Version" />
</div>
<div class="form-check mb-2">
<InputCheckbox class="form-check-input" @bind-Value="Local.VDA5050EnablePassword" />
<label class="form-check-label">Enable Password</label>
</div>
<div class="form-check mb-2">
<InputCheckbox class="form-check-input" @bind-Value="Local.VDA5050EnableTls" />
<label class="form-check-label">Enable TLS</label>
</div>
<div class="mb-2">
<label class="form-label">Description</label>
<InputTextArea class="form-control" @bind-Value="Local.Description" />
</div>
</EditForm>
@code {
[Parameter]
public RobotVDA5050ConfigDto Model { get; set; } = new();
[Parameter]
public EventCallback<RobotVDA5050ConfigDto> ModelChanged { get; set; }
private RobotVDA5050ConfigDto Local = new();
protected override void OnParametersSet()
{
Local = Model is not null ? Model with { } : new RobotVDA5050ConfigDto();
}
private async Task OnSubmit()
{
Model = Local;
await ModelChanged.InvokeAsync(Model);
}
}

View File

@ -1,7 +1,60 @@
<div class="view">
<h4>Infomations</h4>
<h4 class="info-title">Informations</h4>
<div class="info-grid">
<div class="info-col">
<dl class="info-list">
<dt class="info-term">X (m)</dt>
<dd class="info-desc">@Localization.X.ToString("F3")</dd>
<dt class="info-term">Y (m)</dt>
<dd class="info-desc">@Localization.Y.ToString("F3")</dd>
<dt class="info-term">Theta (rad)</dt>
<dd class="info-desc">@Localization.Theta.ToString("F4")</dd>
<dt class="info-term">Theta (deg)</dt>
<dd class="info-desc">@($"{Localization.Theta * 180.0 / Math.PI:F2}°")</dd>
<dt class="info-term">Ready</dt>
<dd class="info-desc">@((Localization.IsReady) ? "Yes" : "No")</dd>
</dl>
</div>
<div class="info-col">
<dl class="info-list">
<dt class="info-term">SlamState</dt>
<dd class="info-desc">@Localization.SlamState</dd>
<dt class="info-term">SlamDetail</dt>
<dd class="info-desc text-truncate" title="@Localization.SlamStateDetail">@Localization.SlamStateDetail</dd>
<dt class="info-term">Active Map</dt>
<dd class="info-desc text-truncate" title="@Localization.CurrentActiveMap">@Localization.CurrentActiveMap</dd>
<dt class="info-term">Reliability</dt>
<dd class="info-desc">@($"{Localization.Reliability:F1}%")</dd>
<dt class="info-term">MatchingScore</dt>
<dd class="info-desc">@($"{Localization.MatchingScore:F1}%")</dd>
</dl>
</div>
</div>
</div>
@code {
private class LocalizationDto
{
public bool IsReady { get; set; }
public double X { get; set; }
public double Y { get; set; }
public double Theta { get; set; }
public string SlamState { get; set; } = "Localization";
public string SlamStateDetail { get; set; } = "/r/n";
public string CurrentActiveMap { get; set; } = "Localization";
public double Reliability { get; set; }
public double MatchingScore { get; set; }
}
private LocalizationDto Localization = new();
}

View File

@ -5,9 +5,117 @@
display: flex;
flex-direction: column;
position: relative;
background-color: var(--mud-palette-surface);
border-radius: var(--mud-default-borderradius);
background-color: var(--mud-palette-surface, #ffffff);
border-radius: var(--mud-default-borderradius, 8px);
transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
box-shadow: var(--mud-elevation-10);
padding: 15px;
box-shadow: var(--mud-elevation-10, 0 4px 12px rgba(0,0,0,0.08));
padding: 12px;
box-sizing: border-box;
border: 1px solid rgba(0,0,0,0.04);
}
.view::before {
content: "";
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 6px;
border-top-left-radius: inherit;
border-bottom-left-radius: inherit;
background: linear-gradient(180deg, var(--mud-palette-primary, #1976d2), rgba(25,118,210,0.7));
}
.info-title {
margin: 0 0 10px 12px;
font-weight: 600;
font-size: 1rem;
color: var(--mud-palette-primary, #1976d2);
}
.info-grid {
display: flex;
gap: 14px;
align-items: flex-start;
flex: 1 1 auto;
overflow: hidden;
padding: 1.5em;
}
.info-col {
flex: 1 1 50%;
min-width: 0;
overflow: hidden;
}
.info-list {
margin: 0;
padding: 0;
}
.info-list dt,
.info-list dd {
display: flex;
align-items: center;
padding: 6px 0;
font-size: 0.95rem;
line-height: 1.2;
}
.info-term {
width: 42%;
text-align: right;
padding-right: 12px;
color: var(--mud-palette-text-secondary, rgba(0,0,0,0.6));
font-weight: 600;
box-sizing: border-box;
white-space: nowrap;
}
.info-desc {
width: 58%;
text-align: left;
font-weight: 700;
color: var(--mud-palette-text-primary, rgba(0,0,0,0.85));
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
.text-truncate {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.info-list dt + dd {
border-bottom: 1px dashed rgba(0,0,0,0.03);
}
.ready-yes {
color: var(--mud-palette-success, #2e7d32);
}
.ready-no {
color: var(--mud-palette-error, #d32f2f);
}
.percent {
color: var(--mud-palette-primary, #1976d2);
}
@media (max-width: 640px) {
.info-grid {
flex-direction: column;
gap: 8px;
}
.info-term {
width: 45%;
}
.info-desc {
width: 55%;
}
}

View File

@ -0,0 +1,208 @@
@page "/robot-config"
@rendermode InteractiveWebAssemblyNoPrerender
@attribute [Authorize]
@using RobotApp.Common.Shares.Dtos
@using RobotApp.Common.Shares.Enums
@inject HttpClient Http
<PageTitle>Robot Configuration</PageTitle>
<div class="d-flex w-100 h-100 p-2 overflow-hidden flex-column">
<div class="rcm-toolbar">
<div class="rcm-toolbar-left">
<label class="rcm-label" for="configType">Config Type</label>
<div class="rcm-select-wrapper">
<select id="configType" class="form-select rcm-select" @bind="SelectedType">
@foreach (var type in Enum.GetValues<RobotConfigType>())
{
<option value="@type">@type</option>
}
</select>
<span class="rcm-select-icon" aria-hidden="true">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path d="M7 10l5 5 5-5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</span>
</div>
</div>
<div class="rcm-toolbar-right">
<div class="rcm-action-group">
<button type="button" class="btn rcm-icon-btn" data-tooltip="Add config" aria-label="Add">
<i class="mdi mdi-plus" aria-hidden="true"></i>
</button>
<button type="button" class="btn rcm-icon-btn" data-tooltip="Update config" aria-label="Update">
<i class="mdi mdi-content-save" aria-hidden="true"></i>
</button>
<button type="button" class="btn rcm-icon-btn rcm-danger" data-tooltip="Delete config" aria-label="Delete">
<i class="mdi mdi-delete" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
<div class="content rcm-content flex-grow-1 d-flex gap-3 mt-3">
<div class="card p-2 config-list rcm-config-list">
<div class="card-body list-body p-0">
@switch (SelectedType)
{
case RobotConfigType.VDA5050:
@RenderList(VdaConfigs, () => SelectVda)
break;
case RobotConfigType.Safety:
@RenderList(SafetyConfigs, () => SelectSafety)
break;
case RobotConfigType.Simulation:
@RenderList(SimulationConfigs, () => SelectSimulation)
break;
case RobotConfigType.PLC:
@RenderList(PlcConfigs, () => SelectPlc)
break;
case RobotConfigType.Core:
@RenderList(CoreConfigs, () => SelectCore)
break;
default:
<div class="p-2">No configs.</div>
break;
}
</div>
</div>
<div class="card p-2 config-content rcm-config-content">
<div class="card-body">
@if (!HasSelection)
{
<div class="text-muted">Select a config from the list or click Add to create one.</div>
}
else
{
@switch (SelectedType)
{
case RobotConfigType.VDA5050:
<RobotApp.Client.Pages.Components.Config.RobotVDA5050Config @bind-Model="SelectedVda" />
break;
case RobotConfigType.Safety:
<RobotApp.Client.Pages.Components.Config.RobotSafetyConfig @bind-Model="SelectedSafety" />
break;
case RobotConfigType.Simulation:
<RobotApp.Client.Pages.Components.Config.RobotSimulationConfig @bind-Model="SelectedSimulation" />
break;
case RobotConfigType.PLC:
<RobotApp.Client.Pages.Components.Config.RobotPLCConfig @bind-Model="SelectedPlc" />
break;
case RobotConfigType.Core:
<RobotApp.Client.Pages.Components.Config.RobotConfig @bind-Model="SelectedCore" />
break;
}
}
</div>
</div>
</div>
</div>
@code {
private List<RobotVDA5050ConfigDto> VdaConfigs { get; } = [];
private List<RobotSafetyConfigDto> SafetyConfigs { get; } = [];
private List<RobotSimulationConfigDto> SimulationConfigs { get; } = [];
private List<RobotPlcConfigDto> PlcConfigs { get; } = [];
private List<RobotConfigDto> CoreConfigs { get; } = [];
private RobotVDA5050ConfigDto? SelectedVda { get; set; }
private RobotSafetyConfigDto? SelectedSafety { get; set; }
private RobotSimulationConfigDto? SelectedSimulation { get; set; }
private RobotPlcConfigDto? SelectedPlc { get; set; }
private RobotConfigDto? SelectedCore { get; set; }
private RobotConfigType SelectedType { get; set; } = RobotConfigType.VDA5050;
private int SelectedIndex { get; set; } = -1;
private bool HasSelection => SelectedIndex >= 0;
RenderFragment RenderList<T>(List<T> list, Func<Action<int>> selectFactory) where T : class
{
return builder =>
{
if (list is null || !list.Any())
{
builder.OpenElement(0, "div");
builder.AddAttribute(1, "class", "p-2 text-muted");
builder.AddContent(2, "No configs found.");
builder.CloseElement();
return;
}
builder.OpenElement(3, "ul");
builder.AddAttribute(4, "class", "list-group list-group-flush");
for (int i = 0; i < list.Count; i++)
{
var item = list[i];
var idx = i;
builder.OpenElement(10 + i * 5, "li");
builder.AddAttribute(11 + i * 5, "class", $"list-group-item {(SelectedIndex==idx ? "active" : "")}");
builder.AddAttribute(12 + i * 5, "style", "cursor:pointer;padding:0.75rem 1rem;");
builder.AddAttribute(13 + i * 5, "onclick", EventCallback.Factory.Create(this, () => selectFactory()(idx)));
// Only show ConfigName
var nameProp = item.GetType().GetProperty("ConfigName");
var name = nameProp?.GetValue(item)?.ToString() ?? "Unnamed";
builder.AddContent(14 + i * 5, name);
// Show active badge at end if IsActive == true
var isActiveProp = item.GetType().GetProperty("IsActive");
var isActive = isActiveProp?.GetValue(item) is bool b && b;
if (isActive)
{
builder.OpenElement(15 + i * 5, "span");
builder.AddAttribute(16 + i * 5, "class", "badge bg-success ms-2 float-end");
builder.AddContent(17 + i * 5, "Active");
builder.CloseElement();
}
builder.CloseElement();
}
builder.CloseElement();
};
}
private Action<int> SelectVda => idx =>
{
SelectedIndex = idx;
SelectedVda = idx >= 0 && idx < VdaConfigs.Count ? VdaConfigs[idx] with { } : null;
StateHasChanged();
};
private Action<int> SelectSafety => idx =>
{
SelectedIndex = idx;
SelectedSafety = idx >= 0 && idx < SafetyConfigs.Count ? SafetyConfigs[idx] with { } : null;
StateHasChanged();
};
private Action<int> SelectSimulation => idx =>
{
SelectedIndex = idx;
SelectedSimulation = idx >= 0 && idx < SimulationConfigs.Count ? SimulationConfigs[idx] with { } : null;
StateHasChanged();
};
private Action<int> SelectPlc => idx =>
{
SelectedIndex = idx;
SelectedPlc = idx >= 0 && idx < PlcConfigs.Count ? PlcConfigs[idx] with { } : null;
StateHasChanged();
};
private Action<int> SelectCore => idx =>
{
SelectedIndex = idx;
SelectedCore = idx >= 0 && idx < CoreConfigs.Count ? CoreConfigs[idx] with { } : null;
StateHasChanged();
};
}

View File

@ -0,0 +1,375 @@
/* Toolbar layout */
.rcm-toolbar {
display: flex;
gap: 1rem;
align-items: center;
justify-content: space-between;
background: linear-gradient(180deg, rgba(30,30,30,0.95), rgba(20,20,20,0.95));
color: #e6e6e6;
padding: 0.75rem 1rem;
border-radius: 0.375rem;
box-shadow: 0 2px 6px rgba(0,0,0,0.6);
border: 1px solid rgba(255,255,255,0.04);
}
/* Content layout: take remaining height */
.rcm-content {
height: calc(100vh - 90px); /* approximate toolbar + padding height; adjust if needed */
min-height: 0; /* allow flex children to shrink properly */
}
/* Config list uses flexible basis and can shrink instead of forcing overflow */
.rcm-config-list {
/* replace fixed width with flex basis + shrink */
flex: 0 1 35%; /* flex-grow:0, flex-shrink:1, flex-basis:35% */
min-width: 240px; /* allow smaller than before but keep readable */
display: flex;
flex-direction: column;
min-height: 0;
}
/* Config content uses remaining space and can shrink/grow */
.rcm-config-content {
flex: 1 1 65%; /* flex-grow:1 to take remaining, flex-shrink:1 */
min-width: 220px;
display: flex;
flex-direction: column;
}
/* Make card body scrollable if content overflows */
.config-list .list-body,
.rcm-config-content .card-body,
.config-list .list-body ul {
overflow: auto;
}
/* Ensure list-body and editor body expand to fill container */
.config-list .list-body {
height: 100%;
}
.rcm-config-content .card-body {
height: 100%;
}
/* Make config list body fill available height and scroll when content overflows */
.rcm-config-list .card-body.list-body {
flex: 1 1 auto; /* expand to fill container */
min-height: 0; /* allow flex children to shrink in many browsers */
overflow: auto; /* enable scrolling when content overflows */
padding: 0.25rem 0.5rem; /* keep slight padding for list */
}
/* Ensure the list itself doesn't add extra margins that affect scrolling */
.rcm-config-list .list-group {
margin: 0;
padding: 0;
}
/* Each list item keeps its padding but stays in flow */
.rcm-config-list .list-group-item {
padding: 0.75rem 1rem;
}
/* List items spacing */
.list-group-item {
cursor: pointer;
}
/* Left side controls (robot id + select) */
.rcm-toolbar-left {
display: flex;
gap: 0.5rem;
align-items: center;
flex-wrap: wrap;
}
/* Right side action buttons */
.rcm-toolbar-right {
display: flex;
align-items: center;
}
/* Group buttons with small spacing */
.rcm-action-group {
display: flex;
gap: 0.5rem;
}
/* Inputs sizing */
.rcm-input {
width: 160px;
background: #1a1a1a;
border: 1px solid rgba(255,255,255,0.06);
color: #e6e6e6;
}
.rcm-select-wrapper {
position: relative;
display: inline-flex;
align-items: center;
}
/* Make select background brighter and match button gradients (blue tone similar to Update) */
.rcm-select {
width: 180px;
margin-left: 6px; /* bring select closer to left controls */
background: linear-gradient(180deg, #4aa0db, #2b87c9);
border: 1px solid rgba(0,0,0,0.12);
color: #fff;
appearance: none;
padding-right: 28px; /* space for icon */
box-shadow: 0 1px 0 rgba(255,255,255,0.04) inset;
}
/* Hover effect: slightly brighter */
.rcm-select-wrapper:hover .rcm-select {
background: linear-gradient(180deg, #5bb6ee, #399ad6);
border-color: rgba(58,123,184,0.22);
transform: translateY(-1px);
}
/* Select icon color to contrast with brighter select */
.rcm-select-icon {
position: absolute;
right: 6px;
pointer-events: none;
color: rgba(255,255,255,0.9);
display: inline-flex;
align-items: center;
}
/* Toolbar labels */
.rcm-label {
font-size: 0.9rem;
margin-right: 6px;
color: #cfcfcf;
}
/* Minor button styling to match toolbar */
.rcm-btn {
min-width: 84px;
}
/* Icon button styles */
.rcm-icon-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 38px;
height: 34px;
padding: 0.25rem;
border-radius: 6px;
background: transparent;
color: #e6e6e6;
border: 1px solid rgba(255,255,255,0.04);
}
.rcm-icon-btn svg {
display: block;
}
/* Icon button hover and focus states */
.rcm-icon-btn:hover, .rcm-icon-btn:focus {
background: rgba(255,255,255,0.03);
color: #fff;
border-color: rgba(255,255,255,0.08);
}
.rcm-danger {
color: #ffb3b3;
border-color: rgba(255,100,100,0.18);
}
/* Adjust bootstrap button colors for dark toolbar when using outline variants */
.rcm-toolbar .btn-outline-primary {
color: #cfe2ff;
border-color: rgba(95,160,255,0.2);
}
.rcm-toolbar .btn-outline-success {
color: #d4f5d4;
border-color: rgba(60,200,120,0.18);
}
.rcm-toolbar .btn-outline-danger {
color: #ffcfcf;
border-color: rgba(255,100,100,0.18);
}
/* Specific styles for action buttons (Add, Update, Delete) */
.rcm-action-group button[aria-label="Add"] {
background: linear-gradient(180deg, #4bb24b, #2f9a2f); /* bright green */
color: #fff;
border: 1px solid rgba(0,0,0,0.08);
box-shadow: 0 1px 0 rgba(255,255,255,0.06) inset;
}
.rcm-action-group button[aria-label="Add"]:hover {
background: linear-gradient(180deg, #66d166, #3fb83f);
transform: translateY(-1px);
}
.rcm-action-group button[aria-label="Add"]:focus {
outline: none;
box-shadow: 0 0 0 3px rgba(56,160,56,0.18);
}
.rcm-action-group button[aria-label="Update"] {
background: linear-gradient(180deg, #4aa0db, #2b87c9); /* bright blue */
color: #fff;
border: 1px solid rgba(0,0,0,0.08);
box-shadow: 0 1px 0 rgba(255,255,255,0.08) inset;
}
.rcm-action-group button[aria-label="Update"]:hover {
background: linear-gradient(180deg, #5bb6ee, #399ad6);
transform: translateY(-1px);
}
.rcm-action-group button[aria-label="Update"]:focus {
outline: none;
box-shadow: 0 0 0 3px rgba(58,123,184,0.18);
}
.rcm-action-group button[aria-label="Delete"] {
background: linear-gradient(180deg, #ff6b6b, #e04848); /* bright red */
color: #fff;
border: 1px solid rgba(0,0,0,0.08);
box-shadow: 0 1px 0 rgba(255,255,255,0.06) inset;
}
.rcm-action-group button[aria-label="Delete"]:hover {
background: linear-gradient(180deg, #ff8282, #ec5b5b);
transform: translateY(-1px);
}
.rcm-action-group button[aria-label="Delete"]:focus {
outline: none;
box-shadow: 0 0 0 3px rgba(224,72,72,0.18);
}
/* Ensure icons inside the action buttons remain visible */
.rcm-action-group button[aria-label="Add"] .mdi,
.rcm-action-group button[aria-label="Update"] .mdi,
.rcm-action-group button[aria-label="Delete"] .mdi {
color: #fff;
}
/* Ensure dropdown options use dark theme matching select */
.rcm-select option {
background-color: #141414 !important;
color: #e6e6e6 !important;
}
/* Hover/active state inside dropdown */
.rcm-select option:hover,
.rcm-select option:checked {
background-color: #2b87c9 !important; /* match update button blue */
color: #fff !important;
}
/* Optgroup styling (if used) */
.rcm-select optgroup {
color: #e6e6e6;
background: #141414;
}
/* Tooltip for action buttons using data-tooltip attribute */
.rcm-action-group button[data-tooltip] {
position: relative;
}
.rcm-action-group button[data-tooltip]::after {
content: attr(data-tooltip);
position: absolute;
left: 50%;
transform: translateX(-50%) translateY(8px);
bottom: -9999px; /* hidden by default below flow */
background: rgba(20,20,20,0.98);
color: #fff;
padding: 6px 10px;
border-radius: 6px;
font-size: 0.85rem;
white-space: nowrap;
box-shadow: 0 2px 6px rgba(0,0,0,0.5);
opacity: 0;
pointer-events: none;
transition: opacity 0.12s ease, transform 0.12s ease;
z-index: 50;
}
/* Arrow below tooltip */
.rcm-action-group button[data-tooltip]::before {
content: "";
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: -9999px;
width: 8px;
height: 8px;
background: rgba(20,20,20,0.98);
transform-origin: center;
rotate: 45deg;
z-index: 49;
}
/* Show tooltip on hover */
.rcm-action-group button[data-tooltip]:hover::after,
.rcm-action-group button[data-tooltip]:hover::before {
bottom: -45px; /* reduced gap: place tooltip closer to the button */
opacity: 1;
transform: translateX(-50%) translateY(0);
}
/* Adjust for danger button (slightly different color) */
.rcm-action-group button[data-tooltip].rcm-danger::after {
background: rgba(224,72,72,0.95);
}
/* Slightly lift the button on hover to match tooltip */
.rcm-action-group button[data-tooltip]:hover {
transform: translateY(-2px);
}
/* Tooltip backgrounds matching their buttons */
.rcm-action-group button[aria-label="Add"][data-tooltip]::after {
background: linear-gradient(180deg, #4bb24b, #2f9a2f);
}
.rcm-action-group button[aria-label="Add"][data-tooltip]::before {
background: #2f9a2f;
}
.rcm-action-group button[aria-label="Update"][data-tooltip]::after {
background: linear-gradient(180deg, #4aa0db, #2b87c9);
}
.rcm-action-group button[aria-label="Update"][data-tooltip]::before {
background: #2b87c9;
}
/* Delete already had a red variant, update the arrow to match precisely */
.rcm-action-group button[aria-label="Delete"][data-tooltip]::after,
.rcm-action-group button[data-tooltip].rcm-danger::after {
background: linear-gradient(180deg, #ff6b6b, #e04848);
}
.rcm-action-group button[aria-label="Delete"][data-tooltip]::before,
.rcm-action-group button[data-tooltip].rcm-danger::before {
background: #e04848;
}
/* Ensure tooltip text stays readable on gradients */
.rcm-action-group button[data-tooltip]::after {
color: #fff;
}
/* Active badge in config list: make slightly larger and more visible */
.rcm-config-list .list-group-item .badge {
font-size: 1.05rem; /* slightly larger */
padding: 0.45rem 0.8rem;
border-radius: 0.5rem;
line-height: 1;
margin-left: 0.6rem;
opacity: 0.98;
display: inline-block;
vertical-align: middle;
}

View File

@ -11,3 +11,5 @@
@using RobotApp.Client
@using Microsoft.AspNetCore.Authorization
@using MudBlazor
@using RobotApp.Common.Shares.Dtos
@using RobotApp.Common.Shares.Enums

View File

@ -14,7 +14,29 @@ public record RobotConfigDto
public double Height { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public bool IsActive { get; set; } = true;
public bool IsActive { get; set; }
public string ConfigName { get; set; }
public string Description { get; set; }
}
public record UpdateRobotConfigDto
{
public NavigationType NavigationType { get; set; }
public double RadiusWheel { get; set; }
public double Width { get; set; }
public double Length { get; set; }
public double Height { get; set; }
public string ConfigName { get; set; }
public string Description { get; set; }
}
public record CreateRobotConfigDto
{
public NavigationType NavigationType { get; set; }
public double RadiusWheel { get; set; }
public double Width { get; set; }
public double Length { get; set; }
public double Height { get; set; }
public string ConfigName { get; set; }
public string Description { get; set; }
}

View File

@ -7,10 +7,28 @@ public record RobotPlcConfigDto
public Guid Id { get; set; }
public string PLCAddress { get; set; }
public int PLCPort { get; set; }
public byte PLCUnitId { get; set; }
public int PLCUnitId { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public bool IsActive { get; set; } = true;
public bool IsActive { get; set; }
public string ConfigName { get; set; }
public string Description { get; set; }
}
public record UpdateRobotPlcConfigDto
{
public string ConfigName { get; set; }
public string Description { get; set; }
public string PLCAddress { get; set; }
public int PLCPort { get; set; }
public int PLCUnitId { get; set; }
}
public record CreateRobotPlcConfigDto
{
public string ConfigName { get; set; }
public string Description { get; set; }
public string PLCAddress { get; set; }
public int PLCPort { get; set; }
public int PLCUnitId { get; set; }
}

View File

@ -1,20 +0,0 @@
namespace RobotApp.Common.Shares.Dtos;
#nullable disable
public record RobotSafetyConfig
{
public Guid Id { get; set; }
public double SafetySpeedVerySlow { get; set; }
public double SafetySpeedSlow { get; set; }
public double SafetySpeedNormal { get; set; }
public double SafetySpeedMedium { get; set; }
public double SafetySpeedOptimal { get; set; }
public double SafetySpeedFast { get; set; }
public double SafetySpeedVeryFast { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public bool IsActive { get; set; } = true;
public string ConfigName { get; set; }
public string Description { get; set; }
}

View File

@ -0,0 +1,46 @@
namespace RobotApp.Common.Shares.Dtos;
#nullable disable
public record RobotSafetyConfigDto
{
public Guid Id { get; set; }
public double SafetySpeedVerySlow { get; set; }
public double SafetySpeedSlow { get; set; }
public double SafetySpeedNormal { get; set; }
public double SafetySpeedMedium { get; set; }
public double SafetySpeedOptimal { get; set; }
public double SafetySpeedFast { get; set; }
public double SafetySpeedVeryFast { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public bool IsActive { get; set; }
public string ConfigName { get; set; }
public string Description { get; set; }
}
public record UpdateRobotSafetyConfigDto
{
public double SafetySpeedVerySlow { get; set; }
public double SafetySpeedSlow { get; set; }
public double SafetySpeedNormal { get; set; }
public double SafetySpeedMedium { get; set; }
public double SafetySpeedOptimal { get; set; }
public double SafetySpeedFast { get; set; }
public double SafetySpeedVeryFast { get; set; }
public string ConfigName { get; set; }
public string Description { get; set; }
}
public record CreateRobotSafetyConfigDto
{
public double SafetySpeedVerySlow { get; set; }
public double SafetySpeedSlow { get; set; }
public double SafetySpeedNormal { get; set; }
public double SafetySpeedMedium { get; set; }
public double SafetySpeedOptimal { get; set; }
public double SafetySpeedFast { get; set; }
public double SafetySpeedVeryFast { get; set; }
public string ConfigName { get; set; }
public string Description { get; set; }
}

View File

@ -12,7 +12,29 @@ public record RobotSimulationConfigDto
public double SimulationDeceleration { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public bool IsActive { get; set; } = true;
public bool IsActive { get; set; }
public string ConfigName { get; set; }
public string Description { get; set; }
}
public record UpdateRobotSimulationConfigDto
{
public bool EnableSimulation { get; set; }
public double SimulationMaxVelocity { get; set; }
public double SimulationMaxAngularVelocity { get; set; }
public double SimulationAcceleration { get; set; }
public double SimulationDeceleration { get; set; }
public string ConfigName { get; set; }
public string Description { get; set; }
}
public record CreateRobotSimulationConfigDto
{
public bool EnableSimulation { get; set; }
public double SimulationMaxVelocity { get; set; }
public double SimulationMaxAngularVelocity { get; set; }
public double SimulationAcceleration { get; set; }
public double SimulationDeceleration { get; set; }
public string ConfigName { get; set; }
public string Description { get; set; }
}

View File

@ -7,7 +7,7 @@ public record RobotVDA5050ConfigDto
public Guid Id { get; set; }
public string SerialNumber { get; set; }
public string VDA5050HostServer { get; set; }
public int VDA5050Port { get; set; } = 1883;
public int VDA5050Port { get; set; }
public string VDA5050UserName { get; set; }
public string VDA5050Password { get; set; }
public string VDA5050Manufacturer { get; set; }
@ -17,7 +17,39 @@ public record RobotVDA5050ConfigDto
public bool VDA5050EnableTls { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public bool IsActive { get; set; } = true;
public bool IsActive { get; set; }
public string ConfigName { get; set; }
public string Description { get; set; }
}
public record UpdateRobotVDA5050ConfigDto
{
public string SerialNumber { get; set; }
public string VDA5050HostServer { get; set; }
public int VDA5050Port { get; set; }
public string VDA5050UserName { get; set; }
public string VDA5050Password { get; set; }
public string VDA5050Manufacturer { get; set; }
public string VDA5050Version { get; set; }
public int VDA5050PublishRepeat { get; set; }
public bool VDA5050EnablePassword { get; set; }
public bool VDA5050EnableTls { get; set; }
public string ConfigName { get; set; }
public string Description { get; set; }
}
public record CreateRobotVDA5050ConfigDto
{
public string SerialNumber { get; set; }
public string VDA5050HostServer { get; set; }
public int VDA5050Port { get; set; }
public string VDA5050UserName { get; set; }
public string VDA5050Password { get; set; }
public string VDA5050Manufacturer { get; set; }
public string VDA5050Version { get; set; }
public int VDA5050PublishRepeat { get; set; }
public bool VDA5050EnablePassword { get; set; }
public bool VDA5050EnableTls { get; set; }
public string ConfigName { get; set; }
public string Description { get; set; }
}

View File

@ -0,0 +1,10 @@
namespace RobotApp.Common.Shares.Enums;
public enum RobotConfigType
{
VDA5050,
Core,
Safety ,
Simulation,
PLC,
}

View File

@ -1,16 +1,956 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using RobotApp.Common.Shares;
using RobotApp.Common.Shares.Dtos;
using RobotApp.Data;
using RobotApp.Services.Robot;
namespace RobotApp.Controllers;
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class RobotConfigsController(Services.Logger<ImagesController> Logger) : ControllerBase
public class RobotConfigsController(Services.Logger<RobotConfigsController> Logger, ApplicationDbContext AppDb, RobotConfiguration RobotConfiguration) : ControllerBase
{
[HttpGet]
[Route("plc")]
public void GetPLCConfig()
public async Task<MessageResult<RobotPlcConfigDto[]>> GetAllPLCConfigs()
{
try
{
var configs = await AppDb.RobotPlcConfigs
.OrderByDescending(c => c.UpdatedAt)
.ToListAsync();
if (configs is null || configs.Count == 0) return new(false, "PLC configuration not found.");
return new(true)
{
Data = [.. configs.Select(config => new RobotPlcConfigDto
{
Id = config.Id,
ConfigName = config.ConfigName,
Description = config.Description,
PLCAddress = config.PLCAddress,
PLCPort = config.PLCPort,
PLCUnitId = config.PLCUnitId,
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive,
})]
};
}
catch (Exception ex)
{
Logger.Error($"Error in Get PLC Configs: {ex.Message}");
return new(false, "An error occurred while retrieving the PLC configuration.");
}
}
[HttpPut]
[Route("plc/{id:guid}")]
public async Task<MessageResult<RobotPlcConfigDto>> UpdatePLCConfig(Guid id, [FromBody] UpdateRobotPlcConfigDto updateDto)
{
try
{
var config = await AppDb.RobotPlcConfigs.FindAsync(id);
if (config is null) return new(false, "PLC configuration not found.");
if (string.IsNullOrEmpty(updateDto.ConfigName)) return new(false, "ConfigName cannot be null or empty.");
if (await AppDb.RobotPlcConfigs.AnyAsync(cf => cf.Id != id && cf.ConfigName == updateDto.ConfigName))
return new(false, "A PLC configuration with the same name already exists.");
config.ConfigName = updateDto.ConfigName ?? config.ConfigName;
config.Description = updateDto.Description ?? config.Description;
config.PLCAddress = updateDto.PLCAddress ?? config.PLCAddress;
config.PLCPort = updateDto.PLCPort;
config.PLCUnitId = (byte)updateDto.PLCUnitId;
config.UpdatedAt = DateTime.Now;
await AppDb.SaveChangesAsync();
if (config.IsActive) await RobotConfiguration.LoadRobotPlcConfigAsync();
return new(true, "PLC configuration updated successfully.")
{
Data = new RobotPlcConfigDto
{
Id = config.Id,
ConfigName = config.ConfigName,
Description = config.Description,
PLCAddress = config.PLCAddress,
PLCPort = config.PLCPort,
PLCUnitId = config.PLCUnitId,
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive,
}
};
}
catch (Exception ex)
{
Logger.Error($"Error in Update PLC Config: {ex.Message}");
return new(false, "An error occurred while updating the PLC configuration.");
}
}
[HttpPost]
[Route("plc")]
public async Task<MessageResult<RobotPlcConfigDto>> CreatePLCConfig([FromBody] CreateRobotPlcConfigDto createDto)
{
try
{
if (string.IsNullOrEmpty(createDto.ConfigName)) return new(false, "ConfigName cannot be null or empty.");
if (await AppDb.RobotPlcConfigs.AnyAsync(cf => cf.ConfigName == createDto.ConfigName))
return new(false, "A PLC configuration with the same name already exists.");
var config = new RobotPlcConfig
{
ConfigName = createDto.ConfigName,
Description = createDto.Description,
PLCAddress = createDto.PLCAddress,
PLCPort = createDto.PLCPort,
PLCUnitId = (byte)createDto.PLCUnitId,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now,
IsActive = false
};
AppDb.RobotPlcConfigs.Add(config);
await AppDb.SaveChangesAsync();
return new(true, "PLC configuration created successfully.")
{
Data = new RobotPlcConfigDto
{
Id = config.Id,
ConfigName = config.ConfigName,
Description = config.Description,
PLCAddress = config.PLCAddress,
PLCPort = config.PLCPort,
PLCUnitId = config.PLCUnitId,
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive,
}
};
}
catch (Exception ex)
{
Logger.Error($"Error in Create PLC Config: {ex.Message}");
return new(false, "An error occurred while creating the PLC configuration.");
}
}
[HttpDelete]
[Route("plc/{id:guid}")]
public async Task<MessageResult> DeletePLCConfig(Guid id)
{
try
{
var config = await AppDb.RobotPlcConfigs.FindAsync(id);
if (config is null) return new(false, "PLC configuration not found.");
if (config.IsActive) return new(false, "Cannot delete an active PLC configuration.");
AppDb.RobotPlcConfigs.Remove(config);
await AppDb.SaveChangesAsync();
return new(true, "Configuration deleted successfully.");
}
catch (Exception ex)
{
Logger.Error($"Error in Delete PLC Config: {ex.Message}");
return new(false, "An error occurred while deleting the PLC configuration.");
}
}
[HttpPut]
[Route("plc/active/{id:guid}")]
public async Task<MessageResult> ActivePLCConfig(Guid id)
{
try
{
var config = await AppDb.RobotPlcConfigs.FindAsync(id);
if (config is null) return new(false, "PLC configuration not found.");
await AppDb.RobotPlcConfigs.ExecuteUpdateAsync(cf => cf.SetProperty(c => c.IsActive, false));
config.IsActive = true;
config.UpdatedAt = DateTime.Now;
await AppDb.SaveChangesAsync();
await RobotConfiguration.LoadRobotPlcConfigAsync();
return new(true, $"PLC configuration {config.ConfigName} activated successfully.");
}
catch (Exception ex)
{
Logger.Error($"Error in Update Active PLC Config: {ex.Message}");
return new(false, "An error occurred while updating the active PLC configuration.");
}
}
[HttpGet]
[Route("vda5050")]
public async Task<MessageResult<RobotVDA5050ConfigDto[]>> GetAllVDA5050Configs()
{
try
{
var configs = await AppDb.RobotVDA5050Configs
.OrderByDescending(c => c.UpdatedAt)
.ToListAsync();
if (configs is null || configs.Count == 0) return new(false, "VDA5050 configuration not found.");
var configDtos = configs.Select(config => new RobotVDA5050ConfigDto
{
Id = config.Id,
SerialNumber = config.SerialNumber,
VDA5050HostServer = config.VDA5050HostServer,
VDA5050Port = config.VDA5050Port,
VDA5050UserName = config.VDA5050UserName,
VDA5050Password = config.VDA5050Password,
VDA5050Manufacturer = config.VDA5050Manufacturer,
VDA5050Version = config.VDA5050Version,
VDA5050PublishRepeat = config.VDA5050PublishRepeat,
VDA5050EnablePassword = config.VDA5050EnablePassword,
VDA5050EnableTls = config.VDA5050EnableTls,
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive,
ConfigName = config.ConfigName,
Description = config.Description
}).ToArray();
return new(true) { Data = configDtos };
}
catch (Exception ex)
{
Logger.Error($"Error in Get All VDA5050 Configs: {ex.Message}");
return new(false, "An error occurred while retrieving VDA5050 configurations.");
}
}
[HttpPut]
[Route("vda5050/{id:guid}")]
public async Task<MessageResult<RobotVDA5050ConfigDto>> UpdateVDA5050Config(Guid id, [FromBody] UpdateRobotVDA5050ConfigDto updateDto)
{
try
{
var config = await AppDb.RobotVDA5050Configs.FindAsync(id);
if (config is null) return new(false, "VDA5050 configuration not found.");
if (string.IsNullOrEmpty(updateDto.ConfigName)) return new(false, "ConfigName cannot be null or empty.");
if (await AppDb.RobotVDA5050Configs.AnyAsync(cf => cf.Id != id && cf.ConfigName == updateDto.ConfigName))
return new(false, "A VDA5050 configuration with the same name already exists.");
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.VDA5050Manufacturer = updateDto.VDA5050Manufacturer ?? config.VDA5050Manufacturer;
config.VDA5050Version = updateDto.VDA5050Version ?? config.VDA5050Version;
config.VDA5050PublishRepeat = updateDto.VDA5050PublishRepeat;
config.VDA5050EnablePassword = updateDto.VDA5050EnablePassword;
config.VDA5050EnableTls = updateDto.VDA5050EnableTls;
config.ConfigName = updateDto.ConfigName ?? config.ConfigName;
config.Description = updateDto.Description ?? config.Description;
config.UpdatedAt = DateTime.Now;
await AppDb.SaveChangesAsync();
if (config.IsActive) await RobotConfiguration.LoadVDA5050ConfigAsync();
return new(true, "VDA5050 configuration updated successfully.")
{
Data = new RobotVDA5050ConfigDto
{
Id = config.Id,
SerialNumber = config.SerialNumber,
VDA5050HostServer = config.VDA5050HostServer,
VDA5050Port = config.VDA5050Port,
VDA5050UserName = config.VDA5050UserName,
VDA5050Password = config.VDA5050Password,
VDA5050Manufacturer = config.VDA5050Manufacturer,
VDA5050Version = config.VDA5050Version,
VDA5050PublishRepeat = config.VDA5050PublishRepeat,
VDA5050EnablePassword = config.VDA5050EnablePassword,
VDA5050EnableTls = config.VDA5050EnableTls,
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive,
ConfigName = config.ConfigName,
Description = config.Description
}
};
}
catch (Exception ex)
{
Logger.Error($"Error in Update VDA5050 Config: {ex.Message}");
return new(false, "An error occurred while updating the VDA5050 configuration.");
}
}
[HttpPost]
[Route("vda5050")]
public async Task<MessageResult<RobotVDA5050ConfigDto>> CreateVDA5050Config([FromBody] CreateRobotVDA5050ConfigDto createDto)
{
try
{
if (string.IsNullOrEmpty(createDto.ConfigName)) return new(false, "ConfigName cannot be null or empty.");
if (await AppDb.RobotVDA5050Configs.AnyAsync(cf => cf.ConfigName == createDto.ConfigName))
return new(false, "A VDA5050 configuration with the same name already exists.");
var config = new RobotVDA5050Config
{
ConfigName = createDto.ConfigName,
Description = createDto.Description,
SerialNumber = createDto.SerialNumber,
VDA5050HostServer = createDto.VDA5050HostServer,
VDA5050Port = createDto.VDA5050Port,
VDA5050UserName = createDto.VDA5050UserName,
VDA5050Password = createDto.VDA5050Password,
VDA5050Manufacturer = createDto.VDA5050Manufacturer,
VDA5050Version = createDto.VDA5050Version,
VDA5050PublishRepeat = createDto.VDA5050PublishRepeat,
VDA5050EnablePassword = createDto.VDA5050EnablePassword,
VDA5050EnableTls = createDto.VDA5050EnableTls,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now,
IsActive = false
};
AppDb.RobotVDA5050Configs.Add(config);
await AppDb.SaveChangesAsync();
return new(true, "VDA5050 configuration created successfully.")
{
Data = new RobotVDA5050ConfigDto
{
Id = config.Id,
ConfigName = config.ConfigName,
Description = config.Description,
SerialNumber = config.SerialNumber,
VDA5050HostServer = config.VDA5050HostServer,
VDA5050Port = config.VDA5050Port,
VDA5050UserName = config.VDA5050UserName,
VDA5050Password = config.VDA5050Password,
VDA5050Manufacturer = config.VDA5050Manufacturer,
VDA5050Version = config.VDA5050Version,
VDA5050PublishRepeat = config.VDA5050PublishRepeat,
VDA5050EnablePassword = config.VDA5050EnablePassword,
VDA5050EnableTls = config.VDA5050EnableTls,
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive,
}
};
}
catch (Exception ex)
{
Logger.Error($"Error in Create VDA5050 Config: {ex.Message}");
return new(false, "An error occurred while creating the VDA5050 configuration.");
}
}
[HttpDelete]
[Route("vda5050/{id:guid}")]
public async Task<MessageResult> DeleteVDA5050Config(Guid id)
{
try
{
var config = await AppDb.RobotVDA5050Configs.FindAsync(id);
if (config is null) return new(false, "VDA5050 configuration not found.");
if (config.IsActive) return new(false, "Cannot delete an active VDA5050 configuration.");
AppDb.RobotVDA5050Configs.Remove(config);
await AppDb.SaveChangesAsync();
return new(true, "Configuration deleted successfully.");
}
catch (Exception ex)
{
Logger.Error($"Error in Delete VDA5050 Config: {ex.Message}");
return new(false, "An error occurred while deleting the VDA5050 configuration.");
}
}
[HttpPut]
[Route("vda5050/active/{id:guid}")]
public async Task<MessageResult> ActiveVDA5050Config(Guid id)
{
try
{
var config = await AppDb.RobotVDA5050Configs.FindAsync(id);
if (config is null) return new(false, "VDA5050 configuration not found.");
await AppDb.RobotVDA5050Configs.ExecuteUpdateAsync(cf => cf.SetProperty(c => c.IsActive, false));
config.IsActive = true;
config.UpdatedAt = DateTime.Now;
await AppDb.SaveChangesAsync();
await RobotConfiguration.LoadVDA5050ConfigAsync();
return new(true, $"VDA5050 configuration {config.ConfigName} activated successfully.");
}
catch (Exception ex)
{
Logger.Error($"Error in Update Active VDA5050 Config: {ex.Message}");
return new(false, "An error occurred while updating the active VDA5050 configuration.");
}
}
[HttpGet]
[Route("robot")]
public async Task<MessageResult<RobotConfigDto[]>> GetAllRobotConfigs()
{
try
{
var configs = await AppDb.RobotConfigs
.OrderByDescending(c => c.UpdatedAt)
.ToListAsync();
if (configs is null || configs.Count == 0) return new(false, "Robot configuration not found.");
var configDtos = configs.Select(config => new RobotConfigDto
{
Id = config.Id,
NavigationType = config.NavigationType,
RadiusWheel = config.RadiusWheel,
Width = config.Width,
Length = config.Length,
Height = config.Height,
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive,
ConfigName = config.ConfigName,
Description = config.Description
}).ToArray();
return new(true) { Data = configDtos };
}
catch (Exception ex)
{
Logger.Error($"Error in Get All Robot Configs: {ex.Message}");
return new(false, "An error occurred while retrieving Robot configurations.");
}
}
[HttpPut]
[Route("robot/{id:guid}")]
public async Task<MessageResult<RobotConfigDto>> UpdateRobotConfig(Guid id, [FromBody] UpdateRobotConfigDto updateDto)
{
try
{
var config = await AppDb.RobotConfigs.FindAsync(id);
if (config is null) return new(false, "Robot configuration not found.");
if (string.IsNullOrEmpty(updateDto.ConfigName)) return new(false, "ConfigName cannot be null or empty.");
if (await AppDb.RobotConfigs.AnyAsync(cf => cf.Id != id && cf.ConfigName == updateDto.ConfigName))
return new(false, "A Robot configuration with the same name already exists.");
config.NavigationType = updateDto.NavigationType;
config.RadiusWheel = updateDto.RadiusWheel;
config.Width = updateDto.Width;
config.Length = updateDto.Length;
config.Height = updateDto.Height;
config.ConfigName = updateDto.ConfigName ?? config.ConfigName;
config.Description = updateDto.Description ?? config.Description;
config.UpdatedAt = DateTime.Now;
await AppDb.SaveChangesAsync();
if (config.IsActive) await RobotConfiguration.LoadRobotConfigAsync();
return new(true, "Robot configuration updated successfully.")
{
Data = new RobotConfigDto
{
Id = config.Id,
NavigationType = config.NavigationType,
RadiusWheel = config.RadiusWheel,
Width = config.Width,
Length = config.Length,
Height = config.Height,
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive,
ConfigName = config.ConfigName,
Description = config.Description
}
};
}
catch (Exception ex)
{
Logger.Error($"Error in Update Robot Config: {ex.Message}");
return new(false, "An error occurred while updating the Robot configuration.");
}
}
[HttpPost]
[Route("robot")]
public async Task<MessageResult<RobotConfigDto>> CreateRobotConfig([FromBody] CreateRobotConfigDto createDto)
{
try
{
if (string.IsNullOrEmpty(createDto.ConfigName)) return new(false, "ConfigName cannot be null or empty.");
if (await AppDb.RobotConfigs.AnyAsync(cf => cf.ConfigName == createDto.ConfigName))
return new(false, "A Robot configuration with the same name already exists.");
var config = new RobotConfig
{
ConfigName = createDto.ConfigName,
Description = createDto.Description,
NavigationType = createDto.NavigationType,
RadiusWheel = createDto.RadiusWheel,
Width = createDto.Width,
Length = createDto.Length,
Height = createDto.Height,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now,
IsActive = false
};
AppDb.RobotConfigs.Add(config);
await AppDb.SaveChangesAsync();
return new(true, "Robot configuration created successfully.")
{
Data = new RobotConfigDto
{
Id = config.Id,
ConfigName = config.ConfigName,
Description = config.Description,
NavigationType = config.NavigationType,
RadiusWheel = config.RadiusWheel,
Width = config.Width,
Length = config.Length,
Height = config.Height,
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive,
}
};
}
catch (Exception ex)
{
Logger.Error($"Error in Create Robot Config: {ex.Message}");
return new(false, "An error occurred while creating the Robot configuration.");
}
}
[HttpDelete]
[Route("robot/{id:guid}")]
public async Task<MessageResult> DeleteRobotConfig(Guid id)
{
try
{
var config = await AppDb.RobotConfigs.FindAsync(id);
if (config is null) return new(false, "Robot configuration not found.");
if (config.IsActive) return new(false, "Cannot delete an active Robot configuration.");
AppDb.RobotConfigs.Remove(config);
await AppDb.SaveChangesAsync();
return new(true, "Configuration deleted successfully.");
}
catch (Exception ex)
{
Logger.Error($"Error in Delete Robot Config: {ex.Message}");
return new(false, "An error occurred while deleting the Robot configuration.");
}
}
[HttpPut]
[Route("robot/active/{id:guid}")]
public async Task<MessageResult> ActiveRobotConfig(Guid id)
{
try
{
var config = await AppDb.RobotConfigs.FindAsync(id);
if (config is null) return new(false, "Robot configuration not found.");
await AppDb.RobotConfigs.ExecuteUpdateAsync(cf => cf.SetProperty(c => c.IsActive, false));
config.IsActive = true;
config.UpdatedAt = DateTime.Now;
await AppDb.SaveChangesAsync();
await RobotConfiguration.LoadRobotConfigAsync();
return new(true, $"Robot configuration {config.ConfigName} activated successfully.");
}
catch (Exception ex)
{
Logger.Error($"Error in Update Active Robot Config: {ex.Message}");
return new(false, "An error occurred while updating the active Robot configuration.");
}
}
[HttpGet]
[Route("simulation")]
public async Task<MessageResult<RobotSimulationConfigDto[]>> GetAllSimulationConfigs()
{
try
{
var configs = await AppDb.RobotSimulationConfigs
.OrderByDescending(c => c.UpdatedAt)
.ToListAsync();
if (configs is null || configs.Count == 0) return new(false, "Simulation configuration not found.");
var configDtos = configs.Select(config => new RobotSimulationConfigDto
{
Id = config.Id,
EnableSimulation = config.EnableSimulation,
SimulationMaxVelocity = config.SimulationMaxVelocity,
SimulationMaxAngularVelocity = config.SimulationMaxAngularVelocity,
SimulationAcceleration = config.SimulationAcceleration,
SimulationDeceleration = config.SimulationDeceleration,
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive,
ConfigName = config.ConfigName,
Description = config.Description
}).ToArray();
return new(true) { Data = configDtos };
}
catch (Exception ex)
{
Logger.Error($"Error in Get All Simulation Configs: {ex.Message}");
return new(false, "An error occurred while retrieving Simulation configurations.");
}
}
[HttpPut]
[Route("simulation/{id:guid}")]
public async Task<MessageResult<RobotSimulationConfigDto>> UpdateSimulationConfig(Guid id, [FromBody] UpdateRobotSimulationConfigDto updateDto)
{
try
{
var config = await AppDb.RobotSimulationConfigs.FindAsync(id);
if (config is null) return new(false, "Simulation configuration not found.");
if (string.IsNullOrEmpty(updateDto.ConfigName)) return new(false, "ConfigName cannot be null or empty.");
if (await AppDb.RobotSimulationConfigs.AnyAsync(cf => cf.Id != id && cf.ConfigName == updateDto.ConfigName))
return new(false, "A Simulation configuration with the same name already exists.");
config.EnableSimulation = updateDto.EnableSimulation;
config.SimulationMaxVelocity = updateDto.SimulationMaxVelocity;
config.SimulationMaxAngularVelocity = updateDto.SimulationMaxAngularVelocity;
config.SimulationAcceleration = updateDto.SimulationAcceleration;
config.SimulationDeceleration = updateDto.SimulationDeceleration;
config.ConfigName = updateDto.ConfigName ?? config.ConfigName;
config.Description = updateDto.Description ?? config.Description;
config.UpdatedAt = DateTime.Now;
await AppDb.SaveChangesAsync();
if (config.IsActive) await RobotConfiguration.LoadRobotSimulationConfigAsync();
return new(true, "Simulation configuration updated successfully.")
{
Data = new RobotSimulationConfigDto
{
Id = config.Id,
EnableSimulation = config.EnableSimulation,
SimulationMaxVelocity = config.SimulationMaxVelocity,
SimulationMaxAngularVelocity = config.SimulationMaxAngularVelocity,
SimulationAcceleration = config.SimulationAcceleration,
SimulationDeceleration = config.SimulationDeceleration,
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive,
ConfigName = config.ConfigName,
Description = config.Description
}
};
}
catch (Exception ex)
{
Logger.Error($"Error in Update Simulation Config: {ex.Message}");
return new(false, "An error occurred while updating the Simulation configuration.");
}
}
[HttpPost]
[Route("simulation")]
public async Task<MessageResult<RobotSimulationConfigDto>> CreateRobotSimulationConfig([FromBody] CreateRobotSimulationConfigDto createDto)
{
try
{
if (string.IsNullOrEmpty(createDto.ConfigName)) return new(false, "ConfigName cannot be null or empty.");
if (await AppDb.RobotSimulationConfigs.AnyAsync(cf => cf.ConfigName == createDto.ConfigName))
return new(false, "A Simulation configuration with the same name already exists.");
var config = new RobotSimulationConfig
{
ConfigName = createDto.ConfigName,
Description = createDto.Description,
EnableSimulation = createDto.EnableSimulation,
SimulationMaxVelocity = createDto.SimulationMaxVelocity,
SimulationMaxAngularVelocity = createDto.SimulationMaxAngularVelocity,
SimulationAcceleration = createDto.SimulationAcceleration,
SimulationDeceleration = createDto.SimulationDeceleration,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now,
IsActive = false
};
AppDb.RobotSimulationConfigs.Add(config);
await AppDb.SaveChangesAsync();
return new(true, "Simulation configuration created successfully.")
{
Data = new RobotSimulationConfigDto
{
Id = config.Id,
ConfigName = config.ConfigName,
Description = config.Description,
EnableSimulation = config.EnableSimulation,
SimulationMaxVelocity = config.SimulationMaxVelocity,
SimulationMaxAngularVelocity = config.SimulationMaxAngularVelocity,
SimulationAcceleration = config.SimulationAcceleration,
SimulationDeceleration = config.SimulationDeceleration,
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive,
}
};
}
catch (Exception ex)
{
Logger.Error($"Error in Create Simulation Config: {ex.Message}");
return new(false, "An error occurred while creating the Simulation configuration.");
}
}
[HttpDelete]
[Route("simulation/{id:guid}")]
public async Task<MessageResult> DeleteRobotSimulationConfig(Guid id)
{
try
{
var config = await AppDb.RobotSimulationConfigs.FindAsync(id);
if (config is null) return new(false, "Simulation configuration not found.");
if (config.IsActive) return new(false, "Cannot delete an active Simulation configuration.");
AppDb.RobotSimulationConfigs.Remove(config);
await AppDb.SaveChangesAsync();
return new(true, "Configuration deleted successfully.");
}
catch (Exception ex)
{
Logger.Error($"Error in Delete Simulation Config: {ex.Message}");
return new(false, "An error occurred while deleting the Simulation configuration.");
}
}
[HttpPut]
[Route("simulation/active/{id:guid}")]
public async Task<MessageResult> ActiveSimulationConfig(Guid id)
{
try
{
var config = await AppDb.RobotSimulationConfigs.FindAsync(id);
if (config is null) return new(false, "Simulation configuration not found.");
await AppDb.RobotSimulationConfigs.ExecuteUpdateAsync(cf => cf.SetProperty(c => c.IsActive, false));
config.IsActive = true;
config.UpdatedAt = DateTime.Now;
await AppDb.SaveChangesAsync();
await RobotConfiguration.LoadRobotSimulationConfigAsync();
return new(true, $"Simulation configuration {config.ConfigName} activated successfully.");
}
catch (Exception ex)
{
Logger.Error($"Error in Update Active Simulation Config: {ex.Message}");
return new(false, "An error occurred while updating the active Simulation configuration.");
}
}
[HttpGet]
[Route("safety")]
public async Task<MessageResult<RobotSafetyConfigDto[]>> GetAllSafetyConfigs()
{
try
{
var configs = await AppDb.RobotSafetyConfigs
.OrderByDescending(c => c.UpdatedAt)
.ToListAsync();
if (configs is null || configs.Count == 0) return new(false, "Safety configuration not found.");
var configDtos = configs.Select(config => new RobotSafetyConfigDto
{
Id = config.Id,
SafetySpeedVerySlow = config.SafetySpeedVerySlow,
SafetySpeedSlow = config.SafetySpeedSlow,
SafetySpeedNormal = config.SafetySpeedNormal,
SafetySpeedMedium = config.SafetySpeedMedium,
SafetySpeedOptimal = config.SafetySpeedOptimal,
SafetySpeedFast = config.SafetySpeedFast,
SafetySpeedVeryFast = config.SafetySpeedVeryFast,
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive,
ConfigName = config.ConfigName,
Description = config.Description
}).ToArray();
return new(true) { Data = configDtos };
}
catch (Exception ex)
{
Logger.Error($"Error in Get All Safety Configs: {ex.Message}");
return new(false, "An error occurred while retrieving Safety configurations.");
}
}
[HttpPut]
[Route("safety/{id:guid}")]
public async Task<MessageResult<RobotSafetyConfigDto>> UpdateSafetyConfig(Guid id, [FromBody] UpdateRobotSafetyConfigDto updateDto)
{
try
{
var config = await AppDb.RobotSafetyConfigs.FindAsync(id);
if (config is null) return new(false, "Safety configuration not found.");
if (string.IsNullOrEmpty(updateDto.ConfigName)) return new(false, "ConfigName cannot be null or empty.");
if (await AppDb.RobotSafetyConfigs.AnyAsync(cf => cf.Id != id && cf.ConfigName == updateDto.ConfigName))
return new(false, "A Safety configuration with the same name already exists.");
config.SafetySpeedVerySlow = updateDto.SafetySpeedVerySlow;
config.SafetySpeedSlow = updateDto.SafetySpeedSlow;
config.SafetySpeedNormal = updateDto.SafetySpeedNormal;
config.SafetySpeedMedium = updateDto.SafetySpeedMedium;
config.SafetySpeedOptimal = updateDto.SafetySpeedOptimal;
config.SafetySpeedFast = updateDto.SafetySpeedFast;
config.SafetySpeedVeryFast = updateDto.SafetySpeedVeryFast;
config.ConfigName = updateDto.ConfigName ?? config.ConfigName;
config.Description = updateDto.Description ?? config.Description;
config.UpdatedAt = DateTime.Now;
await AppDb.SaveChangesAsync();
if (config.IsActive) await RobotConfiguration.LoadRobotSafetyConfigAsync();
return new(true, "Safety configuration updated successfully.")
{
Data = new RobotSafetyConfigDto
{
Id = config.Id,
SafetySpeedVerySlow = config.SafetySpeedVerySlow,
SafetySpeedSlow = config.SafetySpeedSlow,
SafetySpeedNormal = config.SafetySpeedNormal,
SafetySpeedMedium = config.SafetySpeedMedium,
SafetySpeedOptimal = config.SafetySpeedOptimal,
SafetySpeedFast = config.SafetySpeedFast,
SafetySpeedVeryFast = config.SafetySpeedVeryFast,
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive,
ConfigName = config.ConfigName,
Description = config.Description
}
};
}
catch (Exception ex)
{
Logger.Error($"Error in Update Safety Config: {ex.Message}");
return new(false, "An error occurred while updating the Safety configuration.");
}
}
[HttpPost]
[Route("safety")]
public async Task<MessageResult<RobotSafetyConfigDto>> CreateRobotSafetyConfig([FromBody] CreateRobotSafetyConfigDto createDto)
{
try
{
if (string.IsNullOrEmpty(createDto.ConfigName)) return new(false, "ConfigName cannot be null or empty.");
if (await AppDb.RobotSafetyConfigs.AnyAsync(cf => cf.ConfigName == createDto.ConfigName))
return new(false, "A Safety configuration with the same name already exists.");
var config = new RobotSafetyConfig
{
ConfigName = createDto.ConfigName,
Description = createDto.Description,
SafetySpeedVerySlow = createDto.SafetySpeedVerySlow,
SafetySpeedSlow = createDto.SafetySpeedSlow,
SafetySpeedNormal = createDto.SafetySpeedNormal,
SafetySpeedMedium = createDto.SafetySpeedMedium,
SafetySpeedOptimal = createDto.SafetySpeedOptimal,
SafetySpeedFast = createDto.SafetySpeedFast,
SafetySpeedVeryFast = createDto.SafetySpeedVeryFast,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now,
IsActive = false
};
AppDb.RobotSafetyConfigs.Add(config);
await AppDb.SaveChangesAsync();
return new(true, "Safety configuration created successfully.")
{
Data = new RobotSafetyConfigDto
{
Id = config.Id,
ConfigName = config.ConfigName,
Description = config.Description,
SafetySpeedVerySlow = config.SafetySpeedVerySlow,
SafetySpeedSlow = config.SafetySpeedSlow,
SafetySpeedNormal = config.SafetySpeedNormal,
SafetySpeedMedium = config.SafetySpeedMedium,
SafetySpeedOptimal = config.SafetySpeedOptimal,
SafetySpeedFast = config.SafetySpeedFast,
SafetySpeedVeryFast = config.SafetySpeedVeryFast,
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
IsActive = config.IsActive,
}
};
}
catch (Exception ex)
{
Logger.Error($"Error in Create Safety Config: {ex.Message}");
return new(false, "An error occurred while creating the Safety configuration.");
}
}
[HttpDelete]
[Route("safety/{id:guid}")]
public async Task<MessageResult> DeleteRobotSafetyConfig(Guid id)
{
try
{
var config = await AppDb.RobotSafetyConfigs.FindAsync(id);
if (config is null) return new(false, "Safety configuration not found.");
if (config.IsActive) return new(false, "Cannot delete an active Safety configuration.");
AppDb.RobotSafetyConfigs.Remove(config);
await AppDb.SaveChangesAsync();
return new(true, "Configuration deleted successfully.");
}
catch (Exception ex)
{
Logger.Error($"Error in Delete Safety Config: {ex.Message}");
return new(false, "An error occurred while deleting the Safety configuration.");
}
}
[HttpPut]
[Route("safety/active/{id:guid}")]
public async Task<MessageResult> ActiveSafetyConfig(Guid id)
{
try
{
var config = await AppDb.RobotSafetyConfigs.FindAsync(id);
if (config is null) return new(false, "Safety configuration not found.");
await AppDb.RobotSafetyConfigs.ExecuteUpdateAsync(cf => cf.SetProperty(c => c.IsActive, false));
config.IsActive = true;
config.UpdatedAt = DateTime.Now;
await AppDb.SaveChangesAsync();
await RobotConfiguration.LoadRobotSafetyConfigAsync();
return new(true, $"Safety configuration {config.ConfigName} activated successfully.");
}
catch (Exception ex)
{
Logger.Error($"Error in Update Active Safety Config: {ex.Message}");
return new(false, "An error occurred while updating the active Safety configuration.");
}
}
}

View File

@ -1,20 +1,14 @@
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using RobotApp.VDA5050.Order;
using RobotApp.VDA5050.State;
using static MudBlazor.CategoryTypes;
namespace RobotApp.Data
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : IdentityDbContext<ApplicationUser, ApplicationRole, string>(options)
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public DbSet<RobotConfig> RobotConfigs { get; private set; }
public DbSet<RobotSimulationConfig> RobotSimulationConfigs { get; private set; }
public DbSet<RobotPlcConfig> RobotPlcConfigs { get; private set; }
public DbSet<RobotVDA5050Config> RobotVDA5050Configs { get; private set; }
public DbSet<RobotSafetyConfig> RobotSafetyConfigs { get; private set; }
}
}

View File

@ -163,5 +163,27 @@ public static class ApplicationDbExtensions
appDb.RobotVDA5050Configs.Add(defaultConfig);
await appDb.SaveChangesAsync();
}
if (!await appDb.RobotSafetyConfigs.AnyAsync())
{
var defaultConfig = new RobotSafetyConfig
{
ConfigName = "Default",
Description = "Default robot Safety configuration",
SafetySpeedVerySlow = 0.15,
SafetySpeedSlow = 0.3,
SafetySpeedNormal = 0.6,
SafetySpeedMedium = 0.9,
SafetySpeedOptimal = 1.2,
SafetySpeedFast = 1.5,
SafetySpeedVeryFast = 1.9,
IsActive = true,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now,
};
appDb.RobotSafetyConfigs.Add(defaultConfig);
await appDb.SaveChangesAsync();
}
}
}

View File

@ -0,0 +1,565 @@
// <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("20251029064620_InitConfigSafetyDb")]
partial class InitConfigSafetyDb
{
/// <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<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>("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,45 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace RobotApp.Data.Migrations
{
/// <inheritdoc />
public partial class InitConfigSafetyDb : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "RobotSafetyConfig",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
SafetySpeedVerySlow = table.Column<double>(type: "float", nullable: false),
SafetySpeedSlow = table.Column<double>(type: "float", nullable: false),
SafetySpeedNormal = table.Column<double>(type: "float", nullable: false),
SafetySpeedMedium = table.Column<double>(type: "float", nullable: false),
SafetySpeedOptimal = table.Column<double>(type: "float", nullable: false),
SafetySpeedFast = table.Column<double>(type: "float", nullable: false),
SafetySpeedVeryFast = table.Column<double>(type: "float", nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
IsActive = table.Column<bool>(type: "bit", nullable: false),
ConfigName = table.Column<string>(type: "nvarchar(64)", maxLength: 100, nullable: true),
Description = table.Column<string>(type: "ntext", maxLength: 500, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_RobotSafetyConfig", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "RobotSafetyConfig");
}
}
}

View File

@ -0,0 +1,565 @@
// <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("20251029064649_InitConfigSafety1Db")]
partial class InitConfigSafety1Db
{
/// <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<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>("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 InitConfigSafety1Db : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

View File

@ -310,6 +310,68 @@ namespace RobotApp.Migrations
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")

View File

@ -37,7 +37,7 @@ public class RobotConfig
public DateTime UpdatedAt { get; set; }
[Column("IsActive", TypeName = "bit")]
public bool IsActive { get; set; } = true;
public bool IsActive { get; set; }
[Column("ConfigName", TypeName = "nvarchar(64)")]
[MaxLength(100)]

View File

@ -31,7 +31,7 @@ public class RobotPlcConfig
public DateTime UpdatedAt { get; set; }
[Column("IsActive", TypeName = "bit")]
public bool IsActive { get; set; } = true;
public bool IsActive { get; set; }
[Column("ConfigName", TypeName = "nvarchar(64)")]
[MaxLength(100)]

View File

@ -42,7 +42,7 @@ public class RobotSafetyConfig
public DateTime UpdatedAt { get; set; }
[Column("IsActive", TypeName = "bit")]
public bool IsActive { get; set; } = true;
public bool IsActive { get; set; }
[Column("ConfigName", TypeName = "nvarchar(64)")]
[MaxLength(100)]

View File

@ -36,7 +36,7 @@ public class RobotSimulationConfig
public DateTime UpdatedAt { get; set; }
[Column("IsActive", TypeName = "bit")]
public bool IsActive { get; set; } = true;
public bool IsActive { get; set; }
[Column("ConfigName", TypeName = "nvarchar(64)")]
[MaxLength(100)]

View File

@ -24,7 +24,7 @@ public class RobotVDA5050Config
public string VDA5050HostServer { get; set; }
[Column("VDA5050_Port", TypeName = "int")]
public int VDA5050Port { get; set; } = 1883;
public int VDA5050Port { get; set; }
[Column("VDA5050_UserName", TypeName = "nvarchar(64)")]
[MaxLength(50)]
@ -58,7 +58,7 @@ public class RobotVDA5050Config
public DateTime UpdatedAt { get; set; }
[Column("IsActive", TypeName = "bit")]
public bool IsActive { get; set; } = true;
public bool IsActive { get; set; }
[Column("ConfigName", TypeName = "nvarchar(64)")]
[MaxLength(100)]

View File

@ -3,7 +3,6 @@ using MQTTnet.Protocol;
using RobotApp.Common.Shares;
using RobotApp.VDA5050;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace RobotApp.Services;

View File

@ -1,30 +1,26 @@
using RobotApp.Common.Shares.Enums;
using Microsoft.EntityFrameworkCore;
using RobotApp.Common.Shares.Enums;
using RobotApp.Interfaces;
using RobotApp.Services.Robot.Simulation;
namespace RobotApp.Services.Robot;
public class RobotConfiguration
public class RobotConfiguration(IServiceProvider ServiceProvider, Logger<RobotConfiguration> Logger) : BackgroundService
{
public string SerialNumber { get; set; } = "Robot-Demo";
public NavigationType NavigationType { get; set; } = NavigationType.Differential;
public VDA5050.VDA5050Setting VDA5050Setting { get; set; } = new()
{
HostServer = "172.20.235.176",
Port = 1883,
UserName = "robotics",
Password = "robotics",
Manufacturer = "PhenikaaX",
Version = "0.0.1",
PublishRepeat = 2,
};
public string PLCAddress { get; set; } = "127.0.0.1";
public int PLCPort { get; set; } = 502;
public byte PLCUnitId { get; set; } = 1;
public bool IsSimulation { get; set; } = true;
public SimulationModel SimulationModel { get; set; } = new();
public string XlocAddress { get; set; } = "http://192.168.195.56:50050";
public Dictionary<SafetySpeed, double> SafetySpeedMap = new ()
public bool IsReady { get; private set; } = false;
public string SerialNumber { get; private set; } = "";
public double RadiusWheel { get; private set; } = 0.1;
public double Width { get; private set; } = 0.606;
public double Length { get; private set; } = 1.106;
public double Height { get; private set; } = 0.5;
public NavigationType NavigationType { get; private set; } = NavigationType.Differential;
public VDA5050.VDA5050Setting VDA5050Setting { get; private set; } = new();
public string PLCAddress { get; private set; } = "127.0.0.1";
public int PLCPort { get; private set; } = 502;
public byte PLCUnitId { get; private set; } = 1;
public bool IsSimulation { get; private set; } = true;
public SimulationModel SimulationModel { get; private set; } = new();
public Dictionary<SafetySpeed, double> SafetySpeedMap { get; private set; } = new()
{
{ SafetySpeed.Very_Slow, 0.15},
{ SafetySpeed.Slow, 0.3},
@ -34,4 +30,141 @@ public class RobotConfiguration
{ SafetySpeed.Fast, 1.5},
{ SafetySpeed.Very_Fast, 1.9},
};
public async Task LoadVDA5050ConfigAsync()
{
try
{
using var scope = ServiceProvider.CreateAsyncScope();
var appDb = scope.ServiceProvider.GetRequiredService<Data.ApplicationDbContext>();
var config = await appDb.RobotVDA5050Configs.FirstOrDefaultAsync(c => c.IsActive);
if (config is not null)
{
VDA5050Setting.HostServer = config.VDA5050HostServer;
VDA5050Setting.Port = config.VDA5050Port;
VDA5050Setting.UserName = config.VDA5050UserName;
VDA5050Setting.Password = config.VDA5050Password;
VDA5050Setting.Manufacturer = config.VDA5050Manufacturer;
VDA5050Setting.Version = config.VDA5050Version;
VDA5050Setting.PublishRepeat = config.VDA5050PublishRepeat;
VDA5050Setting.EnablePassword = config.VDA5050EnablePassword;
VDA5050Setting.EnableTls = config.VDA5050EnableTls;
SerialNumber = config.SerialNumber;
}
else throw new Exception("Chưa có cấu hình VDA5050.");
}
catch (Exception ex)
{
Logger.Warning($"Có lỗi xảy ra khi tải cấu hình VDA5050: {ex.Message}");
}
}
public async Task LoadRobotConfigAsync()
{
try
{
using var scope = ServiceProvider.CreateAsyncScope();
var appDb = scope.ServiceProvider.GetRequiredService<Data.ApplicationDbContext>();
var config = await appDb.RobotConfigs.FirstOrDefaultAsync(c => c.IsActive);
if (config is not null)
{
Width = config.Width;
Length = config.Length;
Height = config.Height;
RadiusWheel = config.RadiusWheel;
NavigationType = config.NavigationType;
}
else throw new Exception("Chưa có cấu hình robot.");
}
catch (Exception ex)
{
Logger.Warning($"Có lỗi xảy ra khi tải cấu hình robot: {ex.Message}");
}
}
public async Task LoadRobotSafetyConfigAsync()
{
try
{
using var scope = ServiceProvider.CreateAsyncScope();
var appDb = scope.ServiceProvider.GetRequiredService<Data.ApplicationDbContext>();
var config = await appDb.RobotSafetyConfigs.FirstOrDefaultAsync(c => c.IsActive);
if (config is not null)
{
SafetySpeedMap[SafetySpeed.Very_Slow] = config.SafetySpeedVerySlow;
SafetySpeedMap[SafetySpeed.Slow] = config.SafetySpeedSlow;
SafetySpeedMap[SafetySpeed.Normal] = config.SafetySpeedNormal;
SafetySpeedMap[SafetySpeed.Medium] = config.SafetySpeedMedium;
SafetySpeedMap[SafetySpeed.Optimal] = config.SafetySpeedOptimal;
SafetySpeedMap[SafetySpeed.Fast] = config.SafetySpeedFast;
SafetySpeedMap[SafetySpeed.Very_Fast] = config.SafetySpeedVeryFast;
}
else throw new Exception("Chưa có cấu hình safety robot.");
}
catch (Exception ex)
{
Logger.Warning($"Có lỗi xảy ra khi tải cấu hình safety robot: {ex.Message}");
}
}
public async Task LoadRobotSimulationConfigAsync()
{
try
{
using var scope = ServiceProvider.CreateAsyncScope();
var appDb = scope.ServiceProvider.GetRequiredService<Data.ApplicationDbContext>();
var config = await appDb.RobotSimulationConfigs.FirstOrDefaultAsync(c => c.IsActive);
if (config is not null)
{
IsSimulation = config.EnableSimulation;
SimulationModel.MaxVelocity = config.SimulationMaxVelocity;
SimulationModel.MaxAngularVelocity = config.SimulationMaxAngularVelocity;
SimulationModel.Acceleration = config.SimulationAcceleration;
SimulationModel.Deceleration = config.SimulationDeceleration;
}
else throw new Exception("Chưa có cấu hình simulation robot.");
}
catch (Exception ex)
{
Logger.Warning($"Có lỗi xảy ra khi tải cấu hình simulation robot: {ex.Message}");
}
}
public async Task LoadRobotPlcConfigAsync()
{
try
{
using var scope = ServiceProvider.CreateAsyncScope();
var appDb = scope.ServiceProvider.GetRequiredService<Data.ApplicationDbContext>();
var config = await appDb.RobotPlcConfigs.FirstOrDefaultAsync(c => c.IsActive);
if (config is not null)
{
PLCAddress = config.PLCAddress;
PLCPort = config.PLCPort;
PLCUnitId = config.PLCUnitId;
}
else throw new Exception("Chưa có cấu hình plc robot.");
}
catch (Exception ex)
{
Logger.Warning($"Có lỗi xảy ra khi tải cấu hình plc robot: {ex.Message}");
}
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
await LoadVDA5050ConfigAsync();
await LoadRobotConfigAsync();
await LoadRobotSimulationConfigAsync();
await LoadRobotSafetyConfigAsync();
await LoadRobotPlcConfigAsync();
IsReady = true;
}
catch (Exception ex)
{
Logger.Error($"Lỗi khởi tạo RobotConfiguration: {ex.Message}");
}
}
}

View File

@ -29,7 +29,9 @@ public partial class RobotController
PeripheralManager.RightMotorReady &&
BatteryManager.IsReady &&
Localization.IsReady &&
NavigationManager.IsReady) break;
NavigationManager.IsReady &&
RobotConfiguration.IsReady
) break;
}
await Task.Delay(1000, cancellationToken);
}
@ -37,7 +39,7 @@ public partial class RobotController
ConnectionManager.OrderUpdated += NewOrderUpdated;
ConnectionManager.ActionUpdated += NewInstantActionUpdated;
await ConnectionManager.StartConnection(cancellationToken);
//await ConnectionManager.StartConnection(cancellationToken);
Logger.Info("Robot đã kết nối tới Fleet Manager.");
StateManager.TransitionTo(SystemStateType.Standby);

View File

@ -1,10 +1,8 @@
using Google.Protobuf.WellKnownTypes;
using Grpc.Core;
using Grpc.Net.Client;
using RobotApp.Common.Shares;
using RobotApp.Interfaces;
using RobotApp.Services.Robot.Simulation;
using System;
using Xloc;
using static Xloc.XlocDiagnostics.Types;
@ -44,7 +42,7 @@ public class RobotLocalization(RobotConfiguration RobotConfiguration, Simulation
private readonly XlocData XlocData = new();
private readonly bool IsSimulation = RobotConfiguration.IsSimulation;
private readonly XlocServices.XlocServicesClient XlocClient = new(GrpcChannel.ForAddress(RobotConfiguration.XlocAddress));
private readonly XlocServices.XlocServicesClient XlocClient;
private WatchTimerAsync<RobotLocalization>? ReaderTimer;
private const int ReaderInterval = 50;
private int TimerCounter = 0;

View File

@ -30,7 +30,7 @@ public class RobotNavigation(RobotConfiguration RobotConfiguration, IServiceProv
{
if (IsSimulation)
{
SimNavigation = SimulationNavigationManager.GetNavigation(RobotConfiguration.SimulationModel.NavigationType, ServiceProvider);
SimNavigation = SimulationNavigationManager.GetNavigation(RobotConfiguration.NavigationType, ServiceProvider);
SimNavigation.OnNavigationFinished += NavigationFinished;
SimNavigation.Move(nodes, edges);
}
@ -40,7 +40,7 @@ public class RobotNavigation(RobotConfiguration RobotConfiguration, IServiceProv
{
if (IsSimulation)
{
SimNavigation = SimulationNavigationManager.GetNavigation(RobotConfiguration.SimulationModel.NavigationType, ServiceProvider);
SimNavigation = SimulationNavigationManager.GetNavigation(RobotConfiguration.NavigationType, ServiceProvider);
SimNavigation.MoveStraight(x, y);
}
}
@ -59,7 +59,7 @@ public class RobotNavigation(RobotConfiguration RobotConfiguration, IServiceProv
{
if (IsSimulation)
{
SimNavigation = SimulationNavigationManager.GetNavigation(RobotConfiguration.SimulationModel.NavigationType, ServiceProvider);
SimNavigation = SimulationNavigationManager.GetNavigation(RobotConfiguration.NavigationType, ServiceProvider);
SimNavigation.Rotate(angle * 180 / Math.PI);
}
}

View File

@ -242,6 +242,7 @@ public partial class RobotPeripheral(RobotConfiguration RobotConfiguration, IErr
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
if(RobotConfiguration.IsSimulation) return;
await Task.Yield();
while (!stoppingToken.IsCancellationRequested && !RobotConfiguration.IsSimulation)
{

View File

@ -54,13 +54,13 @@ public class DifferentialNavigation : SimulationNavigation
if (NavigationPath[MovePurePursuit.OnNodeIndex].Direction == RobotDirection.FORWARD)
{
AngularVelocityLeft /= RobotConfiguration.SimulationModel.RadiusWheel;
AngularVelocityRight = AngularVelocityRight / RobotConfiguration.SimulationModel.RadiusWheel * -1;
AngularVelocityLeft /= RobotConfiguration.RadiusWheel;
AngularVelocityRight = AngularVelocityRight / RobotConfiguration.RadiusWheel * -1;
}
else
{
AngularVelocityLeft = AngularVelocityLeft / RobotConfiguration.SimulationModel.RadiusWheel * -1;
AngularVelocityRight /= RobotConfiguration.SimulationModel.RadiusWheel;
AngularVelocityLeft = AngularVelocityLeft / RobotConfiguration.RadiusWheel * -1;
AngularVelocityRight /= RobotConfiguration.RadiusWheel;
}
VelocityController.SetSpeed(AngularVelocityLeft, AngularVelocityRight, CycleHandlerMilliseconds / 1000.0);
}

View File

@ -57,7 +57,7 @@ public class SimulationNavigation : INavigation, IDisposable
RobotConfiguration = scope.ServiceProvider.GetRequiredService<RobotConfiguration>();
PathPlanner = scope.ServiceProvider.GetRequiredService<RobotPathPlanner>();
VelocityController = new(Visualization, RobotConfiguration.SimulationModel);
AngularVelocity = RobotConfiguration.SimulationModel.MaxAngularVelocity * RobotConfiguration.SimulationModel.Width / 2 / 2 / RobotConfiguration.SimulationModel.RadiusWheel;
AngularVelocity = RobotConfiguration.SimulationModel.MaxAngularVelocity * RobotConfiguration.Width / 2 / 2 / RobotConfiguration.RadiusWheel;
}
protected void HandleNavigationStart()

View File

@ -1,15 +1,9 @@
using RobotApp.Common.Shares.Enums;
namespace RobotApp.Services.Robot.Simulation;
namespace RobotApp.Services.Robot.Simulation;
public class SimulationModel
{
public readonly double RadiusWheel = 0.1;
public readonly double Width = 0.606;
public readonly double Length = 1.106;
public readonly double MaxVelocity = 1.5;
public readonly double MaxAngularVelocity = 0.5;
public readonly double Acceleration = 2;
public readonly double Deceleration = 10;
public readonly NavigationType NavigationType = NavigationType.Differential;
public double MaxVelocity { get; set; } = 1.5;
public double MaxAngularVelocity { get; set; } = 0.5;
public double Acceleration { get; set; } = 2;
public double Deceleration { get; set; } = 10;
}

View File

@ -9,8 +9,8 @@ public class SimulationVisualization(RobotConfiguration RobotConfiguration)
public double Vy { get; private set; }
public double Omega { get; private set; }
private readonly double RadiusWheel = RobotConfiguration.SimulationModel.RadiusWheel;
private readonly double RadiusRobot = RobotConfiguration.SimulationModel.Width / 2;
private readonly double RadiusWheel = RobotConfiguration.RadiusWheel;
private readonly double RadiusRobot = RobotConfiguration.Width / 2;
public (double x, double y, double angle) UpdatePosition(double wL, double wR, double time)
{

View File

@ -10,7 +10,6 @@ public static class RobotExtensions
public static IServiceCollection AddRobot(this IServiceCollection services)
{
services.AddSingleton<RobotStateMachine>();
services.AddSingleton<RobotConfiguration>();
services.AddSingleton<RobotPathPlanner>();
services.AddSingleton<RobotConnection>();
services.AddSingleton<RobotActionStorage>();
@ -30,6 +29,7 @@ public static class RobotExtensions
services.AddHostedServiceSingleton<RobotVisualization>();
services.AddHostedServiceSingleton<RobotStates>();
services.AddHostedServiceSingleton<RobotFactsheet>();
services.AddHostedServiceSingleton<RobotConfiguration>();
return services;
}

Binary file not shown.