297 lines
9.0 KiB
C#
297 lines
9.0 KiB
C#
using RobotApp.VDA5050.InstantAction;
|
||
using RobotApp.VDA5050.Order;
|
||
using System.Text.Json;
|
||
using System.Text.Json.Serialization;
|
||
using System.Xml.Linq;
|
||
|
||
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 uint 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; } = [];
|
||
public List<Edge> Edges { get; set; } = [];
|
||
|
||
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.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 = []
|
||
};
|
||
}
|
||
|
||
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,
|
||
};
|
||
}
|
||
|
||
public void Import(OrderMsg order)
|
||
{
|
||
HeaderId = order.HeaderId;
|
||
Timestamp = order.Timestamp;
|
||
Version = order.Version;
|
||
Manufacturer = order.Manufacturer;
|
||
SerialNumber = order.SerialNumber;
|
||
OrderId = order.OrderId;
|
||
ZoneSetId = order.ZoneSetId;
|
||
OrderUpdateId = order.OrderUpdateId;
|
||
Nodes = [.. order.Nodes];
|
||
Edges = [.. order.Edges];
|
||
}
|
||
}
|
||
|
||
// ======================================================
|
||
// UI ACTION PARAM
|
||
// ======================================================
|
||
public class UiActionParameter : ActionParameter
|
||
{
|
||
[JsonIgnore]
|
||
public string ValueString
|
||
{
|
||
get => Value?.ToString() ?? "";
|
||
set => Value = value;
|
||
}
|
||
}
|