From 7e0c6af9d5fdcac3bcb6d553fd82911ba4b29a12 Mon Sep 17 00:00:00 2001 From: sonlt Date: Mon, 22 Dec 2025 09:49:02 +0700 Subject: [PATCH] save --- RobotApp.Client/Pages/Home.razor | 1 + RobotApp.Client/Pages/Order/EdgesPanel.razor | 4 +- .../Pages/Order/ImportOrderDialog.razor | 104 ++++++++++ .../Pages/Order/JsonOutputPanel.razor | 23 ++- RobotApp.Client/Pages/Order/OrderMess.razor | 24 ++- RobotApp.Client/Services/UiEdge.cs | 177 +++++++++++++++++- 6 files changed, 315 insertions(+), 18 deletions(-) create mode 100644 RobotApp.Client/Pages/Order/ImportOrderDialog.razor diff --git a/RobotApp.Client/Pages/Home.razor b/RobotApp.Client/Pages/Home.razor index e5a2b14..326a868 100644 --- a/RobotApp.Client/Pages/Home.razor +++ b/RobotApp.Client/Pages/Home.razor @@ -1,4 +1,5 @@ @page "/" +@attribute [Authorize] @using RobotApp.Client.Services @using RobotApp.VDA5050.State @using MudBlazor diff --git a/RobotApp.Client/Pages/Order/EdgesPanel.razor b/RobotApp.Client/Pages/Order/EdgesPanel.razor index 711fa93..dced021 100644 --- a/RobotApp.Client/Pages/Order/EdgesPanel.razor +++ b/RobotApp.Client/Pages/Order/EdgesPanel.razor @@ -117,14 +117,14 @@ } - @if (edge.Radius > 0 && !edge.Expanded) + @if (!edge.HasTrajectory && edge.Radius > 0 && !edge.Expanded) { - Apply Curve + Apply Curve (generate node) } diff --git a/RobotApp.Client/Pages/Order/ImportOrderDialog.razor b/RobotApp.Client/Pages/Order/ImportOrderDialog.razor new file mode 100644 index 0000000..42258d6 --- /dev/null +++ b/RobotApp.Client/Pages/Order/ImportOrderDialog.razor @@ -0,0 +1,104 @@ +@using System.Text.Json +@using MudBlazor + + + + Import Order JSON + + + + + @if (ShowWarning) + { + + Only valid VDA 5050 Order JSON is accepted.
+ Invalid structure will be rejected. +
+ } + + + + @if (!string.IsNullOrEmpty(ErrorMessage)) + { + + @ErrorMessage + + } + +
+ + + Cancel + + Import + + +
+@code { + [CascadingParameter] public IMudDialogInstance MudDialog { get; set; } = default!; + + public string JsonText { get; set; } = ""; + public string? ErrorMessage; + public bool ShowWarning { get; set; } = false; + + private void Cancel() => MudDialog.Cancel(); + + private void ValidateAndImport() + { + ErrorMessage = null; + ShowWarning = false; + + try + { + using var doc = JsonDocument.Parse(JsonText); + var root = doc.RootElement; + + // ===== BASIC STRUCTURE CHECK ===== + if (!root.TryGetProperty("nodes", out _) || + !root.TryGetProperty("edges", out _)) + throw new Exception("Missing 'nodes' or 'edges' field."); + + var order = OrderMessage.FromSchemaObject(root); + + ValidateOrder(order); + + MudDialog.Close(DialogResult.Ok(order)); + } + catch (JsonException) + { + ShowWarning = true; + ErrorMessage = "Invalid JSON format."; + } + catch (Exception ex) + { + ShowWarning = true; + ErrorMessage = ex.Message; + } + } + + private void ValidateOrder(OrderMessage order) + { + if (order.Nodes.Count == 0) + throw new Exception("Order must contain at least one node."); + + var nodeIds = order.Nodes.Select(n => n.NodeId).ToHashSet(); + + foreach (var e in order.Edges) + { + if (!nodeIds.Contains(e.StartNodeId) || + !nodeIds.Contains(e.EndNodeId)) + throw new Exception( + $"Edge '{e.EdgeId}' references unknown node." + ); + } + } +} diff --git a/RobotApp.Client/Pages/Order/JsonOutputPanel.razor b/RobotApp.Client/Pages/Order/JsonOutputPanel.razor index 5cb2ccd..513e27c 100644 --- a/RobotApp.Client/Pages/Order/JsonOutputPanel.razor +++ b/RobotApp.Client/Pages/Order/JsonOutputPanel.razor @@ -5,7 +5,16 @@
- + + + Import JSON + + + - + -
@@ -45,10 +53,11 @@ [Parameter] public string OrderJson { get; set; } = ""; [Parameter] public bool Copied { get; set; } [Parameter] public bool? SendSuccess { get; set; } + [Parameter] public EventCallback OnCopy { get; set; } [Parameter] public EventCallback OnSend { get; set; } + [Parameter] public EventCallback OnImport { get; set; } - // ================= SEND BUTTON STATE ================= private string SendButtonText => SendSuccess switch { diff --git a/RobotApp.Client/Pages/Order/OrderMess.razor b/RobotApp.Client/Pages/Order/OrderMess.razor index 93a3b15..84f5eb5 100644 --- a/RobotApp.Client/Pages/Order/OrderMess.razor +++ b/RobotApp.Client/Pages/Order/OrderMess.razor @@ -50,7 +50,8 @@ Copied="@copied" SendSuccess="@sendSuccess" OnCopy="CopyJsonToClipboard" - OnSend="SendOrderToServer" /> + OnSend="SendOrderToServer" + OnImport="OpenImportDialog" /> @@ -85,6 +86,25 @@ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }); } + 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() { @@ -151,7 +171,7 @@ Order.Nodes.Add(newNode); edge.EndNodeId = newNode.NodeId; - edge.Expanded = true; + edge.MarkExpanded(); // ✅ ResequenceNodes(); } diff --git a/RobotApp.Client/Services/UiEdge.cs b/RobotApp.Client/Services/UiEdge.cs index 3c92d67..061fe7a 100644 --- a/RobotApp.Client/Services/UiEdge.cs +++ b/RobotApp.Client/Services/UiEdge.cs @@ -1,5 +1,6 @@ using RobotApp.VDA5050.InstantAction; using RobotApp.VDA5050.Order; +using System.Text.Json; using System.Text.Json.Serialization; namespace RobotApp.Client.Services; @@ -7,13 +8,37 @@ namespace RobotApp.Client.Services; // ====================================================== // EDGE UI // ====================================================== -public class UiEdge : VDA5050.Order.Edge +public class UiEdge { - public double Radius { get; set; } = 0.0; - public Quadrant Quadrant { get; set; } = Quadrant.I; + public string EdgeId { get; set; } = ""; + public int SequenceId { get; set; } + public bool Released { get; set; } = true; - // Đánh dấu đã expand chưa - public bool Expanded { get; set; } = false; + public string StartNodeId { get; set; } = ""; + public string EndNodeId { get; set; } = ""; + + // ===== CURVE (EDITOR GENERATED) ===== + public double Radius { get; set; } = 0; + public Quadrant Quadrant { get; set; } + + // ===== IMPORTED TRAJECTORY ===== + public bool HasTrajectory { get; set; } = false; + public UiTrajectory? Trajectory { get; set; } + + // ===== UI STATE ===== + public bool Expanded { get; private set; } = false; + + public void MarkExpanded() + { + Expanded = true; + } +} + +public class UiTrajectory +{ + public int Degree { get; set; } + public double[] KnotVector { get; set; } = Array.Empty(); + public List ControlPoints { get; set; } = new(); } public enum Quadrant @@ -138,7 +163,111 @@ public class OrderMessage } }; } + public static OrderMessage FromSchemaObject(JsonElement root) + { + var order = new OrderMessage + { + HeaderId = root.GetProperty("headerId").GetInt32(), + Timestamp = root.GetProperty("timestamp").GetString(), + Version = root.GetProperty("version").GetString(), + Manufacturer = root.GetProperty("manufacturer").GetString(), + SerialNumber = root.GetProperty("serialNumber").GetString(), + OrderId = root.GetProperty("orderId").GetString(), + OrderUpdateId = root.GetProperty("orderUpdateId").GetInt32() + }; + // ================= NODES ================= + foreach (var n in root.GetProperty("nodes").EnumerateArray()) + { + var node = new Node + { + NodeId = n.GetProperty("nodeId").GetString()!, + SequenceId = n.GetProperty("sequenceId").GetInt32(), + Released = n.GetProperty("released").GetBoolean(), + + NodePosition = new NodePosition + { + X = n.GetProperty("nodePosition").GetProperty("x").GetDouble(), + Y = n.GetProperty("nodePosition").GetProperty("y").GetDouble(), + Theta = n.GetProperty("nodePosition").GetProperty("theta").GetDouble(), + AllowedDeviationXY = n.GetProperty("nodePosition").GetProperty("allowedDeviationXY").GetDouble(), + AllowedDeviationTheta = n.GetProperty("nodePosition").GetProperty("allowedDeviationTheta").GetDouble(), + MapId = n.GetProperty("nodePosition").GetProperty("mapId").GetString() + }, + + Actions = ParseActions(n) + }; + + order.Nodes.Add(node); + } + + foreach (var e in root.GetProperty("edges").EnumerateArray()) + { + var edge = new UiEdge + { + EdgeId = e.GetProperty("edgeId").GetString()!, + SequenceId = e.GetProperty("sequenceId").GetInt32(), + Released = e.GetProperty("released").GetBoolean(), + StartNodeId = e.GetProperty("startNodeId").GetString()!, + EndNodeId = e.GetProperty("endNodeId").GetString()!, + }; + + // ===== IMPORT TRAJECTORY ===== + if (e.TryGetProperty("trajectory", out var traj)) + { + edge.HasTrajectory = true; + edge.Trajectory = new UiTrajectory + { + Degree = traj.GetProperty("degree").GetInt32(), + KnotVector = traj.GetProperty("knotVector") + .EnumerateArray() + .Select(x => x.GetDouble()) + .ToArray(), + + ControlPoints = traj.GetProperty("controlPoints") + .EnumerateArray() + .Select(p => new Point( + p.GetProperty("x").GetDouble(), + p.GetProperty("y").GetDouble() + )) + .ToList() + }; + + // 🔥 IMPORTED CURVE → LOCK APPLY + edge.MarkExpanded(); + } + + order.Edges.Add(edge); + } + + return order; + } + + // ================= ACTION PARSER ================= + private static VDA5050.InstantAction.Action[] ParseActions(JsonElement parent) + { + if (!parent.TryGetProperty("actions", out var acts)) + return Array.Empty(); + + return acts.EnumerateArray().Select(a => + new VDA5050.InstantAction.Action + { + ActionId = a.GetProperty("actionId").GetString(), + ActionType = a.GetProperty("actionType").GetString(), + BlockingType = a.GetProperty("blockingType").GetString(), + + ActionParameters = a.TryGetProperty("actionParameters", out var ps) + ? ps.EnumerateArray() + .Select(p => new ActionParameter + { + Key = p.GetProperty("key").GetString(), + Value = p.GetProperty("value").GetString() + }) + .ToArray() + : Array.Empty() + } + ).ToArray(); + } public object ToSchemaObject() { int seq = 0; @@ -199,7 +328,36 @@ public class OrderMessage // ================= EDGES ================= edges = Edges.Select(e => { - // ---------- ĐƯỜNG THẲNG ---------- + // ================================================= + // 1️⃣ IMPORTED TRAJECTORY (ƯU TIÊN CAO NHẤT) + // ================================================= + if (e.HasTrajectory && e.Trajectory != null) + { + return new + { + edgeId = e.EdgeId, + sequenceId = seq++, + released = true, + + startNodeId = e.StartNodeId, + endNodeId = e.EndNodeId, + + trajectory = new + { + degree = e.Trajectory.Degree, + knotVector = e.Trajectory.KnotVector, + controlPoints = e.Trajectory.ControlPoints + .Select(p => new { x = p.X, y = p.Y }) + .ToArray() + }, + + actions = Array.Empty() + }; + } + + // ================================================= + // 2️⃣ STRAIGHT EDGE (KHÔNG CÓ CURVE) + // ================================================= if (e.Radius <= 0) { return new @@ -207,14 +365,19 @@ public class OrderMessage edgeId = e.EdgeId, sequenceId = seq++, released = true, + startNodeId = e.StartNodeId, endNodeId = e.EndNodeId, + actions = Array.Empty() }; } - // ---------- ĐƯỜNG CONG 1/4 ---------- + // ================================================= + // 3️⃣ EDITOR GENERATED CURVE (RADIUS + QUADRANT) + // ================================================= var startNode = Nodes.First(n => n.NodeId == e.StartNodeId); + var A = new Point( startNode.NodePosition.X, startNode.NodePosition.Y