@using RobotNet10.FleetManager.Shared.DTOs.Robot
@using RobotNet10.FleetManager.Client.Services
@using RobotNet10.MapEditor.Services.API
@using RobotNet10.MapEditor.Shared.DTOs.LayoutData
@using RobotNet10.MapEditor.Shared.DTOs.Node
@using RobotNet10.FleetManager.Shared.Models
@using RobotNet10.Shared
@using System.Net.Http.Json
@inject HttpClient HttpClient
@inject ISnackbar Snackbar
@inject RobotApiService RobotApiService
@inject MapManagerApiService MapApiService
Manual Order
@if (isLoadingNodes)
{
Loading nodes...
}
else if (nodes != null && nodes.Any())
{
-- Select Node --
@foreach (var node in nodes.Where(n => !string.IsNullOrEmpty(n.NodeName)).OrderBy(n => n.NodeName))
{
var displayName = !string.IsNullOrWhiteSpace(node.NodeName)
? $"{node.NodeName} ({node.NodeId})"
: node.NodeId;
@displayName
}
}
else
{
No nodes available
}
@if (!string.IsNullOrWhiteSpace(selectedNodeName))
{
var selectedNode = nodes?.FirstOrDefault(n =>
(!string.IsNullOrWhiteSpace(n.NodeName) && n.NodeName == selectedNodeName) ||
(string.IsNullOrWhiteSpace(n.NodeName) && n.NodeId == selectedNodeName));
@if (selectedNode != null)
{
Node ID: @selectedNode.NodeId
@if (!string.IsNullOrWhiteSpace(selectedNode.NodeDescription))
{
Description: @selectedNode.NodeDescription
}
Position: X: @selectedNode.X.ToString("F2")m, Y: @selectedNode.Y.ToString("F2")m
}
}
@if (isSending)
{
Sending...
}
else
{
Send Order
}
@if (isCanceling)
{
Canceling...
}
else
{
Cancel Order
}
Recent Orders
@if (orderHistory.Count > 0)
{
@foreach (var order in orderHistory.Take(10))
{
@order.NodeName - @order.Timestamp.ToString("g")
@if (!order.IsSuccess)
{
Failed
}
else
{
Success
}
}
}
else
{
No orders sent yet
}
@code {
[Parameter]
public string RobotId { get; set; } = string.Empty;
[Parameter]
public Guid? MapId { get; set; }
private string selectedNodeName = string.Empty;
private List nodes = new();
private List orderHistory = new();
private bool isSending = false;
private bool isCanceling = false;
private bool isLoadingNodes = false;
private Dictionary validationErrors = new();
protected override async Task OnInitializedAsync()
{
await LoadNodesAsync();
}
private async Task LoadNodesAsync()
{
if (!MapId.HasValue || MapId.Value == Guid.Empty)
{
// Try to get MapId from robot
try
{
var robot = await RobotApiService.GetByRobotIdAsync(RobotId);
if (robot?.MapId.HasValue == true)
{
MapId = robot.MapId;
}
else
{
nodes = new List();
return;
}
}
catch (Exception ex)
{
Snackbar.Add($"Error loading robot: {ex.Message}", Severity.Warning);
nodes = new List();
return;
}
}
isLoadingNodes = true;
StateHasChanged();
try
{
var layoutData = await MapApiService.GetLayoutDataAsync(MapId.Value);
nodes = layoutData.Nodes?.ToList() ?? new List();
}
catch (Exception ex)
{
Snackbar.Add($"Error loading nodes: {ex.Message}", Severity.Warning);
nodes = new List();
}
finally
{
isLoadingNodes = false;
StateHasChanged();
}
}
private string? GetValidationError(string fieldName)
{
return validationErrors.TryGetValue(fieldName, out var error) ? error : null;
}
private async Task SendOrder()
{
if (string.IsNullOrWhiteSpace(RobotId))
{
Snackbar.Add("Robot ID is required", Severity.Warning);
return;
}
if (string.IsNullOrWhiteSpace(selectedNodeName))
{
validationErrors["NodeName"] = "Please select a node";
StateHasChanged();
return;
}
isSending = true;
validationErrors.Clear();
StateHasChanged();
try
{
// Find the selected node to get NodeName (preferred) or NodeId
var selectedNode = nodes?.FirstOrDefault(n =>
(!string.IsNullOrWhiteSpace(n.NodeName) && n.NodeName == selectedNodeName) ||
(string.IsNullOrWhiteSpace(n.NodeName) && n.NodeId == selectedNodeName));
if (selectedNode == null)
{
Snackbar.Add("Selected node not found", Severity.Warning);
return;
}
// Use NodeName if available, otherwise use NodeId
var nodeNameToSend = !string.IsNullOrWhiteSpace(selectedNode.NodeName)
? selectedNode.NodeName
: selectedNode.NodeId;
// Build order request
var orderRequest = new RobotMoveToNodeModel
{
RobotId = RobotId,
NodeName = nodeNameToSend,
LastAngle = null, // Optional, can be enhanced later
};
// Call API endpoint
var response = await HttpClient.PostAsJsonAsync($"/api/RobotManager/MoveToNode", orderRequest);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadFromJsonAsync();
if (result is null)
{
Snackbar.Add($"Sending order failed", Severity.Warning);
orderHistory.Insert(0, new SentOrder
{
NodeName = selectedNodeName,
Timestamp = DateTime.UtcNow,
IsSuccess = false
});
}
else if (!result.IsSuccess)
{
Snackbar.Add($"{result.Message}", Severity.Warning);
orderHistory.Insert(0, new SentOrder
{
NodeName = selectedNodeName,
Timestamp = DateTime.UtcNow,
IsSuccess = false
});
}
else
{
Snackbar.Add($"Order to node '{selectedNodeName}' sent successfully", Severity.Success);
// Add to history
orderHistory.Insert(0, new SentOrder
{
NodeName = selectedNodeName,
Timestamp = DateTime.UtcNow,
IsSuccess = true
});
// Clear selection
selectedNodeName = string.Empty;
}
}
else if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
{
Snackbar.Add($"Robot with ID '{RobotId}' not found", Severity.Error);
orderHistory.Insert(0, new SentOrder
{
NodeName = selectedNodeName,
Timestamp = DateTime.UtcNow,
IsSuccess = false
});
}
else if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
{
var error = await response.Content.ReadAsStringAsync();
Snackbar.Add($"Invalid order request: {error}", Severity.Warning);
orderHistory.Insert(0, new SentOrder
{
NodeName = selectedNodeName,
Timestamp = DateTime.UtcNow,
IsSuccess = false
});
}
else
{
var error = await response.Content.ReadAsStringAsync();
Snackbar.Add($"Error sending order: {error}", Severity.Error);
orderHistory.Insert(0, new SentOrder
{
NodeName = selectedNodeName,
Timestamp = DateTime.UtcNow,
IsSuccess = false
});
}
}
catch (Exception ex)
{
Snackbar.Add($"Error sending order: {ex.Message}", Severity.Error);
orderHistory.Insert(0, new SentOrder
{
NodeName = selectedNodeName ?? "Unknown",
Timestamp = DateTime.UtcNow,
IsSuccess = false
});
}
finally
{
isSending = false;
StateHasChanged();
}
}
private async Task CancelOrder()
{
if (string.IsNullOrWhiteSpace(RobotId))
{
Snackbar.Add("Robot ID is required", Severity.Warning);
return;
}
isCanceling = true;
StateHasChanged();
try
{
// Call API endpoint
var response = await HttpClient.DeleteAsync($"/api/RobotManager/MoveToNode/{RobotId}");
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadFromJsonAsync();
if (result is null)
{
Snackbar.Add($"Canceling order failed", Severity.Warning);
}
else if (!result.IsSuccess)
{
Snackbar.Add($"{result.Message}", Severity.Warning);
}
else
{
Snackbar.Add($"Order canceled successfully", Severity.Success);
}
}
else if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
{
Snackbar.Add($"Robot with ID '{RobotId}' not found", Severity.Error);
}
else
{
var error = await response.Content.ReadAsStringAsync();
Snackbar.Add($"Error canceling order: {error}", Severity.Error);
}
}
catch (Exception ex)
{
Snackbar.Add($"Error canceling order: {ex.Message}", Severity.Error);
}
finally
{
isCanceling = false;
StateHasChanged();
}
}
// Helper class for order history
private class SentOrder
{
public string NodeName { get; set; } = string.Empty;
public DateTime Timestamp { get; set; }
public bool IsSuccess { get; set; }
}
}