Initial commit

This commit is contained in:
2026-07-13 09:25:40 +07:00
parent c08ff54676
commit bccfb156d7
1938 changed files with 641646 additions and 0 deletions

View 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>();
}

View File

@@ -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!;
}

View 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>();
}

View File

@@ -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; }
}

View File

@@ -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!;
}

View File

@@ -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>();
}

View File

@@ -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);
});
}
}

View 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>();
}

View File

@@ -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!;
}

View File

@@ -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>();
}

View File

@@ -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!;
}

View File

@@ -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>();
}