Major improvements to AGV navigation system: • Consolidated RFID management into MapNode, removing duplicate RfidMapping class • Enhanced MapNode with RFID metadata fields (RfidStatus, RfidDescription) • Added automatic bidirectional connection generation in pathfinding algorithms • Updated all components to use unified MapNode-based RFID system • Added command line argument support for AGVMapEditor auto-loading files • Fixed pathfinding failures by ensuring proper node connectivity Technical changes: - Removed RfidMapping class and dependencies across all projects - Updated AStarPathfinder with EnsureBidirectionalConnections() method - Modified MapLoader to use AssignAutoRfidIds() for RFID automation - Enhanced UnifiedAGVCanvas, SimulatorForm, and MainForm for MapNode integration - Improved data consistency and reduced memory footprint 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
499 lines
13 KiB
C#
499 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using AGVNavigationCore.Models;
|
|
|
|
namespace AGVMapEditor.Models
|
|
{
|
|
/// <summary>
|
|
/// 노드 타입에 따른 PropertyWrapper 팩토리
|
|
/// </summary>
|
|
public static class NodePropertyWrapperFactory
|
|
{
|
|
public static object CreateWrapper(MapNode node, List<MapNode> mapNodes)
|
|
{
|
|
switch (node.Type)
|
|
{
|
|
case NodeType.Label:
|
|
return new LabelNodePropertyWrapper(node, mapNodes);
|
|
case NodeType.Image:
|
|
return new ImageNodePropertyWrapper(node, mapNodes);
|
|
default:
|
|
return new NodePropertyWrapper(node, mapNodes);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 라벨 노드 전용 PropertyWrapper
|
|
/// </summary>
|
|
public class LabelNodePropertyWrapper
|
|
{
|
|
private MapNode _node;
|
|
private List<MapNode> _mapNodes;
|
|
|
|
public LabelNodePropertyWrapper(MapNode node, List<MapNode> mapNodes)
|
|
{
|
|
_node = node;
|
|
_mapNodes = mapNodes;
|
|
}
|
|
|
|
[Category("기본 정보")]
|
|
[DisplayName("노드 ID")]
|
|
[Description("노드의 고유 식별자")]
|
|
[ReadOnly(true)]
|
|
public string NodeId
|
|
{
|
|
get => _node.NodeId;
|
|
}
|
|
|
|
[Category("기본 정보")]
|
|
[DisplayName("노드 타입")]
|
|
[Description("노드의 타입")]
|
|
[ReadOnly(true)]
|
|
public NodeType Type
|
|
{
|
|
get => _node.Type;
|
|
}
|
|
|
|
[Category("라벨")]
|
|
[DisplayName("텍스트")]
|
|
[Description("표시할 텍스트")]
|
|
public string LabelText
|
|
{
|
|
get => _node.LabelText;
|
|
set
|
|
{
|
|
_node.LabelText = value ?? "";
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("라벨")]
|
|
[DisplayName("폰트 패밀리")]
|
|
[Description("폰트 패밀리")]
|
|
public string FontFamily
|
|
{
|
|
get => _node.FontFamily;
|
|
set
|
|
{
|
|
_node.FontFamily = value ?? "Arial";
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("라벨")]
|
|
[DisplayName("폰트 크기")]
|
|
[Description("폰트 크기")]
|
|
public float FontSize
|
|
{
|
|
get => _node.FontSize;
|
|
set
|
|
{
|
|
_node.FontSize = Math.Max(6, Math.Min(72, value));
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("라벨")]
|
|
[DisplayName("폰트 스타일")]
|
|
[Description("폰트 스타일")]
|
|
public FontStyle FontStyle
|
|
{
|
|
get => _node.FontStyle;
|
|
set
|
|
{
|
|
_node.FontStyle = value;
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("라벨")]
|
|
[DisplayName("전경색")]
|
|
[Description("텍스트 색상")]
|
|
public Color ForeColor
|
|
{
|
|
get => _node.ForeColor;
|
|
set
|
|
{
|
|
_node.ForeColor = value;
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("라벨")]
|
|
[DisplayName("배경색")]
|
|
[Description("배경 색상")]
|
|
public Color BackColor
|
|
{
|
|
get => _node.BackColor;
|
|
set
|
|
{
|
|
_node.BackColor = value;
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("라벨")]
|
|
[DisplayName("배경 표시")]
|
|
[Description("배경을 표시할지 여부")]
|
|
public bool ShowBackground
|
|
{
|
|
get => _node.ShowBackground;
|
|
set
|
|
{
|
|
_node.ShowBackground = value;
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("위치")]
|
|
[DisplayName("X 좌표")]
|
|
[Description("맵에서의 X 좌표")]
|
|
public int PositionX
|
|
{
|
|
get => _node.Position.X;
|
|
set
|
|
{
|
|
_node.Position = new Point(value, _node.Position.Y);
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("위치")]
|
|
[DisplayName("Y 좌표")]
|
|
[Description("맵에서의 Y 좌표")]
|
|
public int PositionY
|
|
{
|
|
get => _node.Position.Y;
|
|
set
|
|
{
|
|
_node.Position = new Point(_node.Position.X, value);
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("정보")]
|
|
[DisplayName("생성 일시")]
|
|
[Description("노드가 생성된 일시")]
|
|
[ReadOnly(true)]
|
|
public DateTime CreatedDate
|
|
{
|
|
get => _node.CreatedDate;
|
|
}
|
|
|
|
[Category("정보")]
|
|
[DisplayName("수정 일시")]
|
|
[Description("노드가 마지막으로 수정된 일시")]
|
|
[ReadOnly(true)]
|
|
public DateTime ModifiedDate
|
|
{
|
|
get => _node.ModifiedDate;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 이미지 노드 전용 PropertyWrapper
|
|
/// </summary>
|
|
public class ImageNodePropertyWrapper
|
|
{
|
|
private MapNode _node;
|
|
private List<MapNode> _mapNodes;
|
|
|
|
public ImageNodePropertyWrapper(MapNode node, List<MapNode> mapNodes)
|
|
{
|
|
_node = node;
|
|
_mapNodes = mapNodes;
|
|
}
|
|
|
|
[Category("기본 정보")]
|
|
[DisplayName("노드 ID")]
|
|
[Description("노드의 고유 식별자")]
|
|
[ReadOnly(true)]
|
|
public string NodeId
|
|
{
|
|
get => _node.NodeId;
|
|
}
|
|
|
|
[Category("기본 정보")]
|
|
[DisplayName("노드 타입")]
|
|
[Description("노드의 타입")]
|
|
[ReadOnly(true)]
|
|
public NodeType Type
|
|
{
|
|
get => _node.Type;
|
|
}
|
|
|
|
[Category("이미지")]
|
|
[DisplayName("이미지 경로")]
|
|
[Description("이미지 파일 경로")]
|
|
// 파일 선택 에디터는 나중에 구현
|
|
public string ImagePath
|
|
{
|
|
get => _node.ImagePath;
|
|
set
|
|
{
|
|
_node.ImagePath = value ?? "";
|
|
_node.LoadImage(); // 이미지 다시 로드
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("이미지")]
|
|
[DisplayName("가로 배율")]
|
|
[Description("가로 배율 (1.0 = 원본 크기)")]
|
|
public float ScaleWidth
|
|
{
|
|
get => _node.Scale.Width;
|
|
set
|
|
{
|
|
_node.Scale = new SizeF(Math.Max(0.1f, Math.Min(5.0f, value)), _node.Scale.Height);
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("이미지")]
|
|
[DisplayName("세로 배율")]
|
|
[Description("세로 배율 (1.0 = 원본 크기)")]
|
|
public float ScaleHeight
|
|
{
|
|
get => _node.Scale.Height;
|
|
set
|
|
{
|
|
_node.Scale = new SizeF(_node.Scale.Width, Math.Max(0.1f, Math.Min(5.0f, value)));
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("이미지")]
|
|
[DisplayName("투명도")]
|
|
[Description("투명도 (0.0 = 투명, 1.0 = 불투명)")]
|
|
public float Opacity
|
|
{
|
|
get => _node.Opacity;
|
|
set
|
|
{
|
|
_node.Opacity = Math.Max(0.0f, Math.Min(1.0f, value));
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("이미지")]
|
|
[DisplayName("회전각도")]
|
|
[Description("회전 각도 (도 단위)")]
|
|
public float Rotation
|
|
{
|
|
get => _node.Rotation;
|
|
set
|
|
{
|
|
_node.Rotation = value % 360;
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("위치")]
|
|
[DisplayName("X 좌표")]
|
|
[Description("맵에서의 X 좌표")]
|
|
public int PositionX
|
|
{
|
|
get => _node.Position.X;
|
|
set
|
|
{
|
|
_node.Position = new Point(value, _node.Position.Y);
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("위치")]
|
|
[DisplayName("Y 좌표")]
|
|
[Description("맵에서의 Y 좌표")]
|
|
public int PositionY
|
|
{
|
|
get => _node.Position.Y;
|
|
set
|
|
{
|
|
_node.Position = new Point(_node.Position.X, value);
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("정보")]
|
|
[DisplayName("생성 일시")]
|
|
[Description("노드가 생성된 일시")]
|
|
[ReadOnly(true)]
|
|
public DateTime CreatedDate
|
|
{
|
|
get => _node.CreatedDate;
|
|
}
|
|
|
|
[Category("정보")]
|
|
[DisplayName("수정 일시")]
|
|
[Description("노드가 마지막으로 수정된 일시")]
|
|
[ReadOnly(true)]
|
|
public DateTime ModifiedDate
|
|
{
|
|
get => _node.ModifiedDate;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// PropertyGrid에서 사용할 노드 속성 래퍼 클래스
|
|
/// </summary>
|
|
public class NodePropertyWrapper
|
|
{
|
|
private MapNode _node;
|
|
private List<MapNode> _mapNodes;
|
|
|
|
public NodePropertyWrapper(MapNode node, List<MapNode> mapNodes)
|
|
{
|
|
_node = node;
|
|
_mapNodes = mapNodes;
|
|
}
|
|
|
|
[Category("기본 정보")]
|
|
[DisplayName("노드 ID")]
|
|
[Description("노드의 고유 식별자")]
|
|
[ReadOnly(true)]
|
|
public string NodeId
|
|
{
|
|
get => _node.NodeId;
|
|
set => _node.NodeId = value;
|
|
}
|
|
|
|
[Category("기본 정보")]
|
|
[DisplayName("노드 이름")]
|
|
[Description("노드의 표시 이름")]
|
|
public string Name
|
|
{
|
|
get => _node.Name;
|
|
set
|
|
{
|
|
_node.Name = value;
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
[Category("기본 정보")]
|
|
[DisplayName("노드 타입")]
|
|
[Description("노드의 타입 (Normal, Rotation, Docking, Charging)")]
|
|
public NodeType Type
|
|
{
|
|
get => _node.Type;
|
|
set
|
|
{
|
|
_node.Type = value;
|
|
_node.CanRotate = value == NodeType.Rotation;
|
|
_node.SetDefaultColorByType(value);
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[Category("위치")]
|
|
[DisplayName("X 좌표")]
|
|
[Description("맵에서의 X 좌표")]
|
|
public int PositionX
|
|
{
|
|
get => _node.Position.X;
|
|
set
|
|
{
|
|
_node.Position = new Point(value, _node.Position.Y);
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("위치")]
|
|
[DisplayName("Y 좌표")]
|
|
[Description("맵에서의 Y 좌표")]
|
|
public int PositionY
|
|
{
|
|
get => _node.Position.Y;
|
|
set
|
|
{
|
|
_node.Position = new Point(_node.Position.X, value);
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
[Category("고급")]
|
|
[DisplayName("회전 가능")]
|
|
[Description("이 노드에서 AGV가 회전할 수 있는지 여부")]
|
|
public bool CanRotate
|
|
{
|
|
get => _node.CanRotate;
|
|
set
|
|
{
|
|
_node.CanRotate = value;
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("고급")]
|
|
[DisplayName("RFID")]
|
|
[Description("RFID ID")]
|
|
public string RFID
|
|
{
|
|
get => _node.RfidId;
|
|
set
|
|
{
|
|
_node.RfidId = value;
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
[Category("고급")]
|
|
[DisplayName("설명")]
|
|
[Description("노드에 대한 추가 설명")]
|
|
public string Description
|
|
{
|
|
get => _node.Description;
|
|
set
|
|
{
|
|
_node.Description = value ?? "";
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("고급")]
|
|
[DisplayName("활성화")]
|
|
[Description("노드 활성화 여부")]
|
|
public bool IsActive
|
|
{
|
|
get => _node.IsActive;
|
|
set
|
|
{
|
|
_node.IsActive = value;
|
|
_node.ModifiedDate = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
[Category("정보")]
|
|
[DisplayName("생성 일시")]
|
|
[Description("노드가 생성된 일시")]
|
|
[ReadOnly(true)]
|
|
public DateTime CreatedDate
|
|
{
|
|
get => _node.CreatedDate;
|
|
}
|
|
|
|
[Category("정보")]
|
|
[DisplayName("수정 일시")]
|
|
[Description("노드가 마지막으로 수정된 일시")]
|
|
[ReadOnly(true)]
|
|
public DateTime ModifiedDate
|
|
{
|
|
get => _node.ModifiedDate;
|
|
}
|
|
}
|
|
|
|
|
|
} |