@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.Canceled && result.Data is OrderMessage imported) { Order = 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 UiEdge { EdgeId = $"EDGE_{Order.Edges.Count + 1}", StartNodeId = start, EndNodeId = end }); } void RemoveEdge(UiEdge edge) { Order.Edges.Remove(edge); } void ApplyCurve(UiEdge edge) { if (edge.Radius <= 0 || edge.Expanded) return; var startNode = Order.Nodes.First(n => n.NodeId == edge.StartNodeId); var newNode = OrderMessage.CreateCurveNode(startNode, edge); Order.Nodes.Add(newNode); edge.EndNodeId = newNode.NodeId; edge.MarkExpanded(); // ✅ ResequenceNodes(); } // ================= 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() ?? Array.Empty(); } void AddActionParameter(VDA5050.InstantAction.Action act) { var list = (act.ActionParameters ?? Array.Empty()).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() ?? Array.Empty(); } // ================= 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("navigator.clipboard.writeText", 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 } }