save
This commit is contained in:
parent
f6f8a3bf65
commit
7e0c6af9d5
|
|
@ -1,4 +1,5 @@
|
|||
@page "/"
|
||||
@attribute [Authorize]
|
||||
@using RobotApp.Client.Services
|
||||
@using RobotApp.VDA5050.State
|
||||
@using MudBlazor
|
||||
|
|
|
|||
|
|
@ -117,14 +117,14 @@
|
|||
}
|
||||
|
||||
<!-- Apply Curve -->
|
||||
@if (edge.Radius > 0 && !edge.Expanded)
|
||||
@if (!edge.HasTrajectory && edge.Radius > 0 && !edge.Expanded)
|
||||
{
|
||||
<MudItem xs="12">
|
||||
<MudButton Color="Color.Primary"
|
||||
Variant="Variant.Outlined"
|
||||
StartIcon="@Icons.Material.Filled.Merge"
|
||||
OnClick="@(() => ApplyCurveAsync(edge))">
|
||||
Apply Curve
|
||||
Apply Curve (generate node)
|
||||
</MudButton>
|
||||
</MudItem>
|
||||
}
|
||||
|
|
|
|||
104
RobotApp.Client/Pages/Order/ImportOrderDialog.razor
Normal file
104
RobotApp.Client/Pages/Order/ImportOrderDialog.razor
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
@using System.Text.Json
|
||||
@using MudBlazor
|
||||
|
||||
<MudDialog>
|
||||
<TitleContent>
|
||||
<MudText Typo="Typo.h6">Import Order JSON</MudText>
|
||||
</TitleContent>
|
||||
|
||||
<DialogContent>
|
||||
|
||||
@if (ShowWarning)
|
||||
{
|
||||
<MudAlert Severity="Severity.Warning"
|
||||
Variant="Variant.Outlined"
|
||||
Class="mb-4">
|
||||
Only valid <b>VDA 5050 Order JSON</b> is accepted.<br />
|
||||
Invalid structure will be rejected.
|
||||
</MudAlert>
|
||||
}
|
||||
|
||||
<MudTextField @bind-Value="JsonText"
|
||||
Label="Paste Order JSON"
|
||||
Lines="20"
|
||||
Variant="Variant.Filled"
|
||||
Immediate
|
||||
Style="font-family: monospace" />
|
||||
|
||||
@if (!string.IsNullOrEmpty(ErrorMessage))
|
||||
{
|
||||
<MudAlert Severity="Severity.Error" Class="mt-3">
|
||||
@ErrorMessage
|
||||
</MudAlert>
|
||||
}
|
||||
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<MudButton OnClick="Cancel">Cancel</MudButton>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Primary"
|
||||
OnClick="ValidateAndImport">
|
||||
Import
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
@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."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,16 @@
|
|||
|
||||
<div class="d-flex gap-2">
|
||||
|
||||
<!-- ================= SEND BUTTON ================= -->
|
||||
<!-- IMPORT -->
|
||||
<MudButton Variant="Variant.Outlined"
|
||||
Color="Color.Secondary"
|
||||
Size="Size.Small"
|
||||
StartIcon="@Icons.Material.Filled.UploadFile"
|
||||
OnClick="OnImport">
|
||||
Import JSON
|
||||
</MudButton>
|
||||
|
||||
<!-- SEND -->
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="@SendButtonColor"
|
||||
StartIcon="@SendButtonIcon"
|
||||
|
|
@ -14,7 +23,7 @@
|
|||
@SendButtonText
|
||||
</MudButton>
|
||||
|
||||
<!-- ================= COPY ================= -->
|
||||
<!-- COPY -->
|
||||
<MudTooltip Text="@(Copied ? "Copied!" : "Copy to clipboard")">
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="@(Copied ? Color.Success : Color.Primary)"
|
||||
|
|
@ -27,15 +36,14 @@
|
|||
</div>
|
||||
</MudStack>
|
||||
|
||||
<!-- ================= JSON ================= -->
|
||||
<div class="flex-grow-1" style="overflow:auto;">
|
||||
<MudTextField Value="@OrderJson"
|
||||
ReadOnly="true"
|
||||
ReadOnly
|
||||
Variant="Variant.Filled"
|
||||
Lines="70"
|
||||
Class="h-100"
|
||||
Style="font-family: 'Roboto Mono', Consolas, 'Courier New', monospace;
|
||||
font-size: 0.875rem;
|
||||
Style="font-family: 'Roboto Mono', Consolas, monospace;
|
||||
font-size: 0.85rem;
|
||||
background:#1e1e1e;
|
||||
color:#d4d4d4;" />
|
||||
</div>
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -50,7 +50,8 @@
|
|||
Copied="@copied"
|
||||
SendSuccess="@sendSuccess"
|
||||
OnCopy="CopyJsonToClipboard"
|
||||
OnSend="SendOrderToServer" />
|
||||
OnSend="SendOrderToServer"
|
||||
OnImport="OpenImportDialog" />
|
||||
|
||||
</MudItem>
|
||||
|
||||
|
|
@ -85,6 +86,25 @@
|
|||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||
});
|
||||
}
|
||||
private async Task OpenImportDialog()
|
||||
{
|
||||
var dialog = await DialogService.ShowAsync<ImportOrderDialog>(
|
||||
"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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<double>();
|
||||
public List<Point> 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<VDA5050.InstantAction.Action>();
|
||||
|
||||
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<ActionParameter>()
|
||||
}
|
||||
).ToArray();
|
||||
}
|
||||
public object ToSchemaObject()
|
||||
{
|
||||
int seq = 0;
|
||||
|
|
@ -199,7 +328,36 @@ public class OrderMessage
|
|||
// ================= EDGES =================
|
||||
edges = Edges.Select<UiEdge, object>(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<object>()
|
||||
};
|
||||
}
|
||||
|
||||
// =================================================
|
||||
// 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<object>()
|
||||
};
|
||||
}
|
||||
|
||||
// ---------- ĐƯỜ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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user