@page "/robot-order"
@rendermode InteractiveWebAssemblyNoPrerender
@using System.Text.Json
@using System.Text.Json.Serialization
@inject IJSRuntime JS
@inject IDialogService DialogService
@inject HttpClient Http
@inject ISnackbar Snackbar
@code {
// ================= STATE =================
private OrderMessage Order { get; set; } = new();
private string OrderJson = ""; // 🔥 CACHE JSON (QUAN TRỌNG)
private bool copied;
private bool? sendSuccess;
private bool? cancelSuccess;
private CancellationTokenSource? _copyCts;
// ================= INIT =================
protected override void OnInitialized()
{
RebuildOrderJson();
}
// ================= CORE FIX =================
private void RebuildOrderJson()
{
OrderJson = JsonSerializer.Serialize(
Order.ToSchemaObject(),
new JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
});
}
private async Task OpenImportDialog()
{
var dialog = await DialogService.ShowAsync(
"Import Order JSON",
new DialogOptions
{
FullWidth = true,
MaxWidth = MaxWidth.Large
});
var result = await dialog.Result;
if (result is not null && !result.Canceled && result.Data is OrderMsg imported)
{
Order.Import(imported);
RebuildOrderJson();
StateHasChanged();
}
}
private void OnOrderChanged()
{
RebuildOrderJson(); // 🔥 JSON luôn rebuild
StateHasChanged(); // 🔥 ép render
}
// ================= NODE =================
void AddNode()
{
Order.Nodes.Add(new Node
{
NodeId = $"NODE_{Order.Nodes.Count + 1}",
SequenceId = Order.Nodes.Count,
Released = true,
NodePosition = new VDA5050.Order.NodePosition { MapId = "MAP_01" }
});
}
void RemoveNode(Node node)
{
Order.Nodes.Remove(node);
Order.Edges.RemoveAll(e => e.StartNodeId == node.NodeId || e.EndNodeId == node.NodeId);
ResequenceNodes();
}
void ResequenceNodes()
{
for (int i = 0; i < Order.Nodes.Count; i++)
Order.Nodes[i].SequenceId = i;
}
// ================= EDGE =================
void AddEdge()
{
if (Order.Nodes.Count == 0)
return;
var start = Order.Nodes[0].NodeId;
var end = Order.Nodes.Count > 1
? Order.Nodes[1].NodeId
: start; // 👈 1 node thì start = end
Order.Edges.Add(new VDA5050.Order.Edge
{
EdgeId = $"EDGE_{Order.Edges.Count + 1}",
StartNodeId = start,
EndNodeId = end
});
}
void RemoveEdge(VDA5050.Order.Edge edge)
{
Order.Edges.Remove(edge);
}
// ================= ACTION =================
void AddAction(Node node)
{
var list = node.Actions?.ToList() ?? new();
list.Add(new VDA5050.InstantAction.Action
{
ActionId = Guid.NewGuid().ToString(),
ActionType = ActionType.startPause.ToString(),
BlockingType = "NONE",
ActionParameters = Array.Empty()
});
node.Actions = list.ToArray();
}
void RemoveAction(Node node, VDA5050.InstantAction.Action action)
{
node.Actions = node.Actions?.Where(a => a != action).ToArray() ?? [];
}
void AddActionParameter(VDA5050.InstantAction.Action act)
{
var list = (act.ActionParameters ?? []).ToList();
list.Add(new UiActionParameter());
act.ActionParameters = list.ToArray();
}
void RemoveActionParameter(VDA5050.InstantAction.Action act, ActionParameter param)
{
act.ActionParameters = act.ActionParameters?.Where(p => p != param).ToArray() ?? [];
}
// ================= SEND / COPY =================
async Task SendOrderToServer()
{
// reset trạng thái trước khi gửi
sendSuccess = null;
StateHasChanged();
try
{
var orderMsg = JsonSerializer.Deserialize(OrderJson,
new JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
});
if (orderMsg is null)
{
Snackbar.Add("Unable to convert JSON to Order", Severity.Warning);
return;
}
if (orderMsg.Nodes.Length < 1)
{
Snackbar.Add("The order must contain at least one node (number of nodes > 0)", Severity.Warning);
return;
}
if (orderMsg.Nodes.Length - 1 != orderMsg.Edges.Length)
{
Snackbar.Add("Order must have a number of edges equal to the number of nodes minus 1", Severity.Warning);
return;
}
foreach(var edge in orderMsg.Edges)
{
if (!orderMsg.Nodes.Any(n => n.NodeId == edge.StartNodeId))
{
Snackbar.Add($"The edge {edge.EdgeId} references a startNodeId {edge.StartNodeId} that does not exist in the list of nodes", Severity.Warning);
return;
}
if (!orderMsg.Nodes.Any(n => n.NodeId == edge.EndNodeId))
{
Snackbar.Add($"The edge {edge.EdgeId} references a startNodeId {edge.EndNodeId} that does not exist in the list of nodes", Severity.Warning);
return;
}
}
var response = await Http.PostAsJsonAsync("/api/order",orderMsg);
sendSuccess = response.IsSuccessStatusCode;
}
catch(JsonException jsonEx)
{
Snackbar.Add($"Json to Order failed: {jsonEx.Message}", Severity.Warning);
}
catch (Exception ex)
{
Snackbar.Add($"Send Order failed: {ex.Message}", Severity.Warning);
sendSuccess = false;
}
StateHasChanged();
// 🔥 AUTO RESET SAU 2 GIÂY
_ = Task.Run(async () =>
{
await Task.Delay(2000);
// quay về trạng thái Send
sendSuccess = null;
await InvokeAsync(StateHasChanged);
});
}
async Task CancelOrder()
{
// reset trạng thái trước khi gửi
cancelSuccess = null;
StateHasChanged();
try
{
var res = await Http.PostAsync("/api/order/cancel", null);
cancelSuccess = res.IsSuccessStatusCode;
}
catch
{
cancelSuccess = false;
}
StateHasChanged();
// 🔥 AUTO RESET SAU 2 GIÂY
_ = Task.Run(async () =>
{
await Task.Delay(2000);
cancelSuccess = null;
await InvokeAsync(StateHasChanged);
});
}
async Task CopyJsonToClipboard()
{
_copyCts?.Cancel();
_copyCts = new();
await JS.InvokeVoidAsync("copyToClipboardFallback", OrderJson);
copied = true;
StateHasChanged();
try { await Task.Delay(1500, _copyCts.Token); } catch { }
copied = false;
StateHasChanged();
}
// ================= DIALOG =================
async Task OpenEditNodeDialog(Node node)
{
var parameters = new DialogParameters
{
{ x => x.Node, node }
};
var options = new DialogOptions
{
CloseButton = true,
FullWidth = true,
MaxWidth = MaxWidth.Large
};
var dialog = await DialogService.ShowAsync(
$"Edit Node: {node.NodeId}", parameters, options);
await dialog.Result;
OnOrderChanged(); // 🔥 cập nhật JSON sau dialog
}
}