RobotNet/RobotNet.WebApp/Maps/Components/Editor/Edge/MapEdge.razor
2025-10-15 15:15:53 +07:00

256 lines
10 KiB
Plaintext

@implements IDisposable
@inject IJSRuntime JSRuntime
@inject IHttpClientFactory HttpClientFactory
@inject ISnackbar Snackbar
<g>
@foreach (var edge in Models)
{
<Edge Model="edge" DoubleClick="OnEdgeDoubleClick" EditorState="@EditorState" />
}
@foreach (var edge in Models)
{
<EdgeDirection Model="edge" />
}
</g>
<MudDialog @bind-Visible="updateEdgeVisible">
<TitleContent>
<MudText Typo="Typo.h6">
Update edge @UpdateModel.Id
</MudText>
</TitleContent>
<DialogContent>
<div class="d-flex flex-row align-items-center">
<MudText>
Length: @SelectModelLength m
</MudText>
</div>
<div class="d-flex flex-row mt-2">
<div class="d-flex flex-column">
<div class="mb-2">
<MudNumericField @bind-Value="UpdateModel.ControlPoint1X" Label="Control Point 1 X" Variant="Variant.Outlined" Margin="Margin.Dense" Step="0.1" ReadOnly=@(MapIsActive) />
</div>
<div class="mb-2">
<MudNumericField @bind-Value="UpdateModel.ControlPoint2X" Label="Control Point 2 X" Variant="Variant.Outlined" Margin="Margin.Dense" Step="0.1" ReadOnly=@(MapIsActive) />
</div>
<div class="mb-2">
<MudSelect @bind-Value="UpdateModel.DirectionAllowed" Label="Direction Allowed" Variant="Variant.Outlined" AnchorOrigin="Origin.BottomLeft" Margin="Margin.Dense" Dense ReadOnly=@(MapIsActive)>
<MudSelectItem Value="@(DirectionAllowed.None)" />
<MudSelectItem Value="@(DirectionAllowed.Backward)" />
<MudSelectItem Value="@(DirectionAllowed.Forward)" />
<MudSelectItem Value="@(DirectionAllowed.Both)" />
</MudSelect>
</div>
<div class="mb-2">
<MudNumericField @bind-Value="UpdateModel.AllowedDeviationXy" Label="AllowedDeviationXy" Step="0.01" Min="0" Variant="Variant.Outlined" Margin="Margin.Dense" ReadOnly=@(MapIsActive) />
</div>
<div class="mb-2">
<MudNumericField @bind-Value="UpdateModel.AllowedDeviationTheta" Label="AllowedDeviationTheta" Step="1" Min="0" Max="180" Variant="Variant.Outlined" Margin="Margin.Dense" ReadOnly=@(MapIsActive) />
</div>
<div class="d-flex">
<MudCheckBox @bind-Value="UpdateModel.RotationAllowed" Label="Rotation Allowed" LabelPlacement="Placement.Start" ReadOnly=@(MapIsActive) UncheckedColor="Color.Default" Color="Color.Success" />
</div>
</div>
<div class="d-flex flex-column ms-2">
<div class="mb-2">
<MudNumericField @bind-Value="UpdateModel.ControlPoint1Y" Label="Control Point 1 Y" Variant="Variant.Outlined" Margin="Margin.Dense" Step="0.1" ReadOnly=@(MapIsActive) />
</div>
<div class="mb-2">
<MudNumericField @bind-Value="UpdateModel.ControlPoint2Y" Label="Control Point 2 Y" Variant="Variant.Outlined" Margin="Margin.Dense" Step="0.1" ReadOnly=@(MapIsActive) />
</div>
<div class="mb-2">
<MudNumericField @bind-Value="UpdateModel.MaxSpeed" Label="Max Speed" Variant="Variant.Outlined" Margin="Margin.Dense" Step="0.1" Min="0" ReadOnly=@(MapIsActive) />
</div>
<div class="mb-2">
<MudNumericField @bind-Value="UpdateModel.MaxRotationSpeed" Label="Max Rotation Speed" Variant="Variant.Outlined" Margin="Margin.Dense" Step="0.1" Min="0" ReadOnly=@(MapIsActive) />
</div>
<div class="mb-2">
<MudNumericField @bind-Value="UpdateModel.MaxHeight" Label="Max Height" Variant="Variant.Outlined" Margin="Margin.Dense" Step="0.1" Min="0" ReadOnly=@(MapIsActive) />
</div>
<div class="mb-2">
<MudNumericField @bind-Value="UpdateModel.MinHeight" Label="Min Height" Variant="Variant.Outlined" Margin="Margin.Dense" Step="0.1" Min="0" ReadOnly=@(MapIsActive) />
</div>
</div>
<div class="d-flex flex-column ms-2 flex-grow-1">
<div class="d-flex flex-row mb-2">
<MudSelect @bind-Value=ActionSelected Margin="Margin.Dense" T="ActionDto" Label="Action" Variant="Variant.Outlined" Class="w-100" AnchorOrigin="Origin.BottomLeft">
@foreach (var action in Actions)
{
<MudSelectItem Value="@action">@action.Name</MudSelectItem>
}
</MudSelect>
<MudFab Class="ms-2" Color="Color.Secondary" StartIcon="@Icons.Material.Filled.Add" Size="Size.Medium" OnClick="AddAction" />
</div>
<div class="paper-action">
@foreach (var actionId in UpdateModel.Actions)
{
<div class="m-1" style="height: fit-content">
<MudButton Variant="Variant.Filled" Color="Color.Info" Style="text-transform:none" OnClick="(() => DeleteAction(actionId))">
@(Actions.FirstOrDefault(ac => ac.Id == actionId)?.Name)
</MudButton>
</div>
}
</div>
</div>
</div>
</DialogContent>
<DialogActions>
<MudButton OnClick="CancelUpdateEdge" Class="px-10" Variant="Variant.Filled">Cancel</MudButton>
<MudButton OnClick="UpdateEdge" Variant="Variant.Filled" Color="Color.Primary" Class="px-10" Disabled=@(MapIsActive || EditorState == EditorState.View)>Update</MudButton>
</DialogActions>
</MudDialog>
@code {
[Parameter, EditorRequired]
public MapEdgeModel Models { get; set; } = null!;
[CascadingParameter]
protected bool MapIsActive { get; set; }
[Parameter]
public EventCallback<EdgeModel?> EdgeSelectedChanged { get; set; }
[Parameter]
public EditorState EditorState { get; set; }
private EdgeUpdateModel UpdateModel = new();
private bool updateEdgeVisible;
private string ActionsText { get; set; } = "";
private double SelectModelLength;
private List<ActionDto> Actions = [];
private ActionDto? ActionSelected = null;
private HttpClient Http = default!;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (!firstRender) return;
Http = HttpClientFactory.CreateClient("MapManagerAPI");
Models.Changed += StateHasChanged;
Models.EdgeSelectedChanged += async (model) => await EdgeSelectedChanged.InvokeAsync(model);
}
private async Task LoadActionAsync(Guid mapVersionId)
{
var result = await Http.GetFromJsonAsync<IEnumerable<ActionDto>>($"api/Actions/{mapVersionId}");
if (result is not null && result.Any())
{
Actions.Clear();
Actions.AddRange(result);
}
if (Actions.Any()) ActionSelected = Actions.First();
StateHasChanged();
}
private void AddAction()
{
if (ActionSelected is null) return;
if (UpdateModel.Actions.Any(action => action == ActionSelected.Id))
{
Snackbar.Add("Action đã tồn tại", Severity.Warning);
return;
}
UpdateModel.Actions = [.. UpdateModel.Actions, ActionSelected.Id];
StateHasChanged();
}
private void DeleteAction(Guid actionId)
{
UpdateModel.Actions = UpdateModel.Actions.Where(a => actionId != a).ToArray();
StateHasChanged();
}
private async Task OnEdgeDoubleClick(EdgeModel model)
{
if (model == null || EditorState != EditorState.NavigationEdit) return;
await LoadActionAsync(model.MapId);
UpdateModel.Id = model.Id;
UpdateModel.ControlPoint1X = model.ControlPoint1X;
UpdateModel.ControlPoint1Y = model.ControlPoint1Y;
UpdateModel.ControlPoint2X = model.ControlPoint2X;
UpdateModel.ControlPoint2Y = model.ControlPoint2Y;
UpdateModel.DirectionAllowed = model.DirectionAllowed;
UpdateModel.MaxSpeed = model.MaxSpeed;
UpdateModel.MaxHeight = model.MaxHeight;
UpdateModel.MinHeight = model.MinHeight;
UpdateModel.RotationAllowed = model.RotationAllowed;
UpdateModel.MaxRotationSpeed = model.MaxRotationSpeed;
UpdateModel.AllowedDeviationXy = model.AllowedDeviationXy;
UpdateModel.AllowedDeviationTheta = model.AllowedDeviationTheta;
UpdateModel.Actions = [];
var actions = JsonSerializer.Deserialize<Guid[]>(model.Actions);
if (actions is not null && actions.Length > 0)
{
UpdateModel.Actions = [.. actions];
}
SelectModelLength = MapEditorHelper.GetEdgeLength(new()
{
TrajectoryDegree = model.TrajectoryDegree,
X1 = model.X1,
Y1 = model.Y1,
X2 = model.X2,
Y2 = model.Y2,
ControlPoint1X = model.ControlPoint1X,
ControlPoint1Y = model.ControlPoint1Y,
ControlPoint2X = model.ControlPoint2X,
ControlPoint2Y = model.ControlPoint2Y,
});
updateEdgeVisible = true;
StateHasChanged();
}
private void CancelUpdateEdge()
{
updateEdgeVisible = false;
StateHasChanged();
}
private async Task UpdateEdge()
{
var selectedModel = Models.FirstOrDefault(e => e.Id == UpdateModel.Id);
if (selectedModel == null)
{
updateEdgeVisible = false;
StateHasChanged();
return;
}
var result = await (await Http.PutAsJsonAsync($"api/Edges", UpdateModel)).Content.ReadFromJsonAsync<MessageResult>();
if (result is null)
{
Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
return;
}
else if (!result.IsSuccess)
{
Snackbar.Add(result.Message, Severity.Error);
return;
}
selectedModel.UpdateData(UpdateModel);
updateEdgeVisible = false;
Snackbar.Add("Cập nhật thành công", Severity.Success);
StateHasChanged();
}
public void Dispose()
{
Models.Changed -= StateHasChanged;
GC.SuppressFinalize(this);
}
}