445 lines
15 KiB
C#
445 lines
15 KiB
C#
using RobotApp.VDA5050.InstantAction;
|
||
using RobotApp.VDA5050.Order;
|
||
using System.Text.Json;
|
||
using System.Text.Json.Serialization;
|
||
|
||
namespace RobotApp.Client.Services;
|
||
|
||
// ======================================================
|
||
// EDGE UI
|
||
// ======================================================
|
||
public class UiEdge
|
||
{
|
||
public string EdgeId { get; set; } = "";
|
||
public int SequenceId { get; set; }
|
||
public bool Released { get; set; } = true;
|
||
|
||
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
|
||
{
|
||
I,
|
||
II,
|
||
III,
|
||
IV
|
||
}
|
||
|
||
// ======================================================
|
||
// GEOMETRY MODELS
|
||
// ======================================================
|
||
public record Point(double X, double Y);
|
||
|
||
public record QuarterResult(
|
||
Point EndPoint,
|
||
object Trajectory
|
||
);
|
||
|
||
// ======================================================
|
||
// GEOMETRY HELPER (QUARTER CIRCLE)
|
||
// ======================================================
|
||
public static class QuarterGeometry
|
||
{
|
||
private const double K = 0.5522847498307936;
|
||
|
||
public static QuarterResult BuildQuarterTrajectory(
|
||
Point A,
|
||
double r,
|
||
Quadrant q
|
||
)
|
||
{
|
||
Point P1, P2, C;
|
||
|
||
switch (q)
|
||
{
|
||
case Quadrant.I:
|
||
P1 = new(A.X, A.Y + K * r);
|
||
P2 = new(A.X + K * r, A.Y + r);
|
||
C = new(A.X + r, A.Y + r);
|
||
break;
|
||
|
||
case Quadrant.II:
|
||
P1 = new(A.X - K * r, A.Y);
|
||
P2 = new(A.X - r, A.Y + K * r);
|
||
C = new(A.X - r, A.Y + r);
|
||
break;
|
||
|
||
case Quadrant.III:
|
||
P1 = new(A.X, A.Y - K * r);
|
||
P2 = new(A.X - K * r, A.Y - r);
|
||
C = new(A.X - r, A.Y - r);
|
||
break;
|
||
|
||
case Quadrant.IV:
|
||
P1 = new(A.X + K * r, A.Y);
|
||
P2 = new(A.X + r, A.Y - K * r);
|
||
C = new(A.X + r, A.Y - r);
|
||
break;
|
||
|
||
default:
|
||
throw new ArgumentOutOfRangeException(nameof(q));
|
||
}
|
||
|
||
return new QuarterResult(
|
||
C,
|
||
new
|
||
{
|
||
degree = 3,
|
||
knotVector = new[] { 0, 0, 0, 0, 1, 1, 1, 1 },
|
||
controlPoints = new[]
|
||
{
|
||
new { x = A.X, y = A.Y }, // P0
|
||
new { x = P1.X, y = P1.Y }, // P1
|
||
new { x = P2.X, y = P2.Y }, // P2
|
||
new { x = C.X, y = C.Y } // P3
|
||
}
|
||
}
|
||
);
|
||
}
|
||
}
|
||
// ======================================================
|
||
// ORDER MESSAGE
|
||
// ======================================================
|
||
public class OrderMessage
|
||
{
|
||
public int HeaderId { get; set; }
|
||
public string Timestamp { get; set; } = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
|
||
public string Version { get; set; } = "2.1.0";
|
||
public string Manufacturer { get; set; } = "PhenikaaX";
|
||
public string SerialNumber { get; set; } = "T800-003";
|
||
public string OrderId { get; set; } = "";
|
||
public int OrderUpdateId { get; set; }
|
||
public string? ZoneSetId { get; set; }
|
||
|
||
public List<Node> Nodes { get; set; } = new();
|
||
public List<UiEdge> Edges { get; set; } = new();
|
||
public static Node CreateCurveNode(Node startNode, UiEdge edge)
|
||
{
|
||
var A = new Point(
|
||
startNode.NodePosition.X,
|
||
startNode.NodePosition.Y
|
||
);
|
||
|
||
var result = QuarterGeometry.BuildQuarterTrajectory(
|
||
A,
|
||
edge.Radius,
|
||
edge.Quadrant
|
||
);
|
||
|
||
return new Node
|
||
{
|
||
NodeId = $"NODE_C{Guid.NewGuid():N}".Substring(0, 12),
|
||
Released = true,
|
||
NodePosition = new NodePosition
|
||
{
|
||
X = result.EndPoint.X,
|
||
Y = result.EndPoint.Y,
|
||
Theta = startNode.NodePosition.Theta,
|
||
MapId = startNode.NodePosition.MapId
|
||
}
|
||
};
|
||
}
|
||
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 OrderMsg ToSchemaObject()
|
||
{
|
||
// ================= SORT NODES BY UI SEQUENCE =================
|
||
var orderedNodes = Nodes
|
||
.OrderBy(n => n.SequenceId)
|
||
.ToList();
|
||
|
||
// ================= BUILD NODE OBJECTS =================
|
||
var nodeObjects = orderedNodes
|
||
.Select((n, index) => new Node
|
||
{
|
||
NodeId = n.NodeId,
|
||
SequenceId = index * 2, // ✅ NODE = EVEN
|
||
Released = n.Released,
|
||
|
||
NodePosition = new NodePosition
|
||
{
|
||
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 VDA5050.InstantAction.Action
|
||
{
|
||
ActionId = a.ActionId,
|
||
ActionType = a.ActionType,
|
||
BlockingType = a.BlockingType,
|
||
|
||
ActionParameters = a.ActionParameters?
|
||
.Select(p => new ActionParameter
|
||
{
|
||
Key = p.Key,
|
||
Value = p.Value
|
||
})
|
||
.ToArray()
|
||
?? []
|
||
})
|
||
.ToArray()
|
||
?? []
|
||
})
|
||
.ToArray();
|
||
|
||
// ================= BUILD EDGE OBJECTS =================
|
||
Edge[] edgeObjects = Edges
|
||
.Select((e, index) =>
|
||
{
|
||
int sequenceId = index * 2 + 1; // ✅ EDGE = ODD
|
||
|
||
// ---------- BASE ----------
|
||
var baseEdge = new
|
||
{
|
||
edgeId = e.EdgeId,
|
||
sequenceId,
|
||
released = true,
|
||
startNodeId = e.StartNodeId,
|
||
endNodeId = e.EndNodeId
|
||
};
|
||
|
||
// =================================================
|
||
// 1️⃣ IMPORTED TRAJECTORY
|
||
// =================================================
|
||
if (e.HasTrajectory && e.Trajectory != null)
|
||
{
|
||
return new Edge
|
||
{
|
||
EdgeId = baseEdge.edgeId,
|
||
SequenceId = baseEdge.sequenceId,
|
||
Released= baseEdge.released,
|
||
StartNodeId= baseEdge.startNodeId,
|
||
EndNodeId= baseEdge.endNodeId,
|
||
|
||
Trajectory = new Trajectory
|
||
{
|
||
Degree = e.Trajectory.Degree,
|
||
KnotVector = e.Trajectory.KnotVector,
|
||
ControlPoints = e.Trajectory.ControlPoints
|
||
.Select(p => new ControlPoint { X = p.X, Y = p.Y , Weight = 1})
|
||
.ToArray()
|
||
},
|
||
|
||
Actions = []
|
||
};
|
||
}
|
||
|
||
// =================================================
|
||
// 2️⃣ STRAIGHT EDGE
|
||
// =================================================
|
||
if (e.Radius <= 0)
|
||
{
|
||
return new Edge
|
||
{
|
||
EdgeId = baseEdge.edgeId,
|
||
SequenceId = baseEdge.sequenceId,
|
||
Released = baseEdge.released,
|
||
StartNodeId = baseEdge.startNodeId,
|
||
EndNodeId = baseEdge.endNodeId,
|
||
|
||
Actions = []
|
||
};
|
||
}
|
||
|
||
// =================================================
|
||
// 3️⃣ GENERATED CURVE (EDITOR)
|
||
// =================================================
|
||
var startNode = orderedNodes.First(n => n.NodeId == e.StartNodeId);
|
||
|
||
var A = new Point(
|
||
startNode.NodePosition.X,
|
||
startNode.NodePosition.Y
|
||
);
|
||
|
||
var result = QuarterGeometry.BuildQuarterTrajectory(
|
||
A,
|
||
e.Radius,
|
||
e.Quadrant
|
||
);
|
||
|
||
return new Edge
|
||
{
|
||
EdgeId = baseEdge.edgeId,
|
||
SequenceId = baseEdge.sequenceId,
|
||
Released = baseEdge.released,
|
||
StartNodeId = baseEdge.startNodeId,
|
||
EndNodeId = baseEdge.endNodeId,
|
||
|
||
Actions = []
|
||
};
|
||
})
|
||
.ToArray();
|
||
|
||
// ================= FINAL SCHEMA OBJECT =================
|
||
return new OrderMsg
|
||
{
|
||
HeaderId = (uint)HeaderId++,
|
||
|
||
Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"),
|
||
|
||
Version = Version,
|
||
Manufacturer = Manufacturer,
|
||
SerialNumber = SerialNumber,
|
||
|
||
OrderId = OrderId= Guid.NewGuid().ToString(),
|
||
OrderUpdateId = OrderUpdateId,
|
||
|
||
ZoneSetId = string.IsNullOrWhiteSpace(ZoneSetId)
|
||
? null
|
||
: ZoneSetId,
|
||
|
||
Nodes = nodeObjects,
|
||
Edges = edgeObjects,
|
||
};
|
||
}
|
||
}
|
||
|
||
// ======================================================
|
||
// UI ACTION PARAM
|
||
// ======================================================
|
||
public class UiActionParameter : ActionParameter
|
||
{
|
||
[JsonIgnore]
|
||
public string ValueString
|
||
{
|
||
get => Value?.ToString() ?? "";
|
||
set => Value = value;
|
||
}
|
||
}
|