@page "/" @using System.Text.Json @using System.Text.Json.Serialization @using RobotApp.VDA5050.InstantAction @using RobotApp.VDA5050.Order @using RobotApp.VDA5050.Type @using System.ComponentModel.DataAnnotations @attribute [Authorize] @rendermode InteractiveWebAssemblyNoPrerender @inject IJSRuntime JS @inject IDialogService DialogService
🧾 VDA5050 Order Editor 📍 Nodes Add Node
@foreach (var node in Order.Nodes) {
@node.NodeId
Actions @foreach (var act in node.Actions) { @foreach (var at in Enum.GetValues()) { @at } NONE SOFT HARD Action Parameters @foreach (var p in act.ActionParameters.Cast()) { } Add Parameter Remove Action } Add Action
}
🔗 Edges Add Edge
@foreach (var edge in Order.Edges) { @foreach (var node in Order.Nodes) { @node.NodeId } @foreach (var node in Order.Nodes) { @node.NodeId } Remove Edge }
📄 JSON Output (/order) Send @if (!string.IsNullOrEmpty(sendResult)) { @sendResult } @if (copied) { Copied! } else { Copy }
@code { [Inject] HttpClient Http { get; set; } = default!; bool sending; string? sendResult; bool IsEditNodeOpen; Node? EditingNode; OrderMessage Order = new(); async Task SendOrderToServer() { if (sending) return; sending = true; sendResult = null; StateHasChanged(); try { var response = await Http.PostAsJsonAsync( "/api/order", JsonSerializer.Deserialize(OrderJson) ); if (response.IsSuccessStatusCode) { sendResult = "✅ Done!"; } else { sendResult = $"❌ Failed: {response.StatusCode}"; } } catch (Exception ex) { sendResult = $"❌ Error: {ex.Message}"; } finally { sending = false; StateHasChanged(); } } void AddNode() { Order.Nodes.Add(new Node { NodeId = $"NODE_{Order.Nodes.Count + 1}", SequenceId = Order.Nodes.Count, Released = true, NodePosition = new NodePosition() }); } void RemoveNode(Node node) { Order.Nodes.Remove(node); Order.Edges.RemoveAll(e => e.StartNodeId == node.NodeId || e.EndNodeId == node.NodeId); for (int i = 0; i < Order.Nodes.Count; i++) Order.Nodes[i].SequenceId = i; } void RemoveAction(Node node, VDA5050.InstantAction.Action act) { node.Actions = node.Actions .Where(a => a != act) .ToArray(); } void AddAction(Node node) { node.Actions = node.Actions .Append(new VDA5050.InstantAction.Action { ActionId = Guid.NewGuid().ToString(), ActionType = ActionType.startPause.ToString(), BlockingType = "NONE", ActionParameters = Array.Empty() }) .ToArray(); } private void RemoveActionParameter(VDA5050.InstantAction.Action act, ActionParameter param) { if (act.ActionParameters == null) return; act.ActionParameters = act.ActionParameters .Where(p => p != param) .ToArray(); } void AddActionParameter(VDA5050.InstantAction.Action act) { var newList = (act.ActionParameters ?? Array.Empty()).ToList(); newList.Add(new UiActionParameter()); act.ActionParameters = newList.ToArray(); } void AddEdge() { if (Order.Nodes.Count < 2) return; Order.Edges.Add(new VDA5050.Order.Edge { EdgeId = $"EDGE_{Order.Edges.Count + 1}", StartNodeId = Order.Nodes[^2].NodeId, EndNodeId = Order.Nodes[^1].NodeId }); } bool copied = false; CancellationTokenSource? _copyCts; async Task CopyJsonToClipboard() { _copyCts?.Cancel(); _copyCts = new(); var success = await JS.InvokeAsync( "copyText", OrderJson ); if (!success) return; copied = true; StateHasChanged(); try { await Task.Delay(1500, _copyCts.Token); } catch { } copied = false; StateHasChanged(); } void RemoveEdge(VDA5050.Order.Edge edge) { Order.Edges.Remove(edge); } string OrderJson => JsonSerializer.Serialize( Order.ToSchemaObject(), new JsonSerializerOptions { WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }); public class UiActionParameter : ActionParameter { [JsonIgnore] public string ValueString { get => Value?.ToString() ?? ""; set => Value = value; } } public class OrderMessage { public int HeaderId { get; set; } public string Timestamp { get; set; } = ""; public string Version { get; set; } = "v1"; public string Manufacturer { get; set; } = "PNKX"; public string SerialNumber { get; set; } = "AMR-01"; public string OrderId { get; set; } = Guid.NewGuid().ToString(); public int OrderUpdateId { get; set; } public string? ZoneSetId { get; set; } public List Nodes { get; set; } = new(); public List Edges { get; set; } = new(); public object ToSchemaObject() { int seq = 0; return new { headerId = HeaderId++, timestamp = string.IsNullOrWhiteSpace(Timestamp) ? DateTime.UtcNow.ToString("O") : Timestamp, version = Version, manufacturer = Manufacturer, serialNumber = SerialNumber, orderId = OrderId, orderUpdateId = OrderUpdateId, zoneSetId = string.IsNullOrWhiteSpace(ZoneSetId) ? null : ZoneSetId, // ================= NODES ================= nodes = Nodes .Select(n => new { nodeId = n.NodeId, sequenceId = seq++, released = n.Released, nodePosition = new { x = n.NodePosition.X, y = n.NodePosition.Y, theta = n.NodePosition.Theta, allowedDeviationXY = n.NodePosition.AllowedDeviationXY, allowedDeviationTheta = n.NodePosition.AllowedDeviationTheta, mapId = string.IsNullOrWhiteSpace(n.NodePosition.MapId) ? "MAP_01" : n.NodePosition.MapId }, actions = n.Actions .Select(a => new { actionId = a.ActionId, actionType = a.ActionType, blockingType = a.BlockingType, actionParameters = a.ActionParameters != null ? a.ActionParameters .Select(p => new { key = p.Key, value = p.Value }) .ToArray() : Array.Empty() }) .ToArray() }) .ToArray(), // ✅ QUAN TRỌNG // ================= EDGES ================= edges = Edges .Select(e => new { edgeId = e.EdgeId, sequenceId = seq++, released = true, startNodeId = e.StartNodeId, endNodeId = e.EndNodeId, actions = Array.Empty() }) .ToArray() // ✅ QUAN TRỌNG }; } } private async Task OpenEditNodeDialog(Node node) { var parameters = new DialogParameters { { x => x.Node, node } // Truyền trực tiếp reference gốc }; var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Large, FullWidth = true, CloseOnEscapeKey = true }; var dialog = await DialogService.ShowAsync($"Edit Node: {node.NodeId}", parameters, options); await dialog.Result; // Đợi dialog đóng StateHasChanged(); } }