RobotNet/RobotNet.WebApp/Pages/RobotManager.razor
2025-10-15 15:15:53 +07:00

316 lines
13 KiB
Plaintext

@page "/robots"
@attribute [Authorize]
@implements IAsyncDisposable
@using RobotNet.MapShares
@using RobotNet.RobotShares.Dtos
@using RobotNet.Shares
@using RobotNet.WebApp.Clients
@using RobotNet.WebApp.Robots.Components
@inject ISnackbar Snackbar
@inject IDialogService Dialog
@inject IHttpClientFactory HttpFactory
@inject IJSRuntime JSRuntime
@inject NavigationManager NavManager
@inject RobotHubClient RobotHub
<PageTitle>Robot Manager</PageTitle>
<div class="d-flex flex-column overflow-hidden w-100 h-100 p-2">
<div class="d-flex justify-content-between align-items-center mb-2">
<MudTextField Value="txtSearch" T="string" Label="Search" Variant="Variant.Outlined" Adornment="Adornment.End" AdornmentIcon="@Icons.Material.Filled.Search"
AdornmentColor="Color.Secondary" Style="max-width: 550px" TextChanged="TextSearchChanged" />
<MudButton Class="me-3" Variant="Variant.Filled" Color="Color.Secondary" OnClick="AddSimulation" Size="Size.Large">Simulation</MudButton>
<MudButton StartIcon="@Icons.Material.Filled.Add" Variant="Variant.Filled" Color="Color.Primary" Size="Size.Large" OnClick="OpenCreateRobot">
NEW
</MudButton>
</div>
<div class="d-flex" style="height: 92%">
<MudTable Class="w-100" @ref="Table" T="RobotDto" Items="@RobotsShow" Loading="@IsLoading" Dense Hover FixedHeader ReadOnly Striped RowClass="cursor-pointer"
ServerData="ReloadData" MultiSelection @bind-SelectedItems="@RobotSelecteds"
Outlined="true" Height="95%">
<HeaderContent>
<MudTh>Name</MudTh>
<MudTh>Id</MudTh>
<MudTh>Model</MudTh>
<MudTh>Map</MudTh>
<MudTh>NavigationType</MudTh>
<MudTh>Online</MudTh>
<MudTh>State</MudTh>
<MudTh>Pin</MudTh>
<MudTh></MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Name">@context.Name</MudTd>
<MudTd DataLabel="Id">@context.RobotId</MudTd>
<MudTd DataLabel="Model">@context.ModelName</MudTd>
<MudTd DataLabel="Map">@context.MapName</MudTd>
<MudTd DataLabel="NavigationType">@context.NavigationType</MudTd>
<MudTd DataLabel="Online">
<MudAvatar Color="@(context.Online ? Color.Success : Color.Default)" Size="Size.Small"></MudAvatar>
</MudTd>
<MudTd DataLabel="State">@context.State</MudTd>
<MudTd DataLabel="Pin">@($"{context.Battery} %")</MudTd>
<MudTd>
<div class="d-flex justify-content-center">
<MudIconButton Icon="@Icons.Material.Filled.Delete" aria-label="Delete" Color="Color.Secondary" OnClick="() => DeleteRobot(context)" />
<MudIconButton Icon="@Icons.Material.Filled.TrendingFlat" aria-label="Show" Color="Color.Primary" OnClick="@(() =>NavManager.NavigateTo($"/robots/detail/{context.RobotId}"))" />
</div>
</MudTd>
</RowTemplate>
<PagerContent>
<MudTablePager />
</PagerContent>
</MudTable>
</div>
</div>
<MudDialog @bind-Visible="IsCreateRobotVisible">
<TitleContent>
<MudText Typo="Typo.h6">
Create New Robot
</MudText>
</TitleContent>
<DialogContent>
<MudContainer Class="d-flex flex-column">
<MudFocusTrap DefaultFocus="DefaultFocus.FirstChild">
<MudTextField Class="my-2" Variant="Variant.Outlined"
@bind-Value="RobotCreate.Name" T="string"
Label="Name" Required="true" Validation="@(new Func<string, IEnumerable<string>>(MapEditorHelper.NameValidation))"
RequiredError="Robot's' name is required!" Counter="127" />
<MudTextField Class="my-2" Variant="Variant.Outlined"
@bind-Value="RobotCreate.RobotId" T="string"
Label="RobotId" Required="true" Validation="@(new Func<string, IEnumerable<string>>(MapEditorHelper.RobotIdValidation))"
RequiredError="Robot's' Id is required!" Counter="127" />
<MudSelect Class="my-2" T="RobotModelDto" Label="Model" AnchorOrigin="Origin.BottomLeft" @bind-Value="RobotModelSelected" Variant="Variant.Outlined">
@foreach (var model in ListRobotModels)
{
<MudSelectItem Value="@model">@model.ModelName</MudSelectItem>
}
</MudSelect>
</MudFocusTrap>
</MudContainer>
</DialogContent>
<DialogActions>
<MudButton OnClick="@(() => IsCreateRobotVisible = false)" Variant="Variant.Filled">Cancel</MudButton>
<MudButton Color="Color.Primary" OnClick="CreateRobot" Variant="Variant.Filled">Create</MudButton>
</DialogActions>
</MudDialog>
@code {
private string? txtSearch { get; set; }
private bool IsLoading { get; set; }
private MudTable<RobotDto>? Table;
private readonly List<RobotDto> Robots = new();
private List<RobotDto> RobotsShow = new();
private HashSet<RobotDto> RobotSelecteds = [];
private bool IsCreateRobotVisible { get; set; }
private readonly RobotCreateModel RobotCreate = new();
private readonly List<RobotModelDto> ListRobotModels = new();
private RobotModelDto? RobotModelSelected { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (!firstRender) return;
await LoadRobots();
RobotHub.IsOnlineChanged += RobotIsOnlineChanged;
await RobotHub.StartAsync();
}
private void RobotIsOnlineChanged(IEnumerable<RobotOnlineStateDto> robots)
{
foreach (var robot in robots)
{
var robotDto = Robots.FirstOrDefault(r => r.RobotId == robot.RobotId);
if (robotDto is null) continue;
robotDto.Online = robot.IsOnline;
robotDto.State = robot.State;
robotDto.Battery = robot.Battery;
}
StateHasChanged();
}
private async Task LoadRobots()
{
IsLoading = true;
Robots.Clear();
StateHasChanged();
using var Http = HttpFactory.CreateClient("RobotManagerAPI");
var robots = await Http.GetFromJsonAsync<MessageResult<IEnumerable<RobotDto>>>("api/Robots");
if (robots is null) Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
else if (!robots.IsSuccess) Snackbar.Add($"Có lỗi xảy ra: {robots.Message}", Severity.Error);
else if (robots.Data is null || !robots.Data.Any())
{
Snackbar.Add("Không có robot nào", Severity.Warning);
RobotsShow.Clear();
Table?.ReloadServerData();
}
else
{
Robots.AddRange(robots.Data.OrderByDescending(robot => robot.Online));
Table?.ReloadServerData();
}
IsLoading = false;
StateHasChanged();
}
private bool FilterFunc(RobotDto robot)
{
if (string.IsNullOrWhiteSpace(txtSearch))
return true;
if (robot.Name.Contains(txtSearch, StringComparison.OrdinalIgnoreCase))
return true;
if (robot.RobotId.Contains(txtSearch, StringComparison.OrdinalIgnoreCase))
return true;
if (robot.ModelName.Contains(txtSearch, StringComparison.OrdinalIgnoreCase))
return true;
if (robot.MapName.ToString().Contains(txtSearch, StringComparison.OrdinalIgnoreCase))
return true;
if (robot.State.ToString().Contains(txtSearch, StringComparison.OrdinalIgnoreCase))
return true;
if (robot.NavigationType.ToString().Contains(txtSearch, StringComparison.OrdinalIgnoreCase))
return true;
return false;
}
private Task<TableData<RobotDto>> ReloadData(TableState state, CancellationToken _)
{
var robots = new List<RobotDto>();
Robots.ForEach(robot =>
{
if (FilterFunc(robot)) robots.Add(robot);
});
RobotsShow = robots.Skip(state.Page * state.PageSize).Take(state.PageSize).ToList();
return Task.FromResult(new TableData<RobotDto>() { TotalItems = robots.Count(), Items = RobotsShow });
}
private async Task DeleteRobot(RobotDto robot)
{
var parameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Content, "Bạn có chắc chắn muốn xóa robot đi không?" },
{ x => x.ConfirmText, "Delete" },
{ x => x.Color, Color.Secondary }
};
var ConfirmDelete = await Dialog.ShowAsync<ConfirmDialog>("Xoá Robot Model", parameters);
var result = await ConfirmDelete.Result;
if (result is not null && result.Data is not null && bool.TryParse(result.Data.ToString(), out bool data) && data)
{
using var Http = HttpFactory.CreateClient("RobotManagerAPI");
var delete = await Http.DeleteFromJsonAsync<MessageResult>($"api/Robots/{robot.RobotId}");
if (delete is null) Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
else if (!delete.IsSuccess) Snackbar.Add($"Có lỗi xảy ra: {delete.Message}", Severity.Error);
else
{
Robots.Remove(robot);
Snackbar.Add("Xóa robot thành công!", Severity.Success);
Table?.ReloadServerData();
}
StateHasChanged();
}
}
private void TextSearchChanged(string text)
{
txtSearch = text;
Table?.ReloadServerData();
}
private async Task OpenCreateRobot()
{
ListRobotModels.Clear();
using var Http = HttpFactory.CreateClient("RobotManagerAPI");
var robotmodels = await Http.GetFromJsonAsync<IEnumerable<RobotModelDto>>($"api/RobotModels?txtSearch={txtSearch}");
if (robotmodels is null || robotmodels.Count() == 0)
{
Snackbar.Add("Vui lòng tạo robot model", Severity.Error);
return;
}
else ListRobotModels.AddRange(robotmodels);
RobotModelSelected = ListRobotModels.First();
RobotCreate.Name = "";
RobotCreate.RobotId = "";
RobotCreate.ModelId = Guid.Empty;
IsCreateRobotVisible = true;
StateHasChanged();
}
private async Task CreateRobot()
{
if (RobotModelSelected is null)
{
Snackbar.Add("Hãy chọn robot model!", Severity.Warning);
return;
}
if (string.IsNullOrEmpty(RobotCreate.Name))
{
Snackbar.Add("Trường tên không được để trống", Severity.Warning);
return;
}
var nameInvalid = MapEditorHelper.NameChecking(RobotCreate.Name);
if (!nameInvalid.IsSuccess)
{
Snackbar.Add(nameInvalid.returnStr, Severity.Warning);
return;
}
if (string.IsNullOrEmpty(RobotCreate.RobotId))
{
Snackbar.Add("Trường RobotId không được để trống", Severity.Warning);
return;
}
RobotCreate.ModelId = RobotModelSelected.Id;
using var Http = HttpFactory.CreateClient("RobotManagerAPI");
var create = await (await Http.PostAsJsonAsync("api/Robots", RobotCreate)).Content.ReadFromJsonAsync<MessageResult<RobotDto>>();
if (create is null) Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
else if (!create.IsSuccess) Snackbar.Add($"Có lỗi xảy ra: {create.Message}", Severity.Error);
else if (create.Data is null) Snackbar.Add("Tạo robot không thành công", Severity.Error);
else
{
Robots.Add(new()
{
Id = create.Data.Id,
RobotId = create.Data.RobotId,
Name = create.Data.Name,
ModelId = create.Data.ModelId,
ModelName = create.Data.ModelName,
NavigationType = create.Data.NavigationType,
});
Table?.ReloadServerData();
IsCreateRobotVisible = false;
Snackbar.Add("Tạo robot thành công!", Severity.Success);
}
StateHasChanged();
}
private async Task AddSimulation()
{
if (!RobotSelecteds.Any()) return;
var robotIds = RobotSelecteds.Select(r => r.RobotId).ToList();
using var Http = HttpFactory.CreateClient("RobotManagerAPI");
var onRobot = await (await Http.PostAsJsonAsync($"api/Robots/simulation", robotIds)).Content.ReadFromJsonAsync<MessageResult>();
if (onRobot is null) Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
else if (!onRobot.IsSuccess) Snackbar.Add($"Có lỗi xảy ra: {onRobot.Message}", Severity.Error);
else Snackbar.Add("Khởi tạo thành công!", Severity.Success);
StateHasChanged();
}
public async ValueTask DisposeAsync()
{
RobotHub.IsOnlineChanged -= RobotIsOnlineChanged;
await RobotHub.StopAsync();
}
}