using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Design; using AGVNavigationCore.Models; using AGVNavigationCore.Controls; namespace AGVMapEditor.Models { /// /// 노드 타입에 따른 PropertyWrapper 팩토리 /// public static class NodePropertyWrapperFactory { public static object CreateWrapper(MapNode node, List 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); } } } /// /// 라벨 노드 전용 PropertyWrapper /// public class LabelNodePropertyWrapper { private MapNode _node; private List _mapNodes; public LabelNodePropertyWrapper(MapNode node, List mapNodes) { _node = node; _mapNodes = mapNodes; } /// /// 래핑된 MapNode 인스턴스 접근 /// public MapNode WrappedNode => _node; [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("패딩")] [Description("텍스트 주변 여백 (픽셀 단위)")] public int Padding { get => _node.Padding; set { _node.Padding = Math.Max(0, Math.Min(50, 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; } } /// /// 이미지 노드 전용 PropertyWrapper /// public class ImageNodePropertyWrapper { private MapNode _node; private List _mapNodes; public ImageNodePropertyWrapper(MapNode node, List mapNodes) { _node = node; _mapNodes = mapNodes; } /// /// 래핑된 MapNode 인스턴스 접근 /// public MapNode WrappedNode => _node; [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("이미지 파일 경로 (... 버튼으로 파일 선택)")] [Editor(typeof(ImagePathEditor), typeof(UITypeEditor))] public string ImagePath { get => _node.ImagePath; set { if (string.IsNullOrEmpty(value)) { _node.ImagePath = ""; return; } // 파일이 존재하면 Base64로 변환하여 저장 if (System.IO.File.Exists(value)) { _node.ConvertImageToBase64(value); _node.LoadImage(); // 이미지 다시 로드 } else { _node.ImagePath = value; } _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; } } /// /// PropertyGrid에서 사용할 노드 속성 래퍼 클래스 /// public class NodePropertyWrapper { private MapNode _node; private List _mapNodes; public NodePropertyWrapper(MapNode node, List mapNodes) { _node = node; _mapNodes = mapNodes; } /// /// 래핑된 MapNode 인스턴스 접근 /// public MapNode WrappedNode => _node; [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("도킹 방향")] [Description("도킹이 필요한 노드의 경우 AGV 진입 방향 (DontCare: 방향 무관, Forward: 전진 도킹, Backward: 후진 도킹)")] public DockingDirection DockDirection { get => _node.DockDirection; set { _node.DockDirection = 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 bool IsActive { get => _node.IsActive; set { _node.IsActive = value; _node.ModifiedDate = DateTime.Now; } } [Category("표시")] [DisplayName("노드 배경색")] [Description("노드 배경 색상")] public Color DisplayColor { get => _node.DisplayColor; set { _node.DisplayColor = value; _node.ModifiedDate = DateTime.Now; } } [Category("표시")] [DisplayName("글자 색상")] [Description("노드 텍스트 색상 (NodeId, Name 등)")] public Color ForeColor { get => _node.ForeColor; set { _node.ForeColor = value; _node.ModifiedDate = DateTime.Now; } } [Category("표시")] [DisplayName("글자 크기")] [Description("노드 텍스트 크기 (픽셀)")] public float TextFontSize { get => _node.TextFontSize; set { _node.TextFontSize = Math.Max(5.0f, Math.Min(20.0f, value)); _node.ModifiedDate = DateTime.Now; } } [Category("표시")] [DisplayName("글자 굵게")] [Description("노드 텍스트 볼드 표시")] public bool TextFontBold { get => _node.TextFontBold; set { _node.TextFontBold = value; _node.ModifiedDate = DateTime.Now; } } [Category("표시")] [DisplayName("이름 말풍선 배경색")] [Description("노드 이름 말풍선(하단 표시) 배경색")] public Color NameBubbleBackColor { get => _node.NameBubbleBackColor; set { _node.NameBubbleBackColor = value; _node.ModifiedDate = DateTime.Now; } } [Category("표시")] [DisplayName("이름 말풍선 글자색")] [Description("노드 이름 말풍선(하단 표시) 글자색")] public Color NameBubbleForeColor { get => _node.NameBubbleForeColor; set { _node.NameBubbleForeColor = 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; } } /// /// 다중 노드 선택 시 공통 속성 편집용 래퍼 /// public class MultiNodePropertyWrapper { private List _nodes; public MultiNodePropertyWrapper(List nodes) { _nodes = nodes ?? new List(); } [Category("다중 선택")] [DisplayName("선택된 노드 수")] [Description("현재 선택된 노드의 개수")] [ReadOnly(true)] public int SelectedCount => _nodes.Count; [Category("표시")] [DisplayName("노드 배경색")] [Description("선택된 모든 노드의 배경색을 일괄 변경")] public Color DisplayColor { get => _nodes.Count > 0 ? _nodes[0].DisplayColor : Color.Blue; set { foreach (var node in _nodes) { node.DisplayColor = value; node.ModifiedDate = DateTime.Now; } } } [Category("표시")] [DisplayName("글자 색상")] [Description("선택된 모든 노드의 글자 색상을 일괄 변경")] public Color ForeColor { get => _nodes.Count > 0 ? _nodes[0].ForeColor : Color.Black; set { foreach (var node in _nodes) { node.ForeColor = value; node.ModifiedDate = DateTime.Now; } } } [Category("표시")] [DisplayName("글자 크기")] [Description("선택된 모든 노드의 글자 크기를 일괄 변경 (5~20 픽셀)")] public float TextFontSize { get => _nodes.Count > 0 ? _nodes[0].TextFontSize : 7.0f; set { var validValue = Math.Max(5.0f, Math.Min(20.0f, value)); System.Diagnostics.Debug.WriteLine($"[MultiNode] TextFontSize 변경 시작: {validValue}, 노드 수: {_nodes.Count}"); foreach (var node in _nodes) { System.Diagnostics.Debug.WriteLine($" - 노드 {node.NodeId}: {node.TextFontSize} → {validValue}"); node.TextFontSize = validValue; node.ModifiedDate = DateTime.Now; System.Diagnostics.Debug.WriteLine($" - 변경 후: {node.TextFontSize}"); } System.Diagnostics.Debug.WriteLine($"[MultiNode] TextFontSize 변경 완료"); } } [Category("표시")] [DisplayName("글자 굵게")] [Description("선택된 모든 노드의 글자 굵기를 일괄 변경")] public bool TextFontBold { get { var result = _nodes.Count > 0 ? _nodes[0].TextFontBold : true; System.Diagnostics.Debug.WriteLine($"[MultiNode] TextFontBold GET: {result}"); return result; } set { System.Diagnostics.Debug.WriteLine($"[MultiNode] TextFontBold 변경 시작: {value}, 노드 수: {_nodes.Count}"); foreach (var node in _nodes) { System.Diagnostics.Debug.WriteLine($" - 노드 {node.NodeId}: {node.TextFontBold} → {value}"); node.TextFontBold = value; node.ModifiedDate = DateTime.Now; System.Diagnostics.Debug.WriteLine($" - 변경 후: {node.TextFontBold}"); } System.Diagnostics.Debug.WriteLine($"[MultiNode] TextFontBold 변경 완료"); } } [Category("표시")] [DisplayName("이름 말풍선 배경색")] [Description("선택된 모든 노드의 이름 말풍선(하단 표시) 배경색을 일괄 변경")] public Color NameBubbleBackColor { get => _nodes.Count > 0 ? _nodes[0].NameBubbleBackColor : Color.Gold; set { foreach (var node in _nodes) { node.NameBubbleBackColor = value; node.ModifiedDate = DateTime.Now; } } } [Category("표시")] [DisplayName("이름 말풍선 글자색")] [Description("선택된 모든 노드의 이름 말풍선(하단 표시) 글자색을 일괄 변경")] public Color NameBubbleForeColor { get => _nodes.Count > 0 ? _nodes[0].NameBubbleForeColor : Color.Black; set { foreach (var node in _nodes) { node.NameBubbleForeColor = value; node.ModifiedDate = DateTime.Now; } } } [Category("고급")] [DisplayName("활성화")] [Description("선택된 모든 노드의 활성화 상태를 일괄 변경")] public bool IsActive { get => _nodes.Count > 0 ? _nodes[0].IsActive : true; set { foreach (var node in _nodes) { node.IsActive = value; node.ModifiedDate = DateTime.Now; } } } [Category("정보")] [DisplayName("안내")] [Description("다중 선택 시 공통 속성만 변경할 수 있습니다")] [ReadOnly(true)] public string HelpText => "위 속성을 변경하면 선택된 모든 노드에 일괄 적용됩니다."; } /// /// 캔버스 속성 래퍼 (배경색 등 캔버스 전체 속성 편집) /// public class CanvasPropertyWrapper { private UnifiedAGVCanvas _canvas; public CanvasPropertyWrapper(UnifiedAGVCanvas canvas) { _canvas = canvas; } [Category("배경")] [DisplayName("배경색")] [Description("맵 캔버스 배경색")] public Color BackgroundColor { get => _canvas.BackColor; set { _canvas.BackColor = value; _canvas.Invalidate(); } } [Category("그리드")] [DisplayName("그리드 표시")] [Description("그리드 표시 여부")] public bool ShowGrid { get => _canvas.ShowGrid; set { _canvas.ShowGrid = value; _canvas.Invalidate(); } } [Category("정보")] [DisplayName("설명")] [Description("맵 캔버스 전체 속성")] [ReadOnly(true)] public string Description { get => "빈 공간을 클릭하면 캔버스 전체 속성을 편집할 수 있습니다."; } } }