using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using AGVNavigationCore.Utils; using Newtonsoft.Json; namespace AGVNavigationCore.Models { /// /// 맵 노드 정보를 관리하는 클래스 (주행 경로용 노드) /// public class MapNode : NodeBase { public Station StationType { get; set; } [Browsable(false)] public bool CanDocking { get { if (StationType == Station.Buffer) return true; if (StationType == Station.Loder) return true; if (StationType == Station.Cleaner) return true; if (StationType == Station.Plating) return true; if (StationType == Station.Charger) return true; return false; } } [Category("노드 설정")] [Description("도킹/충전 노드의 진입 방향입니다.")] public DockingDirection DockDirection { get; set; } = DockingDirection.DontCare; [Category("노드 설정")] [Description("각 연결된 노드로 향할 때의 마그넷 방향 정보입니다.")] public Dictionary MagnetDirections { get; set; } = new Dictionary(); [Category("연결 정보")] [Description("연결된 노드 ID 목록입니다.")] [ReadOnly(true)] public List ConnectedNodes { get; set; } = new List(); [JsonIgnore] [Browsable(false)] public List ConnectedMapNodes { get; set; } = new List(); [Category("주행 설정")] [Description("제자리 회전(좌) 가능 여부입니다.")] public bool CanTurnLeft { get; set; } = false; [Category("주행 설정")] [Description("제자리 회전(우) 가능 여부입니다.")] public bool CanTurnRight { get; set; } = false; [Category("주행 설정")] [Description("교차로 주행 가능 여부입니다.")] public bool DisableCross { get { if (Type != NodeType.Normal) return true; return _disablecross; } set { _disablecross = value; } } private bool _disablecross = true; [Category("주행 설정")] [Description("노드 통과 시 제한 속도입니다.")] public SpeedLevel SpeedLimit { get; set; } = SpeedLevel.L; [Category("노드 설정")] [Description("장비 ID 또는 별칭입니다.")] public string AliasName { get; set; } = string.Empty; [Category("기본 정보")] [Description("노드 사용 여부입니다.")] public bool IsActive { get; set; } = true; [Category("RFID 정보")] [Description("물리적 RFID 태그 ID입니다.")] public UInt16 RfidId { get; set; } = 0; [Category("노드 텍스트"), DisplayName("TextColor")] [Description("텍스트 색상입니다.")] public Color NodeTextForeColor { get; set; } = Color.Black; private float _textFontSize = 7.0f; [Category("노드 텍스트"), DisplayName("TextSize")] [Description("일반 노드 텍스트의 크기입니다.")] public float NodeTextFontSize { get => _textFontSize; set => _textFontSize = value > 0 ? value : 7.0f; } [Category("노드 텍스트")] [Description("표시할 텍스트입니다.")] public string Text { get; set; } = ""; public MapNode() : base() { Type = NodeType.Normal; } public MapNode(string nodeId, Point position, Station type) : base(nodeId, position) { Type = NodeType.Normal; } [Category("기본 정보")] [JsonIgnore] [ReadOnly(true), Browsable(false)] public bool isDockingNode { get { if (StationType == Station.Charger || StationType == Station.Buffer || StationType == Station.Plating || StationType == Station.Loder || StationType == Station.Cleaner) return true; return false; } } public void AddConnection(string nodeId) { if (!ConnectedNodes.Contains(nodeId)) { ConnectedNodes.Add(nodeId); ModifiedDate = DateTime.Now; } } public void RemoveConnection(string nodeId) { if (ConnectedNodes.Remove(nodeId)) { if (MagnetDirections != null && MagnetDirections.ContainsKey(nodeId)) { MagnetDirections.Remove(nodeId); } // 🔥 ConnectedMapNodes에서도 제거 (화면 갱신용) var target = ConnectedMapNodes.Find(n => n.Id == nodeId); if (target != null) ConnectedMapNodes.Remove(target); ModifiedDate = DateTime.Now; } } public void SetChargingStation(string stationId) { //StationType = StationType.Charger; //Id = stationId; //DockDirection = DockingDirection.Forward; //ModifiedDate = DateTime.Now; } public override string ToString() { return $"RFID:{RfidId}(NODE:{Id}): AS:{AliasName} ({Type}) at ({Position.X}, {Position.Y})"; } /// /// RFID(*ID) /// public string ID2 { get { if (HasRfid()) return $"{this.RfidId:0000}(*{this.Id})"; else return $"(*{this.Id})"; } } public bool IsNavigationNode() { // 이제 MapNode는 항상 내비게이션 노드임 (Label, Image 분리됨) // 하지만 기존 로직 호환성을 위해 Active 체크만 유지 return IsActive; } public bool HasRfid() { return RfidId > 0; } } }