RobotApp/RobotApp.Client/Pages/Order/OrderMess.razor
2025-12-22 18:38:10 +07:00

317 lines
9.4 KiB
Plaintext

@page "/robot-order"
@attribute [Authorize]
@rendermode InteractiveWebAssemblyNoPrerender
@using System.Text.Json
@using System.Text.Json.Serialization
@inject IJSRuntime JS
@inject IDialogService DialogService
@inject ISnackbar Snackbar
@inject HttpClient Http
<MudMainContent Class="pa-0 ma-0">
<div style="height:100vh; overflow:hidden;">
<MudContainer MaxWidth="MaxWidth.False"
Class="pa-4"
Style="max-width:100%; height:100%; display:flex; flex-direction:column;">
<MudGrid Spacing="4" Class="flex-grow-1" Style="overflow:hidden;">
<!-- ================= LEFT ================= -->
<MudItem xs="12" md="7" Class="d-flex flex-column h-100" Style="gap:16px;">
<MudGrid Spacing="4" Class="flex-grow-1" Style="overflow:hidden;">
<MudItem xs="12" md="6" Class="h-100">
<NodesPanel Order="Order"
OnAddNode="AddNode"
OnRemoveNode="RemoveNode"
OnEditNode="OpenEditNodeDialog"
OnAddAction="AddAction"
OnRemoveAction="@(w => RemoveAction(w.Node, w.Action))"
OnAddActionParameter="AddActionParameter"
OnRemoveActionParameter="@(w => RemoveActionParameter(w.Action, w.Parameter))"
OnOrderChanged="OnOrderChanged" />
</MudItem>
<MudItem xs="12" md="6" Class="h-100">
<EdgesPanel Order="Order"
OnAddEdge="AddEdge"
OnRemoveEdge="RemoveEdge"
OnApplyCurve="ApplyCurve"
OnOrderChanged="OnOrderChanged" />
</MudItem>
</MudGrid>
</MudItem>
<!-- ================= RIGHT ================= -->
<MudItem xs="12" md="5" Class="h-100">
<JsonOutputPanel OrderJson="@OrderJson"
Copied="@copied"
SendSuccess="@sendSuccess"
OnCopy="CopyJsonToClipboard"
OnSend="SendOrderToServer"
OnImport="OpenImportDialog"
OnCancel="CancelOrder" />
</MudItem>
</MudGrid>
</MudContainer>
</div>
</MudMainContent>
@code {
// ================= STATE =================
private OrderMessage Order { get; set; } = new();
private string OrderJson = ""; // 🔥 CACHE JSON (QUAN TRỌNG)
private bool copied;
private bool? sendSuccess;
private CancellationTokenSource? _copyCts;
// ================= INIT =================
protected override void OnInitialized()
{
RebuildOrderJson();
}
// ================= CORE FIX =================
private void RebuildOrderJson()
{
OrderJson = JsonSerializer.Serialize(
Order.ToSchemaObject(),
new JsonSerializerOptions
{
WriteIndented = true,
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()
{
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<ActionParameter>()
});
node.Actions = list.ToArray();
}
void RemoveAction(Node node, VDA5050.InstantAction.Action action)
{
node.Actions = node.Actions?.Where(a => a != action).ToArray()
?? Array.Empty<VDA5050.InstantAction.Action>();
}
void AddActionParameter(VDA5050.InstantAction.Action act)
{
var list = (act.ActionParameters ?? Array.Empty<ActionParameter>()).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<ActionParameter>();
}
// ================= SEND / COPY =================
async Task SendOrderToServer()
{
// reset trạng thái trước khi gửi
sendSuccess = null;
StateHasChanged();
try
{
var response = await Http.PostAsJsonAsync(
"/api/order",
JsonSerializer.Deserialize<JsonElement>(OrderJson)
);
sendSuccess = response.IsSuccessStatusCode;
}
catch (Exception ex)
{
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()
{
try
{
var res = await Http.PostAsync("/api/order/cancel", null);
sendSuccess = null; // reset trạng thái SEND
copied = false;
if (res.IsSuccessStatusCode)
{
Snackbar.Add(
"⛔ Order đã huỷ",
Severity.Info);
}
else
{
Snackbar.Add(
"❌ Huỷ order thất bại",
Severity.Error);
}
}
catch (Exception ex)
{
Snackbar.Add(
$"❌ Lỗi: {ex.Message}",
Severity.Error);
}
}
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<EditNodeDialog>
{
{ x => x.Node, node }
};
var options = new DialogOptions
{
CloseButton = true,
FullWidth = true,
MaxWidth = MaxWidth.Large
};
var dialog = await DialogService.ShowAsync<EditNodeDialog>(
$"Edit Node: {node.NodeId}", parameters, options);
await dialog.Result;
OnOrderChanged(); // 🔥 cập nhật JSON sau dialog
}
}