316 lines
13 KiB
Plaintext
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();
|
|
}
|
|
}
|