Initial commit
This commit is contained in:
68
srcs/RobotNet10/Commons/RobotNet10.MapManager/Data/Edge.cs
Normal file
68
srcs/RobotNet10/Commons/RobotNet10.MapManager/Data/Edge.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RobotNet10.MapManager.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Edge entity - Paths between nodes
|
||||
/// Maps to VDMA LIF: layouts[].edges[]
|
||||
/// </summary>
|
||||
[Table("Edges")]
|
||||
public class Edge
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parent level reference
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid LevelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Edge identifier (VDMA LIF: edgeId) - Required
|
||||
/// Unique within level
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(128)]
|
||||
public string EdgeId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Start node reference (VDMA LIF: startNodeId) - Required
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid StartNodeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// End node reference (VDMA LIF: endNodeId) - Required
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid EndNodeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Edge name - Extension field (NOT in VDMA LIF)
|
||||
/// For UI/display purposes only
|
||||
/// </summary>
|
||||
[StringLength(256)]
|
||||
public string? EdgeName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Edge description - Extension field (NOT in VDMA LIF)
|
||||
/// For UI/display purposes only
|
||||
/// </summary>
|
||||
public string? EdgeDescription { get; set; }
|
||||
|
||||
// Navigation properties
|
||||
[ForeignKey(nameof(LevelId))]
|
||||
public virtual LayoutLevel Level { get; set; } = null!;
|
||||
|
||||
[ForeignKey(nameof(StartNodeId))]
|
||||
public virtual Node StartNode { get; set; } = null!;
|
||||
|
||||
[ForeignKey(nameof(EndNodeId))]
|
||||
public virtual Node EndNode { get; set; } = null!;
|
||||
|
||||
public virtual ICollection<EdgeVehicleProperty> VehicleProperties { get; set; } = new List<EdgeVehicleProperty>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using RobotNet.VDA5050.Type;
|
||||
|
||||
namespace RobotNet10.MapManager.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Edge vehicle property - Vehicle-specific properties for edges
|
||||
/// Maps to VDMA LIF: edge.vehicleTypeEdgeProperties[]
|
||||
/// </summary>
|
||||
[Table("EdgeVehicleProperties")]
|
||||
public class EdgeVehicleProperty
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Edge reference
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid EdgeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Vehicle type reference
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid VehicleTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Vehicle orientation while traversing edge (VDMA LIF: vehicleOrientation) - Optional
|
||||
/// In degrees, range: [0.0 ... 360.0]
|
||||
/// </summary>
|
||||
public double? VehicleOrientation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of orientation (VDMA LIF: orientationType) - Optional
|
||||
/// Values: GLOBAL, TANGENTIAL
|
||||
/// </summary>
|
||||
public OrientationType? OrientationType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is rotation allowed while on edge (VDMA LIF: rotationAllowed) - Optional
|
||||
/// </summary>
|
||||
public bool? RotationAllowed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Rotation allowed at start node (VDMA LIF: rotationAtStartNodeAllowed) - Optional
|
||||
/// Values: NONE, CCW, CW, BOTH
|
||||
/// </summary>
|
||||
public RotationDirection? RotationAtStartNodeAllowed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Rotation allowed at end node (VDMA LIF: rotationAtEndNodeAllowed) - Optional
|
||||
/// Values: NONE, CCW, CW, BOTH
|
||||
/// </summary>
|
||||
public RotationDirection? RotationAtEndNodeAllowed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum speed allowed on edge (VDMA LIF: maxSpeed) - Optional
|
||||
/// In meters per second, range: [0.0 ... double.MaxValue]
|
||||
/// </summary>
|
||||
public double? MaxSpeed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum rotation speed allowed (VDMA LIF: maxRotationSpeed) - Optional
|
||||
/// In radians per second, range: [0.0 ... double.MaxValue]
|
||||
/// </summary>
|
||||
public double? MaxRotationSpeed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum height of vehicle on edge (VDMA LIF: minHeight) - Optional
|
||||
/// In meters, range: [0.0 ... double.MaxValue]
|
||||
/// </summary>
|
||||
public double? MinHeight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum height of vehicle on edge (VDMA LIF: maxHeight) - Optional
|
||||
/// In meters, range: [0.0 ... double.MaxValue]
|
||||
/// </summary>
|
||||
public double? MaxHeight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Can edge be traversed without load (VDMA LIF: loadRestriction.unloaded) - Optional
|
||||
/// </summary>
|
||||
public bool? LoadRestriction_Unloaded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Can edge be traversed with load (VDMA LIF: loadRestriction.loaded) - Optional
|
||||
/// </summary>
|
||||
public bool? LoadRestriction_Loaded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Load set names allowed on edge (VDMA LIF: loadRestriction.loadSetNames) - Optional
|
||||
/// JSON array of strings: ["pallet", "box"]
|
||||
/// </summary>
|
||||
public string? LoadRestriction_LoadSetNames { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Trajectory degree (NURBS curve degree) - Optional
|
||||
/// Values: 1 (linear), 2 (quadratic), 3 (cubic)
|
||||
/// </summary>
|
||||
public int? TrajectoryDegree { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Trajectory control point 1 X coordinate (meters) - Optional
|
||||
/// Used for degree 2 and 3 curves
|
||||
/// </summary>
|
||||
public double? TrajectoryControlPoint1X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Trajectory control point 1 Y coordinate (meters) - Optional
|
||||
/// Used for degree 2 and 3 curves
|
||||
/// </summary>
|
||||
public double? TrajectoryControlPoint1Y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Trajectory control point 2 X coordinate (meters) - Optional
|
||||
/// Used for degree 3 curves only
|
||||
/// </summary>
|
||||
public double? TrajectoryControlPoint2X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Trajectory control point 2 Y coordinate (meters) - Optional
|
||||
/// Used for degree 3 curves only
|
||||
/// </summary>
|
||||
public double? TrajectoryControlPoint2Y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Actions that vehicle can perform at this edge (VDMA LIF: actions) - Optional
|
||||
/// JSON array format:
|
||||
/// [
|
||||
/// {
|
||||
/// "actionType": "pick",
|
||||
/// "actionDescription": "...",
|
||||
/// "requirementType": "REQUIRED",
|
||||
/// "blockingType": "HARD",
|
||||
/// "actionParameters": [{"key": "...", "value": "..."}]
|
||||
/// }
|
||||
/// ]
|
||||
/// </summary>
|
||||
public string? Actions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Corridor left width (meters) - Optional
|
||||
/// Defines the width of the corridor to the left related to the trajectory
|
||||
/// </summary>
|
||||
public double? CorridorLeftWidth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Corridor right width (meters) - Optional
|
||||
/// Defines the width of the corridor to the right related to the trajectory
|
||||
/// </summary>
|
||||
public double? CorridorRightWidth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Corridor reference point (VDA5050: corridorRefPoint) - Optional
|
||||
/// Defines whether the boundaries are valid for the kinematic center or the contour of the vehicle
|
||||
/// Values: KINEMATICCENTER, CONTOUR
|
||||
/// </summary>
|
||||
public CorridorRefPoint? CorridorRefPoint { get; set; }
|
||||
|
||||
// Navigation properties
|
||||
[ForeignKey(nameof(EdgeId))]
|
||||
public virtual Edge Edge { get; set; } = null!;
|
||||
|
||||
[ForeignKey(nameof(VehicleTypeId))]
|
||||
public virtual VehicleType VehicleType { get; set; } = null!;
|
||||
}
|
||||
|
||||
69
srcs/RobotNet10/Commons/RobotNet10.MapManager/Data/Layout.cs
Normal file
69
srcs/RobotNet10/Commons/RobotNet10.MapManager/Data/Layout.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RobotNet10.MapManager.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Layout entity - Root level representing a Building/Facility
|
||||
/// Maps to VDMA LIF: layouts[].layoutId
|
||||
/// </summary>
|
||||
[Table("Layouts")]
|
||||
public class Layout
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unique layout identifier (VDMA LIF: layoutId)
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(128)]
|
||||
public string LayoutId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Layout name (VDMA LIF: layoutName)
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(256)]
|
||||
public string LayoutName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Description
|
||||
/// </summary>
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is layout currently active/operational
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Created date
|
||||
/// </summary>
|
||||
[Required]
|
||||
public DateTime CreatedDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Last modified date
|
||||
/// </summary>
|
||||
[Required]
|
||||
public DateTime ModifiedDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Creator
|
||||
/// </summary>
|
||||
[StringLength(256)]
|
||||
public string? CreatedBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Last modifier
|
||||
/// </summary>
|
||||
[StringLength(256)]
|
||||
public string? ModifiedBy { get; set; }
|
||||
|
||||
// Navigation properties
|
||||
public virtual ICollection<LayoutVersion> Versions { get; set; } = new List<LayoutVersion>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RobotNet10.MapManager.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Layout level entity - Represents floors/levels within a version
|
||||
/// Maps to VDMA LIF: layouts[].layoutLevelId
|
||||
/// </summary>
|
||||
[Table("LayoutLevels")]
|
||||
public class LayoutLevel
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parent version reference
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid VersionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Level identifier (VDMA LIF: layoutLevelId)
|
||||
/// Example: "floor_1", "floor_2", "basement"
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(64)]
|
||||
public string LayoutLevelId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Display order (for sorting in UI)
|
||||
/// Example: Basement=-1, Floor1=0, Floor2=1
|
||||
/// </summary>
|
||||
[Required]
|
||||
public int LevelOrder { get; set; } = 0;
|
||||
|
||||
// Navigation properties
|
||||
[ForeignKey(nameof(VersionId))]
|
||||
public virtual LayoutVersion Version { get; set; } = null!;
|
||||
|
||||
public virtual ICollection<Node> Nodes { get; set; } = new List<Node>();
|
||||
public virtual ICollection<Edge> Edges { get; set; } = new List<Edge>();
|
||||
public virtual ICollection<Station> Stations { get; set; } = new List<Station>();
|
||||
|
||||
/// <summary>
|
||||
/// Editor-specific settings (UI extensions, not part of VDMA LIF)
|
||||
/// </summary>
|
||||
public virtual LayoutLevelEditorSettings? EditorSettings { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RobotNet10.MapManager.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Editor-specific settings for each layout level
|
||||
/// NOT part of VDMA LIF standard - UI/Editor extensions only
|
||||
/// </summary>
|
||||
[Table("LayoutLevelEditorSettings")]
|
||||
public class LayoutLevelEditorSettings
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parent layout level reference (1-to-1 relationship)
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid LevelId { get; set; }
|
||||
|
||||
// ==========================================
|
||||
// EDGE SETTINGS
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// Minimum edge length (in METERS) required to create an edge
|
||||
/// Prevents accidental creation of very short edges
|
||||
/// Default: 0.1 meters (10 cm)
|
||||
/// </summary>
|
||||
[Required]
|
||||
public double EdgeMinLengthCreate { get; set; } = 0.5;
|
||||
|
||||
/// <summary>
|
||||
/// Auto-generate edge names when creating new edges
|
||||
/// Uses 8-character GUID: "Edge_a7f2e3b1"
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool EdgeNameAutoGenerate { get; set; } = false;
|
||||
|
||||
// ==========================================
|
||||
// NODE SETTINGS
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// Auto-generate node names when creating new nodes
|
||||
/// Uses 8-character GUID: "Node_a7f2e3b1"
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool NodeNameAutoGenerate { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Node proximity radius (in METERS) for edge creation
|
||||
/// When creating an edge, if start/end point is within this radius of an existing node,
|
||||
/// the edge will connect to that existing node instead of creating a new one
|
||||
/// Default: 0.35 meters (35 cm)
|
||||
/// </summary>
|
||||
[Required]
|
||||
public double NodeProximityRadius { get; set; } = 0.35;
|
||||
|
||||
// ==========================================
|
||||
// COORDINATE SYSTEM SETTINGS
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// World coordinate origin X in METERS
|
||||
/// Defines where the world coordinate system (0, 0) is located
|
||||
/// Default: 0.0
|
||||
/// </summary>
|
||||
[Required]
|
||||
public double OriginX { get; set; } = 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// World coordinate origin Y in METERS
|
||||
/// Defines where the world coordinate system (0, 0) is located
|
||||
/// Default: 0.0
|
||||
/// </summary>
|
||||
[Required]
|
||||
public double OriginY { get; set; } = 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// Resolution: Meters per pixel
|
||||
/// Conversion factor between world coordinates (meters) and image coordinates (pixels)
|
||||
/// Example: 0.05 means 1 pixel = 5 cm in real world
|
||||
/// Default: 0.05 (5 cm per pixel)
|
||||
/// </summary>
|
||||
[Required]
|
||||
public double Resolution { get; set; } = 0.05;
|
||||
|
||||
// ==========================================
|
||||
// COORDINATE BOUNDS (World Coordinates)
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// Minimum X boundary in METERS (world coordinates)
|
||||
/// Defines the leftmost valid coordinate for this level
|
||||
/// Optional: null means no limit
|
||||
/// </summary>
|
||||
public double? BoundsMinX { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum X boundary in METERS (world coordinates)
|
||||
/// Defines the rightmost valid coordinate for this level
|
||||
/// Optional: null means no limit
|
||||
/// </summary>
|
||||
public double? BoundsMaxX { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum Y boundary in METERS (world coordinates)
|
||||
/// Defines the bottom valid coordinate for this level
|
||||
/// Optional: null means no limit
|
||||
/// </summary>
|
||||
public double? BoundsMinY { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum Y boundary in METERS (world coordinates)
|
||||
/// Defines the top valid coordinate for this level
|
||||
/// Optional: null means no limit
|
||||
/// </summary>
|
||||
public double? BoundsMaxY { get; set; }
|
||||
|
||||
// ==========================================
|
||||
// BACKGROUND IMAGE SETTINGS
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// Background image width in PIXELS
|
||||
/// Represents the actual pixel dimensions of the image file
|
||||
/// Used for rendering and coordinate conversion
|
||||
/// </summary>
|
||||
public double? ImageWidth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Background image height in PIXELS
|
||||
/// Represents the actual pixel dimensions of the image file
|
||||
/// Used for rendering and coordinate conversion
|
||||
/// </summary>
|
||||
public double? ImageHeight { get; set; }
|
||||
|
||||
// ==========================================
|
||||
// METADATA
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// When these settings were created
|
||||
/// </summary>
|
||||
[Required]
|
||||
public DateTime CreatedDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Last time these settings were modified
|
||||
/// </summary>
|
||||
[Required]
|
||||
public DateTime ModifiedDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// ==========================================
|
||||
// NAVIGATION PROPERTIES
|
||||
// ==========================================
|
||||
|
||||
[ForeignKey(nameof(LevelId))]
|
||||
public virtual LayoutLevel Level { get; set; } = null!;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RobotNet10.MapManager.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Layout version entity - Version history for each layout
|
||||
/// Maps to VDMA LIF: layouts[].layoutVersion
|
||||
/// </summary>
|
||||
[Table("LayoutVersions")]
|
||||
public class LayoutVersion
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parent layout reference
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid LayoutId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Version number (VDMA LIF: layoutVersion)
|
||||
/// Suggested: "1", "2", "3" or "1.0", "1.1"
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(32)]
|
||||
public string Version { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Version description (VDMA LIF: layoutDescription)
|
||||
/// </summary>
|
||||
public string? LayoutDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creator of this version
|
||||
/// </summary>
|
||||
[StringLength(256)]
|
||||
public string? CreatedBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creation date
|
||||
/// </summary>
|
||||
[Required]
|
||||
public DateTime CreatedDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Is this the active version?
|
||||
/// Only ONE version can be active per layout
|
||||
/// Active version is READ-ONLY
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
// Navigation properties
|
||||
[ForeignKey(nameof(LayoutId))]
|
||||
public virtual Layout Layout { get; set; } = null!;
|
||||
|
||||
public virtual ICollection<LayoutLevel> Levels { get; set; } = new List<LayoutLevel>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace RobotNet10.MapManager.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Database context for VDMA LIF 1.0.0 Map Manager
|
||||
/// </summary>
|
||||
public class MapDbContext(DbContextOptions<MapDbContext> options) : DbContext(options)
|
||||
{
|
||||
|
||||
// DbSets
|
||||
public DbSet<Layout> Layouts { get; set; } = null!;
|
||||
public DbSet<LayoutVersion> LayoutVersions { get; set; } = null!;
|
||||
public DbSet<LayoutLevel> LayoutLevels { get; set; } = null!;
|
||||
public DbSet<VehicleType> VehicleTypes { get; set; } = null!;
|
||||
public DbSet<Node> Nodes { get; set; } = null!;
|
||||
public DbSet<Edge> Edges { get; set; } = null!;
|
||||
public DbSet<Station> Stations { get; set; } = null!;
|
||||
public DbSet<StationInteractionNode> StationInteractionNodes { get; set; } = null!;
|
||||
public DbSet<NodeVehicleProperty> NodeVehicleProperties { get; set; } = null!;
|
||||
public DbSet<EdgeVehicleProperty> EdgeVehicleProperties { get; set; } = null!;
|
||||
public DbSet<LayoutLevelEditorSettings> LayoutLevelEditorSettings { get; set; } = null!;
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
// Layout configuration
|
||||
modelBuilder.Entity<Layout>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.HasIndex(e => e.LayoutId).IsUnique();
|
||||
entity.HasIndex(e => e.IsActive);
|
||||
|
||||
entity.HasMany(e => e.Versions)
|
||||
.WithOne(e => e.Layout)
|
||||
.HasForeignKey(e => e.LayoutId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
// LayoutVersion configuration
|
||||
modelBuilder.Entity<LayoutVersion>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.HasIndex(e => new { e.LayoutId, e.Version }).IsUnique();
|
||||
entity.HasIndex(e => e.IsActive);
|
||||
|
||||
entity.HasMany(e => e.Levels)
|
||||
.WithOne(e => e.Version)
|
||||
.HasForeignKey(e => e.VersionId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
// LayoutLevel configuration
|
||||
modelBuilder.Entity<LayoutLevel>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.HasIndex(e => new { e.VersionId, e.LayoutLevelId }).IsUnique();
|
||||
entity.HasIndex(e => e.LevelOrder);
|
||||
|
||||
entity.HasMany(e => e.Nodes)
|
||||
.WithOne(e => e.Level)
|
||||
.HasForeignKey(e => e.LevelId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
entity.HasMany(e => e.Edges)
|
||||
.WithOne(e => e.Level)
|
||||
.HasForeignKey(e => e.LevelId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
entity.HasMany(e => e.Stations)
|
||||
.WithOne(e => e.Level)
|
||||
.HasForeignKey(e => e.LevelId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
// VehicleType configuration
|
||||
modelBuilder.Entity<VehicleType>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.HasIndex(e => e.VehicleTypeId).IsUnique();
|
||||
entity.HasIndex(e => e.IsActive);
|
||||
|
||||
entity.HasMany(e => e.NodeVehicleProperties)
|
||||
.WithOne(e => e.VehicleType)
|
||||
.HasForeignKey(e => e.VehicleTypeId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
entity.HasMany(e => e.EdgeVehicleProperties)
|
||||
.WithOne(e => e.VehicleType)
|
||||
.HasForeignKey(e => e.VehicleTypeId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
// Node configuration
|
||||
modelBuilder.Entity<Node>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.HasIndex(e => new { e.LevelId, e.NodeId }).IsUnique();
|
||||
entity.HasIndex(e => e.NodeId);
|
||||
entity.HasIndex(e => e.MapId);
|
||||
entity.HasIndex(e => new { e.X, e.Y });
|
||||
|
||||
entity.HasMany(e => e.VehicleProperties)
|
||||
.WithOne(e => e.Node)
|
||||
.HasForeignKey(e => e.NodeId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
entity.HasMany(e => e.OutgoingEdges)
|
||||
.WithOne(e => e.StartNode)
|
||||
.HasForeignKey(e => e.StartNodeId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
entity.HasMany(e => e.IncomingEdges)
|
||||
.WithOne(e => e.EndNode)
|
||||
.HasForeignKey(e => e.EndNodeId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
entity.HasMany(e => e.StationInteractions)
|
||||
.WithOne(e => e.Node)
|
||||
.HasForeignKey(e => e.NodeId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
// Edge configuration
|
||||
modelBuilder.Entity<Edge>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.HasIndex(e => new { e.LevelId, e.EdgeId }).IsUnique();
|
||||
entity.HasIndex(e => e.EdgeId);
|
||||
entity.HasIndex(e => e.StartNodeId);
|
||||
entity.HasIndex(e => e.EndNodeId);
|
||||
|
||||
entity.ToTable(t => t.HasCheckConstraint("CK_Edges_DifferentNodes", "[StartNodeId] <> [EndNodeId]"));
|
||||
|
||||
entity.HasMany(e => e.VehicleProperties)
|
||||
.WithOne(e => e.Edge)
|
||||
.HasForeignKey(e => e.EdgeId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
// Station configuration
|
||||
modelBuilder.Entity<Station>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.HasIndex(e => new { e.LevelId, e.StationId }).IsUnique();
|
||||
entity.HasIndex(e => e.StationId);
|
||||
|
||||
entity.HasMany(e => e.InteractionNodes)
|
||||
.WithOne(e => e.Station)
|
||||
.HasForeignKey(e => e.StationId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
// StationInteractionNode configuration
|
||||
modelBuilder.Entity<StationInteractionNode>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.HasIndex(e => new { e.StationId, e.NodeId }).IsUnique();
|
||||
entity.HasIndex(e => e.StationId);
|
||||
entity.HasIndex(e => e.NodeId);
|
||||
});
|
||||
|
||||
// NodeVehicleProperty configuration
|
||||
modelBuilder.Entity<NodeVehicleProperty>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.HasIndex(e => new { e.NodeId, e.VehicleTypeId }).IsUnique();
|
||||
entity.HasIndex(e => e.NodeId);
|
||||
entity.HasIndex(e => e.VehicleTypeId);
|
||||
});
|
||||
|
||||
// EdgeVehicleProperty configuration
|
||||
modelBuilder.Entity<EdgeVehicleProperty>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.HasIndex(e => new { e.EdgeId, e.VehicleTypeId }).IsUnique();
|
||||
entity.HasIndex(e => e.EdgeId);
|
||||
entity.HasIndex(e => e.VehicleTypeId);
|
||||
});
|
||||
|
||||
// LayoutLevelEditorSettings configuration (UI extensions - not VDMA LIF)
|
||||
modelBuilder.Entity<LayoutLevelEditorSettings>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
|
||||
// 1-to-1 relationship with LayoutLevel (unique index on LevelId)
|
||||
entity.HasIndex(e => e.LevelId).IsUnique();
|
||||
|
||||
entity.HasOne(e => e.Level)
|
||||
.WithOne(l => l.EditorSettings)
|
||||
.HasForeignKey<LayoutLevelEditorSettings>(e => e.LevelId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
70
srcs/RobotNet10/Commons/RobotNet10.MapManager/Data/Node.cs
Normal file
70
srcs/RobotNet10/Commons/RobotNet10.MapManager/Data/Node.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RobotNet10.MapManager.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Node entity - Waypoints/nodes on the map
|
||||
/// Maps to VDMA LIF: layouts[].nodes[]
|
||||
/// </summary>
|
||||
[Table("Nodes")]
|
||||
public class Node
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parent level reference
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid LevelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Node identifier (VDMA LIF: nodeId) - Required
|
||||
/// Unique within level
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(128)]
|
||||
public string NodeId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Node name (VDMA LIF: nodeName) - Optional
|
||||
/// </summary>
|
||||
[StringLength(256)]
|
||||
public string? NodeName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Node description (VDMA LIF: nodeDescription) - Optional
|
||||
/// </summary>
|
||||
public string? NodeDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Map identifier (VDMA LIF: mapId) - Optional
|
||||
/// Reference to map image or data
|
||||
/// </summary>
|
||||
[StringLength(128)]
|
||||
public string? MapId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// X coordinate in meters (VDMA LIF: nodePosition.x) - Required
|
||||
/// </summary>
|
||||
[Required]
|
||||
public double X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Y coordinate in meters (VDMA LIF: nodePosition.y) - Required
|
||||
/// </summary>
|
||||
[Required]
|
||||
public double Y { get; set; }
|
||||
|
||||
// Navigation properties
|
||||
[ForeignKey(nameof(LevelId))]
|
||||
public virtual LayoutLevel Level { get; set; } = null!;
|
||||
|
||||
public virtual ICollection<NodeVehicleProperty> VehicleProperties { get; set; } = new List<NodeVehicleProperty>();
|
||||
public virtual ICollection<Edge> OutgoingEdges { get; set; } = new List<Edge>();
|
||||
public virtual ICollection<Edge> IncomingEdges { get; set; } = new List<Edge>();
|
||||
public virtual ICollection<StationInteractionNode> StationInteractions { get; set; } = new List<StationInteractionNode>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RobotNet10.MapManager.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Node vehicle property - Vehicle-specific properties for nodes
|
||||
/// Maps to VDMA LIF: node.vehicleTypeNodeProperties[]
|
||||
/// </summary>
|
||||
[Table("NodeVehicleProperties")]
|
||||
public class NodeVehicleProperty
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Node reference
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid NodeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Vehicle type reference
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid VehicleTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Absolute orientation of vehicle on node (VDMA LIF: theta) - Optional
|
||||
/// In radians, range: [-Pi ... Pi]
|
||||
/// </summary>
|
||||
public double? Theta { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Actions that vehicle can perform at this node (VDMA LIF: actions) - Optional
|
||||
/// JSON array format:
|
||||
/// [
|
||||
/// {
|
||||
/// "actionType": "pick",
|
||||
/// "actionDescription": "...",
|
||||
/// "requirementType": "REQUIRED",
|
||||
/// "blockingType": "HARD",
|
||||
/// "actionParameters": [{"key": "...", "value": "..."}]
|
||||
/// }
|
||||
/// ]
|
||||
/// </summary>
|
||||
public string? Actions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allowed deviation radius in meters (VDA5050: allowedDeviationXY) - Optional
|
||||
/// Indicates how exact an AGV has to drive over a node in order for it to count as traversed.
|
||||
/// If = 0: no deviation is allowed (no deviation means within the normal tolerance of the AGV manufacturer).
|
||||
/// If > 0: allowed deviation-radius in meters. If the AGV passes a node within the deviation-radius, the node is considered to have been traversed.
|
||||
/// Minimum: 0
|
||||
/// </summary>
|
||||
public double? AllowedDeviationXY { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allowed deviation of theta angle in radians (VDA5050: allowedDeviationTheta) - Optional
|
||||
/// Indicates how big the deviation of theta angle can be.
|
||||
/// The lowest acceptable angle is theta - allowedDeviationTheta and the highest acceptable angle is theta + allowedDeviationTheta.
|
||||
/// Range: [0.0 ... 3.141592654] (0 to Pi)
|
||||
/// </summary>
|
||||
public double? AllowedDeviationTheta { get; set; }
|
||||
|
||||
// Navigation properties
|
||||
[ForeignKey(nameof(NodeId))]
|
||||
public virtual Node Node { get; set; } = null!;
|
||||
|
||||
[ForeignKey(nameof(VehicleTypeId))]
|
||||
public virtual VehicleType VehicleType { get; set; } = null!;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RobotNet10.MapManager.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Station entity - Interaction points for vehicles
|
||||
/// Maps to VDMA LIF: layouts[].stations[]
|
||||
/// </summary>
|
||||
[Table("Stations")]
|
||||
public class Station
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parent level reference
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid LevelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Station identifier (VDMA LIF: stationId) - Required
|
||||
/// Unique within level
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(128)]
|
||||
public string StationId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Station name (VDMA LIF: stationName) - Optional
|
||||
/// </summary>
|
||||
[StringLength(256)]
|
||||
public string? StationName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Station description (VDMA LIF: stationDescription) - Optional
|
||||
/// </summary>
|
||||
public string? StationDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Station height in meters (VDMA LIF: stationHeight) - Optional
|
||||
/// </summary>
|
||||
public double? StationHeight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// X coordinate in meters (VDMA LIF: stationPosition.x) - Required
|
||||
/// </summary>
|
||||
[Required]
|
||||
public double X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Y coordinate in meters (VDMA LIF: stationPosition.y) - Required
|
||||
/// </summary>
|
||||
[Required]
|
||||
public double Y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Theta orientation in radians (VDMA LIF: stationPosition.theta) - Optional
|
||||
/// Range: [-Pi ... Pi]
|
||||
/// </summary>
|
||||
public double? Theta { get; set; }
|
||||
|
||||
// Navigation properties
|
||||
[ForeignKey(nameof(LevelId))]
|
||||
public virtual LayoutLevel Level { get; set; } = null!;
|
||||
|
||||
public virtual ICollection<StationInteractionNode> InteractionNodes { get; set; } = new List<StationInteractionNode>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RobotNet10.MapManager.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Station interaction node - Junction table linking stations to nodes
|
||||
/// Maps to VDMA LIF: station.interactionNodeIds[]
|
||||
/// </summary>
|
||||
[Table("StationInteractionNodes")]
|
||||
public class StationInteractionNode
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Station reference
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid StationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Node reference
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid NodeId { get; set; }
|
||||
|
||||
// Navigation properties
|
||||
[ForeignKey(nameof(StationId))]
|
||||
public virtual Station Station { get; set; } = null!;
|
||||
|
||||
[ForeignKey(nameof(NodeId))]
|
||||
public virtual Node Node { get; set; } = null!;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RobotNet10.MapManager.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Vehicle type entity - Master data for vehicle types
|
||||
/// Used in vehicleTypeNodeProperties and vehicleTypeEdgeProperties
|
||||
/// </summary>
|
||||
[Table("VehicleTypes")]
|
||||
public class VehicleType
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Vehicle type identifier (VDMA LIF: vehicleTypeId)
|
||||
/// Example: "AMR-T800", "AMR-F100", "Forklift-X1"
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(64)]
|
||||
public string VehicleTypeId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Vehicle type name
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(256)]
|
||||
public string VehicleTypeName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Description
|
||||
/// </summary>
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Additional specifications (JSON)
|
||||
/// For future extensions
|
||||
/// </summary>
|
||||
public string? Specifications { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is vehicle type active
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Actions default that vehicle can perform (VDMA LIF: actions) - Optional
|
||||
/// JSON array format:
|
||||
/// [
|
||||
/// {
|
||||
/// "actionType": "pick",
|
||||
/// "actionDescription": "...",
|
||||
/// "requirementType": "REQUIRED", // Enum: REQUIRED, CONDITIONAL, OPTIONAL (RequirementType enum)
|
||||
/// "blockingType": "HARD",
|
||||
/// "actionParameters": [{"key": "...", "value": "..."}]
|
||||
/// }
|
||||
/// ]
|
||||
/// </summary>
|
||||
public string? Actions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Created date
|
||||
/// </summary>
|
||||
[Required]
|
||||
public DateTime CreatedDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// Navigation properties
|
||||
public virtual ICollection<NodeVehicleProperty> NodeVehicleProperties { get; set; } = new List<NodeVehicleProperty>();
|
||||
public virtual ICollection<EdgeVehicleProperty> EdgeVehicleProperties { get; set; } = new List<EdgeVehicleProperty>();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user