Compare commits
4 Commits
0396c61517
...
3044894e5c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3044894e5c | ||
|
|
ddf111c69f | ||
|
|
64ff2ecfb9 | ||
|
|
82592e117d |
143
Cs_HMI/AGVLogic/AGVMapEditor/Forms/MainForm.Designer.cs
generated
143
Cs_HMI/AGVLogic/AGVMapEditor/Forms/MainForm.Designer.cs
generated
@@ -67,6 +67,16 @@ namespace AGVMapEditor.Forms
|
||||
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.toolStripButton1 = new System.Windows.Forms.ToolStripDropDownButton();
|
||||
this.allTurnLeftRightCrossOnToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.tabPage2 = new System.Windows.Forms.TabPage();
|
||||
this.lstMagnetDirection = new System.Windows.Forms.ListBox();
|
||||
this.toolStrip4 = new System.Windows.Forms.ToolStrip();
|
||||
this.btDirDelete = new System.Windows.Forms.ToolStripButton();
|
||||
this.btMakeDirdata = new System.Windows.Forms.ToolStripButton();
|
||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.button2 = new System.Windows.Forms.Button();
|
||||
this.button3 = new System.Windows.Forms.Button();
|
||||
this.toolStripButton2 = new System.Windows.Forms.ToolStripButton();
|
||||
this.statusStrip1.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
|
||||
this.splitContainer1.Panel1.SuspendLayout();
|
||||
@@ -78,6 +88,9 @@ namespace AGVMapEditor.Forms
|
||||
this.toolStrip1.SuspendLayout();
|
||||
this.toolStrip3.SuspendLayout();
|
||||
this.toolStrip2.SuspendLayout();
|
||||
this.tabPage2.SuspendLayout();
|
||||
this.toolStrip4.SuspendLayout();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// statusStrip1
|
||||
@@ -120,6 +133,7 @@ namespace AGVMapEditor.Forms
|
||||
//
|
||||
this.tabControl1.Controls.Add(this.tabPageNodes);
|
||||
this.tabControl1.Controls.Add(this.tabPage1);
|
||||
this.tabControl1.Controls.Add(this.tabPage2);
|
||||
this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tabControl1.Location = new System.Drawing.Point(0, 0);
|
||||
this.tabControl1.Name = "tabControl1";
|
||||
@@ -444,6 +458,120 @@ namespace AGVMapEditor.Forms
|
||||
this.allTurnLeftRightCrossOnToolStripMenuItem.Text = "All TurnLeft/Right/Cross On";
|
||||
this.allTurnLeftRightCrossOnToolStripMenuItem.Click += new System.EventHandler(this.allTurnLeftRightCrossOnToolStripMenuItem_Click);
|
||||
//
|
||||
// tabPage2
|
||||
//
|
||||
this.tabPage2.Controls.Add(this.lstMagnetDirection);
|
||||
this.tabPage2.Controls.Add(this.tableLayoutPanel1);
|
||||
this.tabPage2.Controls.Add(this.toolStrip4);
|
||||
this.tabPage2.Location = new System.Drawing.Point(4, 22);
|
||||
this.tabPage2.Name = "tabPage2";
|
||||
this.tabPage2.Size = new System.Drawing.Size(292, 309);
|
||||
this.tabPage2.TabIndex = 2;
|
||||
this.tabPage2.Text = "방향 관리";
|
||||
this.tabPage2.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// lstMagnetDirection
|
||||
//
|
||||
this.lstMagnetDirection.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.lstMagnetDirection.FormattingEnabled = true;
|
||||
this.lstMagnetDirection.ItemHeight = 12;
|
||||
this.lstMagnetDirection.Location = new System.Drawing.Point(0, 25);
|
||||
this.lstMagnetDirection.Name = "lstMagnetDirection";
|
||||
this.lstMagnetDirection.Size = new System.Drawing.Size(292, 246);
|
||||
this.lstMagnetDirection.TabIndex = 3;
|
||||
//
|
||||
// toolStrip4
|
||||
//
|
||||
this.toolStrip4.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.btDirDelete,
|
||||
this.btMakeDirdata,
|
||||
this.toolStripButton2});
|
||||
this.toolStrip4.Location = new System.Drawing.Point(0, 0);
|
||||
this.toolStrip4.Name = "toolStrip4";
|
||||
this.toolStrip4.Size = new System.Drawing.Size(292, 25);
|
||||
this.toolStrip4.TabIndex = 5;
|
||||
this.toolStrip4.Text = "toolStrip4";
|
||||
//
|
||||
// btDirDelete
|
||||
//
|
||||
this.btDirDelete.Image = ((System.Drawing.Image)(resources.GetObject("btDirDelete.Image")));
|
||||
this.btDirDelete.ImageTransparentColor = System.Drawing.Color.Magenta;
|
||||
this.btDirDelete.Name = "btDirDelete";
|
||||
this.btDirDelete.Size = new System.Drawing.Size(61, 22);
|
||||
this.btDirDelete.Text = "Delete";
|
||||
this.btDirDelete.Click += new System.EventHandler(this.btDirDelete_Click);
|
||||
//
|
||||
// btMakeDirdata
|
||||
//
|
||||
this.btMakeDirdata.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
|
||||
this.btMakeDirdata.Image = ((System.Drawing.Image)(resources.GetObject("btMakeDirdata.Image")));
|
||||
this.btMakeDirdata.ImageTransparentColor = System.Drawing.Color.Magenta;
|
||||
this.btMakeDirdata.Name = "btMakeDirdata";
|
||||
this.btMakeDirdata.Size = new System.Drawing.Size(69, 22);
|
||||
this.btMakeDirdata.Text = "Remake";
|
||||
this.btMakeDirdata.Click += new System.EventHandler(this.toolStripButton3_Click);
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.ColumnCount = 3;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
|
||||
this.tableLayoutPanel1.Controls.Add(this.button1, 0, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.button2, 1, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.button3, 2, 0);
|
||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 271);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
this.tableLayoutPanel1.RowCount = 1;
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(292, 38);
|
||||
this.tableLayoutPanel1.TabIndex = 6;
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.button1.Location = new System.Drawing.Point(3, 3);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(91, 32);
|
||||
this.button1.TabIndex = 0;
|
||||
this.button1.Text = "Left";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
// button2
|
||||
//
|
||||
this.button2.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.button2.Location = new System.Drawing.Point(100, 3);
|
||||
this.button2.Name = "button2";
|
||||
this.button2.Size = new System.Drawing.Size(91, 32);
|
||||
this.button2.TabIndex = 0;
|
||||
this.button2.Text = "Straight";
|
||||
this.button2.UseVisualStyleBackColor = true;
|
||||
this.button2.Click += new System.EventHandler(this.button2_Click);
|
||||
//
|
||||
// button3
|
||||
//
|
||||
this.button3.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.button3.Location = new System.Drawing.Point(197, 3);
|
||||
this.button3.Name = "button3";
|
||||
this.button3.Size = new System.Drawing.Size(92, 32);
|
||||
this.button3.TabIndex = 0;
|
||||
this.button3.Text = "Right";
|
||||
this.button3.UseVisualStyleBackColor = true;
|
||||
this.button3.Click += new System.EventHandler(this.button3_Click);
|
||||
//
|
||||
// toolStripButton2
|
||||
//
|
||||
this.toolStripButton2.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
|
||||
this.toolStripButton2.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton2.Image")));
|
||||
this.toolStripButton2.ImageTransparentColor = System.Drawing.Color.Magenta;
|
||||
this.toolStripButton2.Name = "toolStripButton2";
|
||||
this.toolStripButton2.Size = new System.Drawing.Size(54, 22);
|
||||
this.toolStripButton2.Text = "Clear";
|
||||
this.toolStripButton2.Click += new System.EventHandler(this.toolStripButton2_Click);
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||
@@ -475,6 +603,11 @@ namespace AGVMapEditor.Forms
|
||||
this.toolStrip3.PerformLayout();
|
||||
this.toolStrip2.ResumeLayout(false);
|
||||
this.toolStrip2.PerformLayout();
|
||||
this.tabPage2.ResumeLayout(false);
|
||||
this.tabPage2.PerformLayout();
|
||||
this.toolStrip4.ResumeLayout(false);
|
||||
this.toolStrip4.PerformLayout();
|
||||
this.tableLayoutPanel1.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
@@ -520,5 +653,15 @@ namespace AGVMapEditor.Forms
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
|
||||
private System.Windows.Forms.ToolStripDropDownButton toolStripButton1;
|
||||
private System.Windows.Forms.ToolStripMenuItem allTurnLeftRightCrossOnToolStripMenuItem;
|
||||
private System.Windows.Forms.TabPage tabPage2;
|
||||
private System.Windows.Forms.ListBox lstMagnetDirection;
|
||||
private System.Windows.Forms.ToolStrip toolStrip4;
|
||||
private System.Windows.Forms.ToolStripButton btDirDelete;
|
||||
private System.Windows.Forms.ToolStripButton btMakeDirdata;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||
private System.Windows.Forms.Button button1;
|
||||
private System.Windows.Forms.Button button2;
|
||||
private System.Windows.Forms.Button button3;
|
||||
private System.Windows.Forms.ToolStripButton toolStripButton2;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ using AGVNavigationCore.Models;
|
||||
using MapImage = AGVNavigationCore.Models.MapImage;
|
||||
using MapLabel = AGVNavigationCore.Models.MapLabel;
|
||||
using Newtonsoft.Json;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace AGVMapEditor.Forms
|
||||
{
|
||||
@@ -44,7 +45,7 @@ namespace AGVMapEditor.Forms
|
||||
{
|
||||
// RFID가 있으면 RFID(노드이름), 없으면 NodeID(노드이름) 형태로 표시
|
||||
string fromDisplay = FromRfidId > 0
|
||||
? $"{FromRfidId:0000}(*{FromNodeId.PadLeft(4,'0')})"
|
||||
? $"{FromRfidId:0000}(*{FromNodeId.PadLeft(4, '0')})"
|
||||
: $"(*{FromNodeId})";
|
||||
|
||||
string toDisplay = ToRfidId > 0
|
||||
@@ -58,6 +59,7 @@ namespace AGVMapEditor.Forms
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
@@ -105,6 +107,7 @@ namespace AGVMapEditor.Forms
|
||||
{
|
||||
_mapCanvas = new UnifiedAGVCanvas();
|
||||
_mapCanvas.Dock = DockStyle.Fill;
|
||||
_mapCanvas.Mode = UnifiedAGVCanvas.CanvasMode.Edit;
|
||||
|
||||
// 이벤트 연결
|
||||
_mapCanvas.NodeAdded += OnNodeAdded;
|
||||
@@ -981,6 +984,9 @@ namespace AGVMapEditor.Forms
|
||||
|
||||
// 이미지 노드인 경우 편집 버튼 활성화
|
||||
UpdateImageEditButton();
|
||||
|
||||
// 마그넷 방향 리스트 업데이트
|
||||
RefreshMagnetDirectionList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -996,6 +1002,7 @@ namespace AGVMapEditor.Forms
|
||||
{
|
||||
_propertyGrid.SelectedObject = null;
|
||||
DisableImageEditButton();
|
||||
lstMagnetDirection.DataSource = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1101,6 +1108,13 @@ namespace AGVMapEditor.Forms
|
||||
// 변경된 속성명 디버그 출력
|
||||
System.Diagnostics.Debug.WriteLine($"[PropertyGrid] 속성 변경됨: {e.ChangedItem.PropertyDescriptor.Name}");
|
||||
|
||||
// 🔥 MagnetDirectionInfo 변경 처리
|
||||
if (_propertyGrid.SelectedObject is MagnetDirectionInfo magInfo)
|
||||
{
|
||||
ApplyDirectionChange(magInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
// RFID 값 변경시 중복 검사
|
||||
if (e.ChangedItem.PropertyDescriptor.Name == "RFID")
|
||||
{
|
||||
@@ -1308,5 +1322,356 @@ namespace AGVMapEditor.Forms
|
||||
UpdateStatusBar($"모든 노드의 회전/교차 속성 활성화 완료");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 마그넷 방향 정보를 표현하는 클래스
|
||||
/// </summary>
|
||||
public class MagnetDirectionInfo
|
||||
{
|
||||
[JsonIgnore, Browsable(false)]
|
||||
public MapNode FromNode { get; set; }
|
||||
|
||||
[JsonIgnore, Browsable(false)]
|
||||
public MapNode ToNode { get; set; }
|
||||
|
||||
[Category("연결 정보")]
|
||||
[DisplayName("출발 노드")]
|
||||
[Description("출발 노드의 ID입니다.")]
|
||||
[ReadOnly(true)]
|
||||
public string FromNodeId => FromNode?.ID2 ?? "Unknown";
|
||||
|
||||
[Category("연결 정보")]
|
||||
[DisplayName("도착 노드")]
|
||||
[Description("도착 노드의 ID입니다.")]
|
||||
[ReadOnly(true)]
|
||||
public string ToNodeId => ToNode?.ID2 ?? "Unknown";
|
||||
|
||||
[Category("설정")]
|
||||
[DisplayName("방향")]
|
||||
[Description("이동할 마그넷 방향입니다.")]
|
||||
public MagnetPosition? Direction { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string dirStr = Direction.HasValue ? Direction.Value.ToString() : "None";
|
||||
|
||||
string fromStr = FromNode != null ? FromNode.ID2 : "Unknown";
|
||||
string toStr = ToNode != null ? ToNode.ID2 : "Unknown";
|
||||
|
||||
return $"{fromStr} -> {toStr} : {dirStr}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void RefreshMagnetDirectionList()
|
||||
{
|
||||
// 현재 선택된 항목 기억
|
||||
int selectedIndex = lstMagnetDirection.SelectedIndex;
|
||||
|
||||
// 데이터 소스 초기화 (UI 갱신 강제)
|
||||
lstMagnetDirection.DataSource = null;
|
||||
lstMagnetDirection.Items.Clear();
|
||||
|
||||
if (this._mapCanvas.Nodes == null) return;
|
||||
|
||||
var directions = new List<MagnetDirectionInfo>();
|
||||
|
||||
// 모든 노드 검색
|
||||
foreach (var nodeItem in this._mapCanvas.Nodes)
|
||||
{
|
||||
if (nodeItem is MapNode node)
|
||||
{
|
||||
if (node.MagnetDirections != null && node.MagnetDirections.Count > 0)
|
||||
{
|
||||
foreach (var kvp in node.MagnetDirections)
|
||||
{
|
||||
var neighborId = kvp.Key;
|
||||
var dir = kvp.Value;
|
||||
var neighbor = this._mapCanvas.Nodes.FirstOrDefault(t => t.Id == neighborId);
|
||||
|
||||
directions.Add(new MagnetDirectionInfo
|
||||
{
|
||||
FromNode = node,
|
||||
ToNode = neighbor,
|
||||
Direction = dir
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 보기 좋게 정렬 (FromNode ID 순)
|
||||
directions.Sort((a, b) => string.Compare(a.FromNode.Id, b.FromNode.Id));
|
||||
|
||||
if (directions.Count > 0)
|
||||
{
|
||||
lstMagnetDirection.DataSource = directions;
|
||||
}
|
||||
|
||||
// 이벤트 연결
|
||||
lstMagnetDirection.SelectedIndexChanged -= LstMagnetDirection_SelectedIndexChanged;
|
||||
lstMagnetDirection.SelectedIndexChanged += LstMagnetDirection_SelectedIndexChanged;
|
||||
|
||||
lstMagnetDirection.DoubleClick -= LstMagnetDirection_DoubleClick;
|
||||
lstMagnetDirection.DoubleClick += LstMagnetDirection_DoubleClick;
|
||||
|
||||
// 선택 항목 복원 (가능한 경우) -> 선택된 객체가 다르게 생성되므로 인덱스로 복원 시도
|
||||
if (selectedIndex >= 0 && selectedIndex < lstMagnetDirection.Items.Count)
|
||||
{
|
||||
lstMagnetDirection.SelectedIndex = selectedIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private void LstMagnetDirection_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (lstMagnetDirection.SelectedItem is MagnetDirectionInfo info)
|
||||
{
|
||||
// 버튼 상태 업데이트
|
||||
UpdateDirectionButtons(info);
|
||||
}
|
||||
}
|
||||
|
||||
private void LstMagnetDirection_DoubleClick(object sender, EventArgs e)
|
||||
{
|
||||
if (lstMagnetDirection.SelectedItem is MagnetDirectionInfo info)
|
||||
{
|
||||
var node = info.FromNode;
|
||||
if (node != null)
|
||||
{
|
||||
// 방향 순환
|
||||
if (info.Direction == MagnetPosition.S) info.Direction = MagnetPosition.L;
|
||||
else if (info.Direction == MagnetPosition.L) info.Direction = MagnetPosition.R;
|
||||
else if (info.Direction == MagnetPosition.R) info.Direction = MagnetPosition.S;
|
||||
|
||||
ApplyDirectionChange(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyDirectionChange(MagnetDirectionInfo info)
|
||||
{
|
||||
var node = info.FromNode;
|
||||
if (node == null) return;
|
||||
|
||||
// 딕셔너리 업데이트
|
||||
if (node.MagnetDirections == null)
|
||||
node.MagnetDirections = new Dictionary<string, MagnetPosition>();
|
||||
|
||||
if (info.ToNode != null)
|
||||
{
|
||||
if (info.Direction == null)
|
||||
{
|
||||
if (node.MagnetDirections.ContainsKey(info.ToNode.Id))
|
||||
node.MagnetDirections.Remove(info.ToNode.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
node.MagnetDirections[info.ToNode.Id] = info.Direction.Value;
|
||||
}
|
||||
|
||||
_hasChanges = true;
|
||||
UpdateTitle();
|
||||
|
||||
// 리스트 갱신
|
||||
RefreshMagnetDirectionList();
|
||||
|
||||
// 캔버스 등 갱신
|
||||
_mapCanvas.Invalidate();
|
||||
|
||||
// 속성창 갱신 (선택된 객체가 바뀌었을 수 있으므로 다시 설정은 RefreshMagnetDirectionList의 선택 복원에서 처리됨)
|
||||
_propertyGrid.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
private void btMakeDirdata_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void toolStripButton3_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (this._mapCanvas.Nodes == null || this._mapCanvas.Nodes.Count == 0)
|
||||
return;
|
||||
|
||||
// 기존 목록을 모두 지울지 물어보고
|
||||
var result = MessageBox.Show(
|
||||
"마그넷방향을 자동 생성할까요? 없는 부분만 추가됩니다.",
|
||||
"마그넷 방향 자동 생성",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Question);
|
||||
|
||||
if (result != DialogResult.Yes)
|
||||
return;
|
||||
|
||||
bool clearAll = false;// (result == DialogResult.Yes);
|
||||
|
||||
int updateCount = 0;
|
||||
|
||||
foreach (var node in this._mapCanvas.Nodes)
|
||||
{
|
||||
// 연결 노드가 3개 이상인 노드들을 찾아서
|
||||
if (node.Type == NodeType.Normal && node is MapNode mapNode)
|
||||
{
|
||||
if (clearAll)
|
||||
{
|
||||
if (mapNode.MagnetDirections != null)
|
||||
mapNode.MagnetDirections.Clear();
|
||||
else mapNode.MagnetDirections = new Dictionary<string, MagnetPosition>();
|
||||
}
|
||||
|
||||
if (mapNode.ConnectedNodes.Count >= 3)
|
||||
{
|
||||
// 마그넷 방향 딕셔너리가 없으면 생성
|
||||
if (mapNode.MagnetDirections == null)
|
||||
mapNode.MagnetDirections = new Dictionary<string, MagnetPosition>();
|
||||
|
||||
foreach (var connectedId in mapNode.ConnectedNodes)
|
||||
{
|
||||
// 이미 설정된 경우 건너뜀 (모두 초기화 안 한 경우)
|
||||
if (!clearAll && mapNode.MagnetDirections.ContainsKey(connectedId))
|
||||
continue;
|
||||
|
||||
// 모두 자동 생성을 해준다 (기본은 직진으로)
|
||||
mapNode.MagnetDirections[connectedId] = MagnetPosition.S;
|
||||
updateCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updateCount > 0)
|
||||
{
|
||||
_hasChanges = true;
|
||||
UpdateTitle();
|
||||
|
||||
// 현재 선택된 노드의 속성창 및 리스트 갱신
|
||||
UpdateNodeProperties();
|
||||
|
||||
MessageBox.Show($"총 {updateCount}개의 연결에 대해 마그넷 방향(직진)이 설정되었습니다.", "완료", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("변경된 사항이 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
}
|
||||
|
||||
private void btDirDelete_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (lstMagnetDirection.SelectedItem is MagnetDirectionInfo info)
|
||||
{
|
||||
// 선택된 방향정보를 삭제한다.
|
||||
var result = MessageBox.Show(
|
||||
$"선택한 마그넷 방향 정보를 삭제하시겠습니까?\n{info.FromNodeId} -> {info.ToNodeId} : {info.Direction}",
|
||||
"삭제 확인",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Question);
|
||||
|
||||
if (result == DialogResult.Yes)
|
||||
{
|
||||
if (info.FromNode != null && info.FromNode.MagnetDirections != null)
|
||||
{
|
||||
if (info.ToNode != null && info.FromNode.MagnetDirections.ContainsKey(info.ToNode.Id))
|
||||
{
|
||||
info.FromNode.MagnetDirections.Remove(info.ToNode.Id);
|
||||
|
||||
_hasChanges = true;
|
||||
UpdateTitle();
|
||||
|
||||
// 리스트 및 UI 갱신
|
||||
RefreshMagnetDirectionList();
|
||||
|
||||
// 캔버스 갱신
|
||||
_mapCanvas.Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("삭제할 항목을 선택해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
//set left
|
||||
if (lstMagnetDirection.SelectedItem is MagnetDirectionInfo info)
|
||||
{
|
||||
info.Direction = MagnetPosition.L;
|
||||
ApplyDirectionChange(info);
|
||||
|
||||
UpdateDirectionButtons(info);
|
||||
}
|
||||
}
|
||||
|
||||
private void button2_Click(object sender, EventArgs e)
|
||||
{
|
||||
//set straight
|
||||
if (lstMagnetDirection.SelectedItem is MagnetDirectionInfo info)
|
||||
{
|
||||
info.Direction = MagnetPosition.S;
|
||||
ApplyDirectionChange(info);
|
||||
|
||||
UpdateDirectionButtons(info);
|
||||
}
|
||||
}
|
||||
|
||||
private void button3_Click(object sender, EventArgs e)
|
||||
{
|
||||
//set right
|
||||
if (lstMagnetDirection.SelectedItem is MagnetDirectionInfo info)
|
||||
{
|
||||
info.Direction = MagnetPosition.R;
|
||||
ApplyDirectionChange(info);
|
||||
|
||||
UpdateDirectionButtons(info);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateDirectionButtons(MagnetDirectionInfo info)
|
||||
{
|
||||
button1.BackColor = SystemColors.Control;
|
||||
button2.BackColor = SystemColors.Control;
|
||||
button3.BackColor = SystemColors.Control;
|
||||
|
||||
if (info.Direction == MagnetPosition.L) button1.BackColor = Color.Lime;
|
||||
else if (info.Direction == MagnetPosition.S) button2.BackColor = Color.Lime;
|
||||
else if (info.Direction == MagnetPosition.R) button3.BackColor = Color.Lime;
|
||||
}
|
||||
|
||||
private void toolStripButton2_Click(object sender, EventArgs e)
|
||||
{
|
||||
var result = MessageBox.Show(
|
||||
"기존 설정된 마그넷 방향 정보를 모두 초기화하시겠습니까?",
|
||||
"마그넷 방향 일괄 삭제",
|
||||
MessageBoxButtons.YesNoCancel,
|
||||
MessageBoxIcon.Question);
|
||||
|
||||
if (result == DialogResult.Cancel)
|
||||
return;
|
||||
|
||||
bool clearAll = (result == DialogResult.Yes);
|
||||
|
||||
int updateCount = 0;
|
||||
|
||||
foreach (var node in this._mapCanvas.Nodes)
|
||||
{
|
||||
// 연결 노드가 3개 이상인 노드들을 찾아서
|
||||
if (node.Type == NodeType.Normal && node is MapNode mapNode)
|
||||
{
|
||||
if (clearAll)
|
||||
{
|
||||
if (mapNode.MagnetDirections != null)
|
||||
mapNode.MagnetDirections.Clear();
|
||||
else mapNode.MagnetDirections = new Dictionary<string, MagnetPosition>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 현재 선택된 노드의 속성창 및 리스트 갱신
|
||||
UpdateNodeProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,6 +137,54 @@
|
||||
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<metadata name="toolStrip4.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 56</value>
|
||||
</metadata>
|
||||
<data name="btDirDelete.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<data name="btMakeDirdata.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<data name="toolStripButton2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<metadata name="toolStrip3.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
@@ -146,14 +194,14 @@
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHrSURBVDhPldBLaBNBHMfx/0kUVBBJ0lxWPIhihBJKyAqS
|
||||
pHkQIS+9hXg3RhQviicrITnmJqFnQQ8RqiamRqkhj6VCQtuIQaVQcxdq3d3s61Dy0w002KnU9nP67+x8
|
||||
pHkQIS+9hXg3RhQviicrITnmJqFnQQ8RqiamRqkhj6VCQtuIQaVQc5di3d3s61Dy0w002KnU9nP67+x8
|
||||
h2GIDmD0kT+mLk/fZNf3pQkznCrM3DFnZflSRG05euast7izcpM72GGqMP1ZFRw1tXm+qq9dg9LiHgwb
|
||||
dnFYP51i9/6T0r4wp39Kwfh2F8bGI2irEYjvTmo/Gpbj7N4JpXNxShUcdbV1DvpaHMb3HNrP4uiVb2Cj
|
||||
cQtadxbSh6OQ3tM82+6iNLk5rXcd7ecJGIaB0WiE1dcp6F9v41eNvmxV6QzbTMjtKYtct9Wi0Si63S50
|
||||
XUe/30fjaQTG+n1IVRpKb4lnuzFtyc4Nl06VE4kE0uk0CoUCSqUSqvOzMNYfYnORtqVFWhEr9JhtJ+Lx
|
||||
+DjmeR5+vx+xWAzqSgRy3Q65dgJbFeLYZmIndrvd8Pl8sFqt5pWfbL6hbalCl6Uy9cSXlGG7sWQyiXw+
|
||||
P469Xi8sFgvMdblCV6RXVDNnvKAjPxfoKttSOBxGLpfbE+8QFyj09/cugUAA2WwWLpcLHo9nT7yvTCaD
|
||||
wWAAp9OJUCh0uNhkHtDpdFAsFscPxv7/r2AweM+8ts1mO3z8x29OYwsb4/6fnQAAAABJRU5ErkJggg==
|
||||
wWAAp9OJUCh0uNhkHtDpdFAsFscPxv7/r2AweM+8ts1mO3z8x29KrQsZMgRtMAAAAABJRU5ErkJggg==
|
||||
</value>
|
||||
</data>
|
||||
<data name="btnMove.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
@@ -174,75 +222,75 @@
|
||||
<data name="btnAddLabel.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHISURBVDhPnZJfa9pgFMb7JXa7sW9RkH2y3awro5ddS3Et
|
||||
u2vHRsRcBEw0iEHiYpSg+Hcm0blUtOocdOCWbEZ5xnnlTXFule2BEN7znuf3HJKz12630Wq10Gg0UK/X
|
||||
UavVUK1WUalU6P1gb5eazSZWq9XWMxwOGUTX9fshlEyGwWDAkj3PiyBUsyzrfgiNTM2URudyuVyZz+cI
|
||||
w5DVCWia5t8hlMobDcP4aRjGIAgCfL75DkOdsrt+v49CofBnCCXzkZfLJcj8ZRrg1aGLk6cO3mfWkF6v
|
||||
h3w+D0VRNiGWZfmz2Yw1LRYLlhx/7uLyuI+3Zx7OD7sRxHVd5HK5TUipVHpimqY/na6b7PotS746+QTh
|
||||
/HoL4jgOVFXdhBSLxZimaf5kMmFN3dZXxA8cvDm9g8QPXOjK+r7T6UCWZQiCcAfRdT2WzWb98Xi8BXkX
|
||||
9/D6qIeLow8IwyWbIp1OI5lMPooAJE3TYplMxh+NRhHk7JmNixddXJ128W0eRGZBEB5vmLlUVWUQ2kT2
|
||||
4Zq3uHz5ET+CcLeZS5blWCqV8mkT+S8ms6Iou81ckiTtS5Lk04LZtv1vZi5RFPdFUfT/y8xFkEQi8fD3
|
||||
Otcvn84Wo7k6b1AAAAAASUVORK5CYII=
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHJSURBVDhPnZJfa9pgFMb7JXa7sW9RkH2y3awro5dbS7Et
|
||||
u2vHRsRcBEw0QkTinyhB8e9MonOZ6NRZaME12YzyjPPKm+LcKtsDIbznPc/vOSRnr91uo9VqodFooF6v
|
||||
o1aroVqtolKp0PvR3i41m02sVqutZzgcMoiu6w9DKJkMg8GAJbuuG0KoZprmwxAamZopjc7lcrkyn88R
|
||||
BAGrE9AwjL9DKJU35vP5n4VCYeD7Pr59vUNBnbK7fr+PXC73Zwgl85GXyyXIfD31cXbo4Pi5jXxqDen1
|
||||
eshms1AUZRNimqY3m81Y02KxYMnRlw4uX/fx/tTF+WE3hDiOA03TNiGlUumZYRjedLpusuo3LPnq+DOE
|
||||
8y9bENu2oarqJqRYLEY0TfMmkwlr6rZuET2w8e7kHhI9cKAr6/tOpwNZliEIwj1E1/VIOp32xuPxFuRD
|
||||
1MXbox4ujj4iCJZsimQyiXg8/iQEkDKZTCSVSnmj0SiEnL6wcPGqi6uTLr7P/dAsCMLTDTOXqqoMQpvI
|
||||
PlzzBpdvPuGHH+w2c8myHEkkEh5tIv/FZFYUZbeZS5KkfUmSPFowy7L+zcwliuK+KIref5m5CBKLxR7/
|
||||
Xuf6BYuvFpozmyYBAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<data name="btnAddImage.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAG6SURBVDhPnZLditpQFIXnJXrb0rcYkD5YL4pMYS7bKaXM
|
||||
O0TMRUp+ETM2EqMERY3BH0TE0ap4UQaZE/TksIZzhpPBOjPSLgghe+/1rQ3ZZ3Eco9frodvtotPpoN1u
|
||||
o9Vqodls8vebs1OKogiMsaNnPp8LiOd5r0N4MjfMZjORPJ1OMwivhWH4OoSvzId5Gv9uNBrN7XYLSqmo
|
||||
c2AQBC9DeKoc9H1/5/v+LEkS0D+/QWNb9CaTCarV6vMQnixXTtMU3Mzu1kitC6Q/P4LGluiNx2NUKhWY
|
||||
pnkICcOQbDYbMbTf70VyaubB3C9gv76DWRcZZDQaoVwuH0Lq9fqHIAjIer1+hNy2RTK7uQKrXh9BhsMh
|
||||
HMc5hNRqtZzrumS1Wj1CFhGokQe7+fYEMfOgkS76/X4fhmFAUZQniOd5uVKpRJbL5THE+wHmXOLeuARL
|
||||
qdjCsiwUi8V3GYDLdd2cbdtksVhkkL3+Ccz+jKR8hR3ZZmZFUd4fmKUcxxEQfokCMu+Cul/Bdslps5Rh
|
||||
GDld1wm/RPmLudk0zdNmKU3TzjVNI/zABoPBv5mlVFU9V1WV/JdZikMKhcLbv+tSD5T6HZWMaVplAAAA
|
||||
AElFTkSuQmCC
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAG7SURBVDhPnZLditpQFIXnJXrb0rcYkD5YL4pMYS7bGUrp
|
||||
O0TMRUp+hWRs/IkSFDUGfxARR6viRRmkJ+jJYZVzhpPB2o60C0LI3nt9a0P2RRzH6Pf76PV66Ha76HQ6
|
||||
aLfbaLVa/P3i4pyiKAJj7ORZLBYC4vv+8xCezA3z+Vwkz2azDMJrYRg+D+Er82Gexr+bzWZrt9uBUirq
|
||||
HBgEwd8hPFUOVqvVfa1WmydJAvrjO2hsi950OkWlUvkzhCfLldM0BTezhw1S6wrp17egsSV6k8kE5XIZ
|
||||
pmkeQ8IwJNvtVgwdDgeRnJp5MO8D2LdPYNZVBhmPx3Bd9xjSaDTeBEFANpvNI+S+I5LZ3Q1Y5csJZDQa
|
||||
wXGcY0i9Xs+5rkvW6/UjZBmBGnmwu9sniJkHjXTRHwwGMAwDiqI8QXzfz5VKJbJarU4h/mcw5xo/jWuw
|
||||
lIotLMtCsVh8lQG4PM/L2bZNlstlBjno78Ds90jcG+zJLjMrivL6yCzlOI6A8EsUkEUP1PsItk/Om6UM
|
||||
w8jpuk74JcpfzM2maZ43S2madqlpGuEHNhwO/80sparqpaqq5L/MUhxSKBRe/l6X+gWA2x2MFEPZrwAA
|
||||
AABJRU5ErkJggg==
|
||||
</value>
|
||||
</data>
|
||||
<data name="btnAddNode.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHdSURBVDhPnZLda9NgFMb3T3ir+F8Min/PbmQO0VvdUEQF
|
||||
nV93bk7xKqXZzNqkzbrGmJKmXUhpm9Z+UEpJW9tSULT6lvWDR94X3kicrugDIeSc8/yeAzkrlUoF5XIZ
|
||||
pVIJxWIRhUIB+XwejuPQ94WVZXJdF4vF4szT7XYZxDCM8yE0mRo8z2PJ7Xbbh9CabdvnQ+jKdJim0e9c
|
||||
LueMx2PMZjNWp0DLsv4Ooal80DTNU9M0vclkguGXDj64B6zXarWQTqf/DKHJfOX5fA5qHn39hCfRDTzY
|
||||
X4Pu7rNes9mErutQFCUIsW2bjEYjNjSdTjH43MF29BpeJm9hT7uDp9ENH9JoNJBKpYKQbDZ7xbIsMhwO
|
||||
2dBH7wQPD9awe7yJt/p9vNbuBiD1eh2qqgYhmUwmpGkaGQwGbKjWyeOxtI5Xx1s+ZPtwHalimPWr1Spk
|
||||
WYYgCL8ghmGEkskk6ff7ZyBv3t/DC+UGnh/exGw+ZVvE43FEIpFLPoBK07RQIpEgvV7Phzx6dxXPYtex
|
||||
c7SF7z+++WZBEC4HzFyqqjIIvUS2rudg5+g2JqdkuZlLluVQLBYj9BL5L6ZmRVGWm7kkSVqVJInQA6vV
|
||||
av9m5hJFcVUURfJfZi4KCYfDF3+vc/0ED18PUDextaQAAAAASUVORK5CYII=
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHdSURBVDhPnZLra9NgFMb3T/hV8b8YFP+efZE5RL+qQxEV
|
||||
dN6+OXfBTynNZtYmbeKSxfSS1pDSNq29UEpJW9tSULT6lvXCI+8LicTpij4QQs45z+85kLNSqVRQLpdR
|
||||
KpVQLBZRKBSQz+dh2zZ9X1hZJsdxsFgszjzdbpdBDMM4H0KTqcF1XZbcbrd9CK1ZlnU+hK5Mh2ka/c7l
|
||||
cvZ4PMZsNmN1CjRN8+8QmuoNplKp03Q67U4mEwy/dPDeOWS9VquFZDL5ZwhN9laez+eg5tHXT3ga3cDD
|
||||
gzXozgHrNZtN6LoOSZKCEMuyyGg0YkPT6RSDzx1sRa/h1btb2NXu4ll0w4c0Gg2oqhqEZLPZK6ZpkuFw
|
||||
yIY+uh/w6HANr4/v4I3+AHvavQCkXq9DluUgJJPJhFRVJYPBgA3VOnk8Edaxc7zpQ7aO1qEWw6xfrVYh
|
||||
iiI4jvsFMQwjpCgK6ff7ZyD7J/fxUrqBF0c3MZtP2RbxeByRSOSSD6DSNC2USCRIr9fzIY/fXsXz2HVs
|
||||
K5v4/uObb+Y47nLA7EmWZQahl8jWdW1sK7cxOSXLzZ5EUQzFYjFCL9H7xdQsSdJysydBEFYFQSD0wGq1
|
||||
2r+ZPfE8v8rzPPkvsycKCYfDF3+ve/oJ+zEPR++RdtEAAAAASUVORK5CYII=
|
||||
</value>
|
||||
</data>
|
||||
<data name="btnDelete.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGvSURBVDhP7Y89T1phGIZpgR/QqTiQ6NDZP+BgCiZd2jA0
|
||||
sUNNBx0cjIMDtB3EycGGxY2Evmym7eBGhfgVeuB8cM7hcIDQGKSU7+NBYWlrEw23ed+oMUdr/QHeybW8
|
||||
ea77fR6b7T4s0WjUQwgRCSG4I5VIJPLisoAQUlJVAc1mmdFqUfYZ7XaF0en8gGFUYRg/USiotKRxtcCs
|
||||
Vkvg+RSSySSD4zikUimk02nwPA9RFCFJElRVZSXhcPjYUvAdkiQw0SplMhkoisJkXc/BNBvXC2q1PSiK
|
||||
DEEQmEglWZaZlM1mkcvloOs6isUCut3W9YJ6vQxNU5lEf6OSpmlMzOfzUMKL+Db1BFseJ3ZfubE2//Lk
|
||||
QsbOziYOD9s4OqJ00OsZ6PcPzjFR/vwBef8YjmMhDEpx/P60AHluFF8Cr8EK/kfM58KfWAhYfQ4EHgHL
|
||||
I+iujOOr7zEuz7gtG08dg4G6jqvpB12g79bZG5Pw2hu/Pk4DQRf++m3o+W2ozdoRn7A3rbM3hpscWpLe
|
||||
DJ+0AyMw3zlRmXmA7WeO0w3vw/fW2X+Gm3S/TXjsVbo23ehCPgMKrqo38mZYEwAAAABJRU5ErkJggg==
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVDhP7Y+/TxphGMex4B/QqXQgxqGz/0CHRmw6aRhM
|
||||
dGjToQ4OujiAdqidOrRhccIEXyYT26EbFmLV4MEdx91xHBCahiLy+zwUFn80oeFr3jdCyKm1f0C/yWd5
|
||||
83y+7/NYLP/DEggEnISQOCEE/0jB7/dP9QsIITlFEVCt5hm1GuUXo14vMBqNQ+h6Ebp+hExGoSWVwQKj
|
||||
WMyB56OIRCIMjuMQjUYRi8XA8zzi8ThEUYSiKKzE5/Ndmgp+QBQFJpqlRCIBWZaZrGkpGEblZkGp9BOy
|
||||
LEEQBCZSSZIkJiWTSaRSKWiahmw2g2azdrOgXM5DVRUm0d+opKoqE9PpNOT1dzh49QTfncPYn3Vgc3G6
|
||||
05Oxt7eDk5M6Tk8pDbRaOtrt42sM5D9/Qtr9FJdBL7q5EM63liAtjOGL5yVYwX0EXXZcBL3A2iTgeQh8
|
||||
GEXz4zNsux6hf8bfsj1u63aVrxhMe9UO+m6evTXhCWvlbOMNsGrHb7cFLbcFpXkrQs+tVfPsreFmHr8X
|
||||
X4906p5RGCvDKMwNYfeF7c+3iQdvzbN3hptxLIed1iJdm27Uk68A8qiqJzQDmt8AAAAASUVORK5CYII=
|
||||
</value>
|
||||
</data>
|
||||
<data name="btnEditImage.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAL4SURBVDhPfdLxU9N1HMdx/oN+7/qp7vzF6+qX7vqts9K0
|
||||
vDKzjvIcepx1GZ3XpQ1HJGL6FRbjKwOBDfg2CKMNJRkChps6JB34hcEWWmGIgIODue/23faRI3x229US
|
||||
znrdvX/6vD+Pz93n/c5R3MMVSrs6J7tUzdL8mHKsLtmpavazamXOP2noHJy/Fw4/1BMJEknxv7WoC+aj
|
||||
cWrb/NEskH45rieo6ZqmoitAyfkfcNyw4hz9mtOBUqp9Zj5usvF5Yz/GxnEW4gLZNaJlgcpmVUvrteen
|
||||
kXp6cQxZaRs7gH3oI2qv51Pn34e57yj76lsxKTeZ1x4DxJOC+p4ZSrpb+D5QSp1/Lyd/3oXZ9z7SlVws
|
||||
vs/Iq/qG4uZfmdNSyK7h1YCWFNh7ZznsdvCtWow8YEC6/B5HvNv56uJ2JE8BBrkMpa2FW625/H7uGIHG
|
||||
TQRq1hsyQDQhaLxwD8ndjdV3gsqBfEq872D66S0Oe3L58sdCapoPEvYa0ca6QJ8lOtaOr3zzXAa4rwuU
|
||||
vjDW7lEKnQpS537KPXuR+vZQ5PoES1MBC6ESlqY7CV82kwqe5c87VxmxfShy0rOOxAUOz1wGqXIH+KLU
|
||||
wHHLq0jmDTRUbSYSKmYldZXU7QIig59ysyWfgC130W/dti4DLMYEJuebGNu2cuD069iKXiCo7CD03Ubu
|
||||
Bw+xIq6RnMhD3N1JfNxE6NTWhyOVG5/PfGKF44YWiQl8E5eyNRDsoP/cfsJDxr8vGxBTHxALFXG7/g0U
|
||||
5YyenYIlA6RWAd5LZYxfOMhKyoeYzOfB3V1owUP8VvcaM1OTyM5HxljtUqci0RgxXRBLPGDE28Iv3SZG
|
||||
e2Rm/O+SnNxJJFDIRMPbLM7+wUJmlQf/XWV7h1puax/uTy9HWnaWvQLxO9yy7cBrepKBui14jr+00qS4
|
||||
9fR51Zlhzd6hyllgbY7uXre8dK2aJf9J/Mc20Jr3lN5rXP/c2r7/zLYXnxBS3jPLvSe2LPdVvHylq/DZ
|
||||
p9f2PJq/AD40i0VffXQ/AAAAAElFTkSuQmCC
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAL0SURBVDhPhdLdT1N3HMdx/oPdL16ZbDfGuJtll4vbfNrM
|
||||
3JxzOGMbQ3SZYzHLdMUyJuL0CB3lQCFAC5xVfGCtyqQIOGzVItOKBwpFfBgOkedA7WlP258E8b202Tol
|
||||
e/gk36vf9/f6Jb/vN0vx9JYqbnVadqua1fn/JbtUzXFOLcv6K3UtN2cmJief6fE48YT4z5rTBTORGNWn
|
||||
b0QyQOrlmB6nqnWM0tYghRd+wnnLhqv/e04Fi6j0W/i8wc7X9V2Y6oeYjQlkd5+WAcqcqpbSqy+MIbV3
|
||||
4Oyx0TSwD0fPZ1TfyKEmsAdL52H21J7ErNxhRvsHIJYQ1LaPU9jWyOlgETWBXZT/ugOLfxvS1Wys/q8w
|
||||
VvxAwfF7TGtJZHfvi4CWEDg6JjjocfKjWoDcbUC6spVDvs18d2kzkjcXg1yM0tTI3ZPZ/Hb+CMH6tQSr
|
||||
VhjSQCQuqL84ieRpw+Y/Rll3DoW+jzD/somD3my+/TmPquP7mfKZ0AZaQZ8gMnAGf8n66TTwWBconVPY
|
||||
2vrJcylILXsp8e5C6txJvvsLrA25zA4WMj/WwtQVC8nQOZ4+vEZf7W6RlZptOCZweqfTSIUnyDdFBo5a
|
||||
30GyrKauYj3hwQIWk9dIPsglfPNL7jTmELR/MhewffBqGpiLCsyu9zE1bWTfqXex579OSNnC4Ik1PA4d
|
||||
YFFcJzFsRDzaTmzITKhq47O+sjWvpT+x1HlLC0cF/uHLmeoONdN1fi9TPaY/LxsQo58SHcznQe17KMpZ
|
||||
PTMFaxpIvgD4LhczdHE/i0k/YiSHJ492oIUOcL9mHeOjI8iu58ZY6VZHw5EoUV0QjT+hz9fI7TYz/e0y
|
||||
44GPSYxsJxzMY7juQ+Ymfmc2vcqBv1fZ0ayW2M/0dqWWIyW7it+G2EPu2rfgM79Md80GvEffXGxQPHrq
|
||||
vPxsr+ZoVuUMsDSHja8szF+vZD5QTuDIak4Yl+kdphWrlvb9aza98ZKQjMsX2o9tWOgsfetqa97K5Ut7
|
||||
ns8f9tyLJQW2uh8AAAAASUVORK5CYII=
|
||||
</value>
|
||||
</data>
|
||||
<data name="btnConnect.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
@@ -263,16 +311,16 @@
|
||||
<data name="btnDeleteConnection.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHxSURBVDhP7Y7Ni1JhGMVvXadgWrWzRdQsU5BcRW4cR72E
|
||||
H8FtoS6iFinWKwn2MaPdheJikIEJvGCuElpcaKOtTKUMdNxEZBut1Z1BipAiDZrSJvPEM5CYOP9BB154
|
||||
OOd3Di/H/ddEdrv9GGPsbiKR6OVyOUiS1GGMnWOMaSVJUsmjjBhi/ynrdLojTqdzI51Oo9vtQlVV1Go1
|
||||
xOPxbUmS6q1Wa0heo9EAMcRSZzJgNBovhsPhr+12u+fz+Z4KgjAKBAKQZRmxWAx0k0eZoigdYqkzPbCR
|
||||
TCYHsiwHXS7Xoslk+latVtFsNlGv15HP50EeZcQQS53JgMFguBEMBvuZTOaOKIoVi8WyJwgCRFHcf3ST
|
||||
RxkxxFJnMqDX68+43e53qVSqryjKl0KhgGw2i0gkgmg0un+X1xleXDr1+/nKAp4Ii3uPlo/enwxwHMeb
|
||||
zeazDodjx+/374ZCoQFj7KfH47ni9XqtDy5bhm9un8eguInx2zK+P76FVyHd6JlVc3N65ECVbLz6o7gJ
|
||||
yG5g7TiwvoTPG8soWvjtWXauSiua8fh1AdPqx7Ugf5adq4qNf7/78BoQ12K4yqG3yqFznUfZzn+YZedq
|
||||
y3si8fLq6V8f15bwKbYA1X8I1QuaUcl2+N4se6C2vCejFSu/Q9+mH/0t/wFGlxos/Pd5kgAAAABJRU5E
|
||||
rkJggg==
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHvSURBVDhP7Y7Ni1JhGMVvXadgWrWzRQyzTEFyFa7U0Wvg
|
||||
R3A3KgS1SLFeSbCPGe0uHFyMIEzgBXOVECi00VbOKGWg4yYiW4i1ujNIEVKkQVPaZJ7hGUhMnP+gAy88
|
||||
nPM7h5fj/msiQRDOMMbux2KxXjabhSRJHcbYJcaYWpIkhTzKiCH2n7JGoznlcDiSqVQK3W4XiqKgVqtR
|
||||
YVeSpHqr1RqS12g0QAyx1JkM6PX6K6FQ6Fu73e55vd4tQRBGfr8fsiwjGo2CbvIoy+fzHWKpMz2QjMfj
|
||||
A1mWA06nc9FgMHyvVqtoNpuo1+soFAogjzJiiKXOZECn090KBAL9dDp9TxTFitlsPrDZbBBF8ejRTR5l
|
||||
xBBLncmAVqu94HK53icSiX4ul/taLBaRyWQQDocRiUSO7vIGw0tx6c+LlQU8sy0ePDGdfjgZ4DiONxqN
|
||||
F+12+57P59sPBoMDxtgvt9t9zePxWB5dNQ3f3jVgUNrE+F0ZP57eweugZvTcoro9PXKstq288rO0Ccgu
|
||||
YO0ssLGML0kTSmZ+d5adq60V1Xj8pohp9WNqkD/LzlXFyn/Yf3wDiKkxXOXQW+XQucmjLPAfZ9m52vGc
|
||||
W391fen3p7VlfI4uQPGdQPWyarRtPflglj1WO57zkYqF36Nv04/+lg8BALcaCRX7gQ0AAAAASUVORK5C
|
||||
YII=
|
||||
</value>
|
||||
</data>
|
||||
<data name="btnToggleGrid.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
@@ -287,18 +335,18 @@
|
||||
<data name="btnFitMap.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAKYSURBVDhPhZBfSFNRHMfvY0RPEfQSFD2tniKh9WBZmM5W
|
||||
es02del1ii7LJAsjtG6pOCdZuiJLZg8TbFMzuiSKlm7m/LPU3XOWkUKF89xBK0ak51pBcDlx1L1cF33h
|
||||
vBy+n+/v+/sxzIZm3flasatAAF3m6Ey3WZl2c4rfyUX97TmCv/WsNuaLK+jmLPBpUUTsLACzHef4gNOk
|
||||
mXRkafztBt7vMIKJtpyIz55pUXNropPX4TybGML7wBI2ACRXQEkuD6KV9Dm0vNd3n7WN29mI965+cxPR
|
||||
XSDQyYEQ1gCELwKEq6GErwNp9RpE8lUoyXkw9GPPWAsLPI16Qc0zwG2Oznbk8sEwzgIIV63DcuUGXA4k
|
||||
XAaQfMLToOdfNaRH1Twz08UpAYdJAyX5UmxqbAXaCCJcSlsM1aRqvE16Rc0zb135Cj0YNVIQLMmXaZiI
|
||||
8IW1PwmXBJGcQwM8Nt3mgElnXtT/2MCL0mraegtctgaHsQVIuBiilUJRwkcHb6bwQzdSN68w1WYQplqN
|
||||
gB4KIjl7AyyB0koRhQHC7Lvwz139dSd/D1QlT6t5xmfP0E48yoy8aWFt4uKv3TAsH6eVIZKNQYQTKext
|
||||
zvwz11tBvJXHyIuKRJM6gxltyrD47GzEc+cUGK7X8XRf+gark/nRe+nKfH81CQccZHGimYzUs+RlsXan
|
||||
OoPx2vTa11a9MNSgi9JjjVhTlb7buuXhutPk02gj+fqhk3xbcJGPw7Wk13xEcccLiSfhShI31pJLQpN2
|
||||
EnnfQZb8D8n0kyJiS9F87jufsFXtj6vnZYnciPUMWRi4ReYHasj4g2zSW3rw+zPj/m1q7z/VYznEufIT
|
||||
yKvaNNJdqP3iMiXsUHv+q57iw9vbcw9YneakLbG/v5ifpNsR5bepAAAAAElFTkSuQmCC
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAKYSURBVDhPhZBfSFNRHMfvY0RPEfQSFD2tngqh9WBZmFOX
|
||||
ess2de2Pii7LJAsjHN1ScU4yc0WWzB4m2KZmdMkSV7qZ889tuHvuMlKocJ47aMWI9FwrCC4njrqX66Iv
|
||||
nJfD9/P9fX8/itrQrNek5vssLOgrTYT6LfJbr0mecZsTXHcxy3WeUSd9KSV4zVbhcXmc77WA2Z6zTNht
|
||||
UE27ClVct47hXHow1VUcDzpPWZXcmsjkddjo4KNoH1hCOgClWkGUaiJwJX8OLu8N3qUdk046Hrit3dyE
|
||||
91pYMjkcRSoA0QUAkU0Q0TUgrl4VoHRFECWjEP2xZ6KDBv5WLavkKeAtTcz2lDCRGCoEENWvw1LdBlwD
|
||||
RFQNoHTC36JlfC35CSVPhfpMcthlUAmidDE5NbkCaSRAVEVa+Bo0qkBbrqzkKc5jlMnBiJGAYEm6RMJ4
|
||||
iM6v/YmoMgKlYhLgd2g2B0y7jQnuoY7hxdWc9Raoeg2OISsQUYUAV8p4ER0duZ7F+GyazSvMdOnYmU49
|
||||
IIcSoFS0AVYK4ko5gQFE9LvYz10vmnJ/D9dnhpQ8FXQWqKce0PE3HbSDX/y1W4hJx0llAUr6CETpBPa3
|
||||
03/mBmtxoO4YflabblBmUONtBdagk477b50Eo83ZDNmXvBFbJjPenifPv7ThWNiFF6fu4LFmGj+vUO9U
|
||||
ZlABh1b92q5lfS2aBDnWmD1LHrqZvTzalIc/jbfirx968bcFD/442ogHLUdkb6qQVGIvZ5gnOkpwdNqJ
|
||||
4+978BJ3H4celWNHlurz0Lm0rUp/Sj2tTjeP2U/jheEbeH64AU/eK8KDVQe/P9Hv36b0/lMD1kNmjykN
|
||||
v2rMwf1l6i8eQ9oOpee/Gqg4vL275IDdXZqxJfn3F2EzpMPWLB83AAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<metadata name="toolStrip2.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
|
||||
@@ -72,6 +72,9 @@ namespace AGVNavigationCore.Controls
|
||||
DrawDragGhost(g);
|
||||
}
|
||||
|
||||
// 마그넷 방향 텍스트 그리기 (노드 위에 표시)
|
||||
DrawMagnetDirections(g);
|
||||
|
||||
// AGV 그리기
|
||||
DrawAGVs(g);
|
||||
|
||||
@@ -105,6 +108,105 @@ namespace AGVNavigationCore.Controls
|
||||
|
||||
}
|
||||
|
||||
private void DrawMagnetDirections(Graphics g)
|
||||
{
|
||||
if (_nodes == null) return;
|
||||
|
||||
using (var font = new Font("Arial", 8, FontStyle.Bold))
|
||||
using (var brushS = new SolidBrush(Color.Magenta))
|
||||
using (var brushL = new SolidBrush(Color.Green))
|
||||
using (var brushR = new SolidBrush(Color.Blue))
|
||||
using (var brushBg = new SolidBrush(Color.FromArgb(180, 255, 255, 255)))
|
||||
{
|
||||
foreach (var node in _nodes)
|
||||
{
|
||||
if (node.MagnetDirections != null && node.MagnetDirections.Count > 0)
|
||||
{
|
||||
foreach (var kvp in node.MagnetDirections)
|
||||
{
|
||||
var targetId = kvp.Key;
|
||||
var dir = kvp.Value;
|
||||
var targetNode = _nodes.FirstOrDefault(n => n.Id == targetId);
|
||||
|
||||
if (targetNode != null)
|
||||
{
|
||||
// 방향 텍스트 위치 계산 (출발 -> 도착 벡터의 일정 거리 지점)
|
||||
var start = node.Position;
|
||||
var end = targetNode.Position;
|
||||
var angle = Math.Atan2(end.Y - start.Y, end.X - start.X);
|
||||
|
||||
// 박스(텍스트) 중심 위치: 약 40px 거리
|
||||
var boxDist = 40;
|
||||
var boxX = start.X + boxDist * Math.Cos(angle);
|
||||
var boxY = start.Y + boxDist * Math.Sin(angle);
|
||||
|
||||
string text = dir.ToString();
|
||||
|
||||
Color color = Color.Blue;
|
||||
if (dir == MagnetPosition.L) color = Color.LimeGreen;
|
||||
else if (dir == MagnetPosition.R) color = Color.Red;
|
||||
|
||||
// 화살표 및 텍스트 설정
|
||||
using (var arrowBrush = new SolidBrush(color))
|
||||
using (var arrowPen = new Pen(color, 2)) // 두께 약간 증가
|
||||
using (var textBrush = new SolidBrush(color))
|
||||
{
|
||||
// 1. 화살표 그리기 (박스를 가로지르는 선)
|
||||
// 시작점: 노드 근처 (25px)
|
||||
// 끝점: 박스 너머 (55px)
|
||||
var arrowStartDist = 25;
|
||||
var arrowEndDist = 55;
|
||||
|
||||
var pStart = new PointF((float)(start.X + arrowStartDist * Math.Cos(angle)), (float)(start.Y + arrowStartDist * Math.Sin(angle)));
|
||||
var pEnd = new PointF((float)(start.X + arrowEndDist * Math.Cos(angle)), (float)(start.Y + arrowEndDist * Math.Sin(angle)));
|
||||
|
||||
// 화살표 선 그리기
|
||||
g.DrawLine(arrowPen, pStart, pEnd);
|
||||
|
||||
// 화살표 머리 그리기 (끝점에)
|
||||
var arrowSize = 6;
|
||||
var pHead1 = new PointF((float)(pEnd.X + arrowSize * Math.Cos(angle)), (float)(pEnd.Y + arrowSize * Math.Sin(angle))); // 뾰족한 끝
|
||||
// 삼각형 머리 (채우기)
|
||||
var pBackL = new PointF((float)(pEnd.X + arrowSize * Math.Cos(angle + 2.5)), (float)(pEnd.Y + arrowSize * Math.Sin(angle + 2.5)));
|
||||
var pBackR = new PointF((float)(pEnd.X + arrowSize * Math.Cos(angle - 2.5)), (float)(pEnd.Y + arrowSize * Math.Sin(angle - 2.5)));
|
||||
|
||||
// pHead1이 가장 먼 쪽이 되도록 조정 (pEnd가 삼각형의 뒷부분 중심이 되도록)
|
||||
// pEnd에서 시작해서 앞으로 나가는 삼각형
|
||||
// pTip = pEnd + size * angle
|
||||
// pBackL = pEnd + size/2 * angle_back_L (약간 뒤로)
|
||||
// 현재 코드는 pEnd를 중심으로, pHead1이 앞, pBackL/R이 뒤... 가 아니라
|
||||
// pHead1, pBackK, pBackR로 삼각형을 그림.
|
||||
// pHead1이 팁.
|
||||
g.FillPolygon(arrowBrush, new PointF[] { pHead1, pBackL, pBackR });
|
||||
|
||||
// 2. 텍스트 그리기 (화살표 위에 박스, 그 위에 텍스트)
|
||||
var textSize = g.MeasureString(text, font);
|
||||
var textPoint = new PointF((float)(boxX - textSize.Width / 2), (float)(boxY - textSize.Height / 2));
|
||||
|
||||
//편집모드에서만 글자를 표시한다.
|
||||
if(Mode == CanvasMode.Edit)
|
||||
{
|
||||
// 텍스트 배경 (반투명 - 선이 은은하게 보이도록 투명도 조절하거나, 가독성을 위해 불투명하게 처리)
|
||||
// 사용자가 "박스를 가로지르는" 느낌을 원했으므로 선이 보여야 함. 하지만 텍스트 가독성도 필요.
|
||||
// 배경을 아주 옅게 (Alpha 100정도) 처리하여 선이 보이게 함.
|
||||
using (var translucentBg = new SolidBrush(Color.FromArgb(120, 255, 255, 255)))
|
||||
{
|
||||
g.FillRectangle(translucentBg, textPoint.X - 1, textPoint.Y - 1, textSize.Width + 2, textSize.Height + 2);
|
||||
}
|
||||
g.DrawString(text, font, textBrush, textPoint);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawAlertMessage(Graphics g)
|
||||
{
|
||||
if (showalert == false) return;
|
||||
|
||||
@@ -40,6 +40,11 @@ namespace AGVNavigationCore.Models
|
||||
[Description("도킹/충전 노드의 진입 방향입니다.")]
|
||||
public DockingDirection DockDirection { get; set; } = DockingDirection.DontCare;
|
||||
|
||||
|
||||
[Category("노드 설정")]
|
||||
[Description("각 연결된 노드로 향할 때의 마그넷 방향 정보입니다.")]
|
||||
public Dictionary<string, MagnetPosition> MagnetDirections { get; set; } = new Dictionary<string, MagnetPosition>();
|
||||
|
||||
[Category("연결 정보")]
|
||||
[Description("연결된 노드 ID 목록입니다.")]
|
||||
[ReadOnly(true)]
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace AGVNavigationCore.PathFinding.Core
|
||||
/// <summary>
|
||||
/// 오류 메시지 (실패시)
|
||||
/// </summary>
|
||||
public string ErrorMessage { get; set; }
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 도킹 검증 결과
|
||||
@@ -116,7 +116,7 @@ namespace AGVNavigationCore.PathFinding.Core
|
||||
ExploredNodes = 0;
|
||||
EstimatedTimeSeconds = 0;
|
||||
RotationCount = 0;
|
||||
ErrorMessage = string.Empty;
|
||||
Message = string.Empty;
|
||||
PlanDescription = string.Empty;
|
||||
RequiredDirectionChange = false;
|
||||
DirectionChangeNode = string.Empty;
|
||||
@@ -149,36 +149,19 @@ namespace AGVNavigationCore.PathFinding.Core
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 실패 결과 생성
|
||||
/// </summary>
|
||||
/// <param name="errorMessage">오류 메시지</param>
|
||||
/// <param name="calculationTimeMs">계산 시간</param>
|
||||
/// <returns>실패 결과</returns>
|
||||
public static AGVPathResult CreateFailure(string errorMessage, long calculationTimeMs)
|
||||
{
|
||||
return new AGVPathResult
|
||||
{
|
||||
Success = false,
|
||||
ErrorMessage = errorMessage,
|
||||
CalculationTimeMs = calculationTimeMs
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 실패 결과 생성 (확장)
|
||||
/// </summary>
|
||||
/// <param name="errorMessage">오류 메시지</param>
|
||||
/// <param name="calculationTimeMs">계산 시간</param>
|
||||
/// <param name="exploredNodes">탐색된 노드 수</param>
|
||||
/// <returns>실패 결과</returns>
|
||||
public static AGVPathResult CreateFailure(string errorMessage, long calculationTimeMs, int exploredNodes)
|
||||
public static AGVPathResult CreateFailure(string errorMessage, long calculationTimeMs = 0, int exploredNodes = 0)
|
||||
{
|
||||
return new AGVPathResult
|
||||
{
|
||||
Success = false,
|
||||
ErrorMessage = errorMessage,
|
||||
Message = errorMessage,
|
||||
CalculationTimeMs = calculationTimeMs,
|
||||
ExploredNodes = exploredNodes
|
||||
};
|
||||
@@ -276,22 +259,6 @@ namespace AGVNavigationCore.PathFinding.Core
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 상세 경로 정보 반환
|
||||
/// </summary>
|
||||
/// <returns>상세 정보 문자열</returns>
|
||||
public string GetDetailedInfo()
|
||||
{
|
||||
if (!Success)
|
||||
{
|
||||
return $"경로 계산 실패: {ErrorMessage} (계산시간: {CalculationTimeMs}ms)";
|
||||
}
|
||||
|
||||
return $"경로: {Path.Count}개 노드, 거리: {TotalDistance:F1}px, " +
|
||||
$"회전: {RotationCount}회, 예상시간: {EstimatedTimeSeconds:F1}초, " +
|
||||
$"계산시간: {CalculationTimeMs}ms";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 경로의 노드 정보를 포함
|
||||
/// </summary>
|
||||
@@ -300,13 +267,14 @@ namespace AGVNavigationCore.PathFinding.Core
|
||||
{
|
||||
if (!Success)
|
||||
{
|
||||
return $"경로 계산 실패: {ErrorMessage} (계산시간: {CalculationTimeMs}ms)";
|
||||
return $"경로 계산 실패: {Message} (계산시간: {CalculationTimeMs}ms)";
|
||||
}
|
||||
|
||||
var data = DetailedPath.Select(t => {
|
||||
return $"{t.RfidId}[{t.NodeId}] {t.MotorDirection.ToString().Substring(0,1)}-{t.MagnetDirection.ToString().Substring(0,1)}";
|
||||
var data = DetailedPath.Select(t =>
|
||||
{
|
||||
return $"{t.RfidId}[{t.NodeId}] {t.MotorDirection.ToString().Substring(0, 1)}-{t.MagnetDirection.ToString().Substring(0, 1)}";
|
||||
});
|
||||
return string.Join(" → ",data);
|
||||
return string.Join(" → ", data);
|
||||
}
|
||||
|
||||
|
||||
@@ -334,7 +302,7 @@ namespace AGVNavigationCore.PathFinding.Core
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"Failed: {ErrorMessage}";
|
||||
return $"Failed: {Message}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,12 +351,12 @@ namespace AGVNavigationCore.PathFinding.Core
|
||||
|
||||
if (!previousResult.Success)
|
||||
return AGVPathResult.CreateFailure(
|
||||
$"이전 경로 결과 실패: {previousResult.ErrorMessage}",
|
||||
$"이전 경로 결과 실패: {previousResult.Message}",
|
||||
previousResult.CalculationTimeMs);
|
||||
|
||||
if (!currentResult.Success)
|
||||
return AGVPathResult.CreateFailure(
|
||||
$"현재 경로 결과 실패: {currentResult.ErrorMessage}",
|
||||
$"현재 경로 결과 실패: {currentResult.Message}",
|
||||
currentResult.CalculationTimeMs);
|
||||
|
||||
// 경로가 비어있는 경우 처리
|
||||
|
||||
@@ -138,8 +138,9 @@ namespace AGVNavigationCore.PathFinding.Planning
|
||||
pathResult.PrevDirection = prevDirection;
|
||||
|
||||
if (!pathResult.Success)
|
||||
return AGVPathResult.CreateFailure(pathResult.ErrorMessage ?? "경로 없음", 0, 0);
|
||||
return AGVPathResult.CreateFailure(pathResult.Message ?? "경로 없음", 0, 0);
|
||||
|
||||
// 3. 상세 데이터 생성 (갈림길 마그넷 방향 계산 포함)
|
||||
// 3. 상세 데이터 생성 (갈림길 마그넷 방향 계산 포함)
|
||||
if (pathResult.Path != null && pathResult.Path.Count > 0)
|
||||
{
|
||||
@@ -149,54 +150,41 @@ namespace AGVNavigationCore.PathFinding.Planning
|
||||
var node = pathResult.Path[i];
|
||||
var nextNode = (i + 1 < pathResult.Path.Count) ? pathResult.Path[i + 1] : null;
|
||||
|
||||
// 마그넷 방향 계산 (갈림길인 경우)
|
||||
// 마그넷 방향 계산 (갈림길인 경우)
|
||||
MagnetDirection magnetDirection = MagnetDirection.Straight;
|
||||
|
||||
if ((node.ConnectedNodes?.Count ?? 0) >= 2 && nextNode != null)
|
||||
//갈림길에 있다면 미리 방향을 저장해준다.
|
||||
if ((node.ConnectedNodes?.Count ?? 0) > 2 && nextNode != null)
|
||||
{
|
||||
// 갈림길인 경우: 진입 방향과 진출 방향의 각도 계산
|
||||
//var prevNode = pathResult.Path[i - 1];
|
||||
//var nextNode = pathResult.Path[i + 1];
|
||||
|
||||
// 진입 각도 계산 (이전 노드 → 현재 노드)
|
||||
//double entryAngle = Math.Atan2(
|
||||
// node.Position.Y - prevNode.Position.Y,
|
||||
// node.Position.X - prevNode.Position.X
|
||||
//) * 180.0 / Math.PI;
|
||||
|
||||
// 진출 각도 계산 (현재 노드 → 다음 노드)
|
||||
double exitAngle = Math.Atan2(
|
||||
nextNode.Position.Y - node.Position.Y,
|
||||
nextNode.Position.X - node.Position.X
|
||||
) * 180.0 / Math.PI;
|
||||
|
||||
// 각도 차이 계산 (-180~180 범위로 정규화)
|
||||
double angleDiff = exitAngle;// - entryAngle;
|
||||
while (angleDiff > 180) angleDiff -= 360;
|
||||
while (angleDiff < -180) angleDiff += 360;
|
||||
|
||||
// 10도 이상 차이나면 좌/우회전 처리
|
||||
if (Math.Abs(angleDiff) >= 10)
|
||||
//다음 노드ID를 확인해서 마그넷 방향 데이터를 찾는다.
|
||||
if (node.MagnetDirections.ContainsKey(nextNode.Id) == false)
|
||||
{
|
||||
if (angleDiff > 0)
|
||||
{
|
||||
magnetDirection = MagnetDirection.Right;
|
||||
return AGVPathResult.CreateFailure($"{node.ID2}->{nextNode.ID2} 의 (목표)갈림길 방향이 입력되지 않았습니다", 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
magnetDirection = MagnetDirection.Left;
|
||||
}
|
||||
}
|
||||
var magdir = node.MagnetDirections[nextNode.Id].ToString();
|
||||
if (magdir == "L") magnetDirection = MagnetDirection.Left;
|
||||
else if (magdir == "R") magnetDirection = MagnetDirection.Right;
|
||||
|
||||
}
|
||||
|
||||
var nodeInfo = new NodeMotorInfo(i + 1, node.Id, node.RfidId, prevDirection, nextNode.Id, magnetDirection);
|
||||
|
||||
}
|
||||
|
||||
var nodeInfo = new NodeMotorInfo(i + 1, node.Id, node.RfidId, prevDirection, nextNode, magnetDirection);
|
||||
|
||||
// 속도 설정
|
||||
var mapNode = _mapNodes.FirstOrDefault(n => n.Id == node.Id);
|
||||
if (mapNode != null) nodeInfo.Speed = mapNode.SpeedLimit;
|
||||
|
||||
if (mapNode != null)
|
||||
{
|
||||
nodeInfo.Speed = mapNode.SpeedLimit;
|
||||
detailedPath.Add(nodeInfo);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
pathResult.DetailedPath = detailedPath;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
||||
/// <summary>
|
||||
/// 다음 노드 ID (경로예측용)
|
||||
/// </summary>
|
||||
public string NextNodeId { get; set; }
|
||||
public MapNode NextNode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 회전 가능 노드 여부
|
||||
@@ -87,14 +87,14 @@ namespace AGVNavigationCore.PathFinding.Planning
|
||||
/// </summary>
|
||||
public string SpecialActionDescription { get; set; }
|
||||
|
||||
public NodeMotorInfo(int seqno,string nodeId,ushort rfid, AgvDirection motorDirection, string nextNodeId = null, MagnetDirection magnetDirection = MagnetDirection.Straight)
|
||||
public NodeMotorInfo(int seqno,string nodeId,ushort rfid, AgvDirection motorDirection, MapNode nextNodeId = null, MagnetDirection magnetDirection = MagnetDirection.Straight)
|
||||
{
|
||||
seq = seqno;
|
||||
NodeId = nodeId;
|
||||
RfidId = rfid;
|
||||
MotorDirection = motorDirection;
|
||||
MagnetDirection = magnetDirection;
|
||||
NextNodeId = nextNodeId;
|
||||
NextNode = nextNodeId;
|
||||
CanRotate = false;
|
||||
IsDirectionChangePoint = false;
|
||||
RequiresSpecialAction = false;
|
||||
@@ -108,7 +108,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
var result = $"R{RfidId}[N{NodeId}]:{MotorDirection}";
|
||||
var result = $"R{RfidId}[*{NodeId}]:{MotorDirection}";
|
||||
|
||||
// 마그넷 방향이 직진이 아닌 경우 표시
|
||||
if (MagnetDirection != MagnetDirection.Straight)
|
||||
|
||||
@@ -191,7 +191,7 @@ namespace AGVNavigationCore.Utils
|
||||
}
|
||||
|
||||
// 검증 수행
|
||||
if (LastNodeInfo.MotorDirection == requiredDirection && pathResult.DetailedPath[pathResult.DetailedPath.Count - 2].MotorDirection == requiredDirection)
|
||||
if (LastNodeInfo.MotorDirection == requiredDirection && pathResult.DetailedPath[pathResult.DetailedPath.Count - 1].MotorDirection == requiredDirection)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[DockingValidator] ✅ 도킹 검증 성공");
|
||||
return DockingValidationResult.CreateValid(
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace AGVSimulator.Forms
|
||||
}
|
||||
|
||||
private UnifiedAGVCanvas _simulatorCanvas;
|
||||
private AGVPathfinder _advancedPathfinder;
|
||||
// private AGVPathfinder _advancedPathfinder;
|
||||
private List<VirtualAGV> _agvList;
|
||||
private SimulationState _simulationState;
|
||||
private System.Windows.Forms.Timer _simulationTimer;
|
||||
@@ -186,6 +186,7 @@ namespace AGVSimulator.Forms
|
||||
{
|
||||
_simulatorCanvas = new UnifiedAGVCanvas();
|
||||
_simulatorCanvas.Dock = DockStyle.Fill;
|
||||
_simulatorCanvas.Mode = UnifiedAGVCanvas.CanvasMode.Emulator;
|
||||
|
||||
// 목적지 선택 이벤트 구독
|
||||
_simulatorCanvas.NodesSelected += OnTargetNodeSelected;
|
||||
@@ -1273,7 +1274,21 @@ namespace AGVSimulator.Forms
|
||||
{
|
||||
var info = advancedResult.DetailedPath[i];
|
||||
var rfidId = GetRfidByNodeId(info.NodeId);
|
||||
var nextRfidId = info.NextNodeId != null ? GetRfidByNodeId(info.NextNodeId).ToString("0000") : "-END-";
|
||||
|
||||
var nextRfidId = "";
|
||||
if (info.NextNode != null && info.NextNode.HasRfid())
|
||||
{
|
||||
nextRfidId = info.NextNode.RfidId.ToString("0000");
|
||||
}
|
||||
else if (info.NextNode != null)
|
||||
{
|
||||
nextRfidId = info.NextNode.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextRfidId = "-END-";
|
||||
}
|
||||
|
||||
|
||||
var flags = new List<string>();
|
||||
if (info.CanRotate) flags.Add("회전가능");
|
||||
@@ -1564,7 +1579,7 @@ namespace AGVSimulator.Forms
|
||||
/// UI 상태로부터 테스트 결과 생성 (테스트용)
|
||||
/// </summary>
|
||||
private PathTestLogItem CreateTestResultFromUI(MapNode prevNode, MapNode targetNode,
|
||||
string directionName, (bool result, string message) calcResult)
|
||||
string directionName, AGVPathResult calcResult)
|
||||
{
|
||||
var currentNode = _simulatorCanvas.Nodes.FirstOrDefault(n => n.Id ==
|
||||
(_agvListCombo.SelectedItem as VirtualAGV)?.CurrentNodeId);
|
||||
@@ -1578,10 +1593,10 @@ namespace AGVSimulator.Forms
|
||||
DockingPosition = (targetNode.StationType == StationType.Charger1 || targetNode.StationType == StationType.Charger2) ? "충전기" : "장비"
|
||||
};
|
||||
|
||||
if (calcResult.result)
|
||||
if (calcResult.Success)
|
||||
{
|
||||
// 경로 계산 성공 - 현재 화면에 표시된 경로 정보 사용
|
||||
var currentPath = _simulatorCanvas.CurrentPath;
|
||||
var currentPath = calcResult;// _simulatorCanvas.CurrentPath;
|
||||
if (currentPath != null && currentPath.Success)
|
||||
{
|
||||
// 도킹 검증
|
||||
@@ -1611,7 +1626,7 @@ namespace AGVSimulator.Forms
|
||||
{
|
||||
// 경로 계산 실패
|
||||
logItem.Success = false;
|
||||
logItem.Message = calcResult.message;
|
||||
logItem.Message = calcResult.Message;
|
||||
logItem.DetailedPath = "-";
|
||||
}
|
||||
|
||||
@@ -1748,19 +1763,25 @@ namespace AGVSimulator.Forms
|
||||
SetTargetNodeComboBox(dockingTarget.Id);
|
||||
|
||||
// 경로 계산 버튼 클릭 (실제 사용자 동작)
|
||||
//var calcResult = CalcPath();
|
||||
var startNode = (_startNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
|
||||
var targetNode = (_targetNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
|
||||
var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
|
||||
var calcResult = CalcPath(startNode, targetNode, this._simulatorCanvas.Nodes,selectedAGV.PrevNode, selectedAGV.PrevDirection);
|
||||
|
||||
|
||||
|
||||
|
||||
//// 테스트 결과 생성
|
||||
//testResult = CreateTestResultFromUI(nodeA, dockingTarget, directionName, calcResult);
|
||||
testResult = CreateTestResultFromUI(nodeA, dockingTarget, directionName, calcResult);
|
||||
|
||||
//// 로그 추가
|
||||
//logForm.AddLogItem(testResult);
|
||||
logForm.AddLogItem(testResult);
|
||||
|
||||
//// 실패한 경우에만 경로를 화면에 표시 (시각적 확인)
|
||||
//if (!testResult.Success && _simulatorCanvas.CurrentPath != null)
|
||||
//{
|
||||
// _simulatorCanvas.Invalidate();
|
||||
//}
|
||||
if (!testResult.Success && _simulatorCanvas.CurrentPath != null)
|
||||
{
|
||||
_simulatorCanvas.Invalidate();
|
||||
}
|
||||
|
||||
Application.DoEvents();
|
||||
});
|
||||
@@ -2504,72 +2525,102 @@ namespace AGVSimulator.Forms
|
||||
|
||||
private void btPath2_Click(object sender, EventArgs e)
|
||||
{
|
||||
// 1. 기본 정보 획득
|
||||
if (_startNodeCombo.SelectedItem == null || _startNodeCombo.Text == "선택하세요") SetStartNodeFromAGVPosition();
|
||||
if (_startNodeCombo.SelectedItem == null || _targetNodeCombo.SelectedItem == null)
|
||||
{
|
||||
MessageBox.Show("시작/목표 노드를 확인하세요");
|
||||
return;
|
||||
}
|
||||
|
||||
//var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
|
||||
//if (selectedAGV == null) return AGVPathResult.CreateFailure("Virtual AGV 없음");
|
||||
var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
|
||||
|
||||
// 경로계산2 (Gateway Logic)
|
||||
var rlt = CalcPathGateway();
|
||||
if (rlt.result == false) MessageBox.Show(rlt.message, "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
var startNode = (_startNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
|
||||
var targetNode = (_targetNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
|
||||
var rlt = CalcPath(startNode, targetNode, this._simulatorCanvas.Nodes, selectedAGV.PrevNode, selectedAGV.PrevDirection);
|
||||
if (rlt.Success == false) MessageBox.Show(rlt.Message, "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
else
|
||||
{
|
||||
// 8. 적용
|
||||
|
||||
ApplyResultToSimulator(rlt, selectedAGV);
|
||||
UpdateAdvancedPathDebugInfo(rlt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 길목(Gateway) 기반 경로 계산
|
||||
/// 버퍼-버퍼 상태에서는 별도의 추가 로직을 적용합니다
|
||||
/// </summary>
|
||||
private (bool result, string message) CalcPathGateway()
|
||||
public AGVPathResult CalcPath(MapNode startNode, MapNode targetNode, List<MapNode> nodes,
|
||||
MapNode prevNode, AgvDirection prevDir)
|
||||
{
|
||||
// 1. 기본 정보 획득
|
||||
if (_startNodeCombo.SelectedItem == null || _startNodeCombo.Text == "선택하세요") SetStartNodeFromAGVPosition();
|
||||
if (_startNodeCombo.SelectedItem == null || _targetNodeCombo.SelectedItem == null) return (false, "시작/목표 노드 선택 필요");
|
||||
AGVPathResult Retval;
|
||||
|
||||
var startNode = (_startNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
|
||||
var targetNode = (_targetNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
|
||||
if (startNode == null || targetNode == null) return (false, "노드 정보 오류");
|
||||
if (startNode == null || targetNode == null) return AGVPathResult.CreateFailure("시작/종료노드가 지정되지 않음");
|
||||
|
||||
if (_advancedPathfinder == null) _advancedPathfinder = new AGVPathfinder(_simulatorCanvas.Nodes);
|
||||
|
||||
try
|
||||
{
|
||||
var pathFinder = new AGVPathfinder(nodes);
|
||||
var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
|
||||
if (selectedAGV == null) return (false, "Virtual AGV 없음");
|
||||
if (selectedAGV == null) return AGVPathResult.CreateFailure("Virtual AGV 없음");
|
||||
|
||||
var currentAgvDir = selectedAGV.CurrentDirection;
|
||||
var prevNode = selectedAGV.PrevNode;
|
||||
var prevDir = selectedAGV.PrevDirection;
|
||||
//종료노드라면 이전위치로 이동시켜야한다.
|
||||
AGVPathResult LimitPath = null;
|
||||
if (startNode.ConnectedMapNodes.Count == 1)
|
||||
{
|
||||
LimitPath = pathFinder.FindPathAStar(startNode, targetNode);
|
||||
for(int i = 0; i < LimitPath.Path.Count;i++)
|
||||
{
|
||||
var nodeinfo = LimitPath.Path[i];
|
||||
var dir = (prevDir == AgvDirection.Forward ? AgvDirection.Backward : AgvDirection.Forward);
|
||||
LimitPath.DetailedPath.Add(new NodeMotorInfo(i + 1, nodeinfo.Id, nodeinfo.RfidId, dir));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//var prevNode = selectedAGV.PrevNode;
|
||||
//var prevDir = selectedAGV.PrevDirection;
|
||||
|
||||
// 2. Buffer-to-Buffer 예외 처리
|
||||
|
||||
var node05 = FindNode(5); //05~31사이의 노드는 모두 버퍼이다.
|
||||
var node31 = FindNode(31);
|
||||
if (node05 == null || node31 == null) return (false, "버퍼구간 노드가 없습니다(05~31)");
|
||||
if (node05 == null || node31 == null) return AGVPathResult.CreateFailure("버퍼구간 노드가 없습니다(05~31)");
|
||||
|
||||
var rlt = _advancedPathfinder.FindPathAStar(node05, node31);
|
||||
if (rlt.Success == false) return (false, "버퍼구간 노드경로 확인 실패(05~31)");
|
||||
var rlt = pathFinder.FindPathAStar(node05, node31);
|
||||
if (rlt.Success == false) return AGVPathResult.CreateFailure("버퍼구간 노드경로 확인 실패(05~31)");
|
||||
|
||||
//하이라이트노드 해제
|
||||
_simulatorCanvas.HighlightNodeId = null;
|
||||
|
||||
//버퍼구간내에 시작과 종료가 모두 포함되어있다
|
||||
if (rlt.Path.Contains(startNode) && rlt.Path.Contains(targetNode))
|
||||
{
|
||||
return CalcPathBufferToBuffer(startNode, targetNode, prevNode, prevDir, currentAgvDir, selectedAGV);
|
||||
Retval = CalcPathBufferToBuffer(pathFinder, startNode, targetNode, prevNode, prevDir, selectedAGV);
|
||||
}
|
||||
|
||||
|
||||
else
|
||||
{
|
||||
// 3. 목적지별 Gateway 및 진입 조건 확인
|
||||
var gatewayNode = GetGatewayNode(targetNode);
|
||||
if (gatewayNode == null)
|
||||
{
|
||||
//게이트웨이가 없는 경우라면 목적지가 도킹포인트가 아니므로, a*알골리즘으로 진행 방향만 맟춰서 이동한다
|
||||
var simplePath = _advancedPathfinder.FindBasicPath(startNode, targetNode, prevNode, prevDir);
|
||||
if (simplePath.Success)
|
||||
Retval = pathFinder.FindBasicPath(startNode, targetNode, prevNode, prevDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
_simulatorCanvas.HighlightNodeId = null; // 일반 경로는 강조 없음
|
||||
ApplyResultToSimulator(simplePath, selectedAGV);
|
||||
UpdateAdvancedPathDebugInfo(simplePath);
|
||||
return (true, "일반 이동 경로(Gateway 없음)");
|
||||
}
|
||||
return (false, $"일반 이동 경로 실패: {simplePath.ErrorMessage}");
|
||||
}
|
||||
|
||||
// Gateway Node 찾음
|
||||
_simulatorCanvas.HighlightNodeId = gatewayNode.Id; // Gateway 강조 설정
|
||||
|
||||
// 4. Start -> Gateway 경로 계산 (A*)
|
||||
var pathToGateway = _advancedPathfinder.FindBasicPath(startNode, gatewayNode, prevNode, prevDir);
|
||||
|
||||
if (!pathToGateway.Success) return (false, $"Gateway({gatewayNode.ID2})까지 경로 실패: {pathToGateway.ErrorMessage}");
|
||||
var pathToGateway = pathFinder.FindBasicPath(startNode, gatewayNode, prevNode, prevDir);
|
||||
if (pathToGateway.Success == false)
|
||||
AGVPathResult.CreateFailure($"Gateway({gatewayNode.ID2})까지 경로 실패: {pathToGateway.Message}");
|
||||
|
||||
//마지막경로는 게이트웨이이므로 제거하낟.(260113)
|
||||
if (pathToGateway.Path.Count > 1 && pathToGateway.Path.Last().Id == gatewayNode.Id)
|
||||
@@ -2579,27 +2630,227 @@ namespace AGVSimulator.Forms
|
||||
}
|
||||
|
||||
// 5. Gateway -> Target 경로 계산 (회차 패턴 및 최종 진입 포함)
|
||||
|
||||
|
||||
MapNode GateprevNode = pathToGateway.Path.Last();
|
||||
NodeMotorInfo GatePrevDetail = pathToGateway.DetailedPath.Last();
|
||||
|
||||
|
||||
var arrivalOrientation = GatePrevDetail.MotorDirection;
|
||||
var gatewayPathResult = GetPathFromGateway(pathFinder, gatewayNode, targetNode, GateprevNode, arrivalOrientation);
|
||||
if (!gatewayPathResult.Success) return AGVPathResult.CreateFailure($"{gatewayPathResult.Message}");
|
||||
Retval = CombinePaths(pathToGateway, gatewayPathResult);
|
||||
}
|
||||
|
||||
//아래코드오류발생함
|
||||
var gatewayPathResult = GetPathFromGateway(gatewayNode, targetNode, GateprevNode, arrivalOrientation);
|
||||
}
|
||||
|
||||
if (!gatewayPathResult.Success) return (false, $"{gatewayPathResult.ErrorMessage}");
|
||||
AGVPathResult finalPath = pathToGateway;
|
||||
//경로오류 검사
|
||||
if (Retval.Success == false) return Retval;
|
||||
|
||||
finalPath = CombinePaths(finalPath, gatewayPathResult);
|
||||
//해당경로와 대상의 도킹포인트의 방향을 검사합니다
|
||||
if (targetNode.DockDirection != DockingDirection.DontCare)
|
||||
{
|
||||
var lastPath = Retval.DetailedPath.Last();
|
||||
if (targetNode.DockDirection == DockingDirection.Forward && lastPath.MotorDirection != AgvDirection.Forward)
|
||||
{
|
||||
return AGVPathResult.CreateFailure($"생성된 경로와 목적지의 도킹방향이 일치하지 않습니다(FWD) Target:{targetNode.DockDirection}, Path:{Retval.GetDetailedPathInfo()}");
|
||||
}
|
||||
if (targetNode.DockDirection == DockingDirection.Backward && lastPath.MotorDirection != AgvDirection.Backward)
|
||||
{
|
||||
return AGVPathResult.CreateFailure($"생성된 경로와 목적지의 도킹방향이 일치하지 않습니다(BWD) Target:{targetNode.DockDirection}, Path:{Retval.GetDetailedPathInfo()}");
|
||||
}
|
||||
}
|
||||
|
||||
//6[F][R] → 13[B][L] → 6[F][L] 이런경우를 찾아서 경로를 최적화한다.
|
||||
//위 예제에서는 6FR 13BL 이 제자리로 오는 경로이므로 6FL만 남기면된다.
|
||||
//단순히 ID만 같은게 아니라, 실제로 갔다가 되돌아오는 패턴(역방향)인지 확인해야 함.
|
||||
while (true)
|
||||
{
|
||||
var updatecount = 0;
|
||||
for (int i = 0; i < Retval.DetailedPath.Count - 2; i++)
|
||||
{
|
||||
var n1 = Retval.DetailedPath[i];
|
||||
var n2 = Retval.DetailedPath[i + 1];
|
||||
var n3 = Retval.DetailedPath[i + 2];
|
||||
|
||||
if (n1.NodeId == n3.NodeId)
|
||||
{
|
||||
bool isInverse = false;
|
||||
|
||||
// 1. 모터 방향이 반대인가? (F <-> B)
|
||||
bool isMotorInverse = (n1.MotorDirection != n2.MotorDirection) &&
|
||||
(n1.MotorDirection == AgvDirection.Forward || n1.MotorDirection == AgvDirection.Backward) &&
|
||||
(n2.MotorDirection == AgvDirection.Forward || n2.MotorDirection == AgvDirection.Backward);
|
||||
|
||||
if (isMotorInverse)
|
||||
{
|
||||
// 2. 마그넷 방향이 반대인가? (L <-> R, S <-> S)
|
||||
bool isMagnetInverse = false;
|
||||
if (n1.MagnetDirection == MagnetDirection.Straight && n2.MagnetDirection == MagnetDirection.Straight) isMagnetInverse = true;
|
||||
else if (n1.MagnetDirection == MagnetDirection.Left && n2.MagnetDirection == MagnetDirection.Right) isMagnetInverse = true;
|
||||
else if (n1.MagnetDirection == MagnetDirection.Right && n2.MagnetDirection == MagnetDirection.Left) isMagnetInverse = true;
|
||||
|
||||
if (isMagnetInverse) isInverse = true;
|
||||
}
|
||||
|
||||
if (isInverse)
|
||||
{
|
||||
// 제자리 회귀 경로 발견 -> 앞의 두 노드(n1, n2)를 제거하여 n3만 남김
|
||||
Retval.DetailedPath.RemoveAt(i);
|
||||
Retval.DetailedPath.RemoveAt(i);
|
||||
|
||||
if (Retval.Path.Count > i + 1)
|
||||
{
|
||||
Retval.Path.RemoveAt(i);
|
||||
Retval.Path.RemoveAt(i);
|
||||
}
|
||||
i--; // 인덱스 재조정
|
||||
updatecount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (updatecount == 0) break;
|
||||
}
|
||||
|
||||
|
||||
// 8. 적용
|
||||
ApplyResultToSimulator(finalPath, selectedAGV);
|
||||
UpdateAdvancedPathDebugInfo(finalPath);
|
||||
return (true, "성공");
|
||||
//detail 경로를 확인해서 왓던길에서 바로 방향이 전환되는 경우를 찾는다
|
||||
for (int i = 0; i < Retval.DetailedPath.Count - 2; i++)
|
||||
{
|
||||
|
||||
var n1 = Retval.DetailedPath[i];
|
||||
var n2 = Retval.DetailedPath[i + 1];
|
||||
var n3 = Retval.DetailedPath[i + 2];
|
||||
|
||||
if (n1.NodeId == n3.NodeId && n1.MotorDirection == n3.MotorDirection)
|
||||
{
|
||||
return AGVPathResult.CreateFailure($"불가능한 회전 경로가 포함되어있습니다. {n1.RfidId}->{n2.RfidId}->{n3.RfidId}\n{Retval.GetDetailedPathInfo()}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//6[F][R] → 13[B][L] → 6[F][L] 이런경우를 찾아서 경로를 최적화한다.
|
||||
//위 예제에서는 6FR 13BL 이 제자리로 오는 경로이므로 6FL만 남기면된다.
|
||||
|
||||
|
||||
//최종결과 반환
|
||||
return Retval;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return AGVPathResult.CreateFailure($"[계산오류] {ex.Message}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private AGVPathResult CalcPathBufferToBuffer(AGVPathfinder pathfinder, MapNode start, MapNode target, MapNode prev, AgvDirection prevDir, VirtualAGV agv)
|
||||
{
|
||||
// Monitor Side 판단 로직
|
||||
// 현재 AGV의 물리적 방향(Monitor Side)이 "Right" 상태여야 버퍼 진입이 용이하다고 가정.
|
||||
// Monitor Left 상태(부적절한 방향)라면 Gateway로 탈출해야 함.
|
||||
|
||||
// 이동 벡터 X 변화량
|
||||
// prev가 없거나 start와 같으면 이동 방향을 알 수 없음 -> 이 경우 보수적으로 기존 로직(Backward면 탈출) 따름
|
||||
int deltaX = 0;
|
||||
if (prev == null) return AGVPathResult.CreateFailure("이전 노드 정보가 없습니다");
|
||||
else deltaX = start.Position.X - prev.Position.X;
|
||||
|
||||
bool isMonitorLeft = false;
|
||||
|
||||
if (deltaX > 0) // 오른쪽(Forward)으로 이동해 옴 (예: 2 -> 4)
|
||||
{
|
||||
// 이동방향(Right) + 전진(F) => Monitor Right (Good)
|
||||
// 이동방향(Right) + 후진(B) => Monitor Left (Bad)
|
||||
isMonitorLeft = (prevDir == AgvDirection.Backward);
|
||||
}
|
||||
else if (deltaX < 0) // 왼쪽(Reverse)으로 이동해 옴 (예: 4 -> 2)
|
||||
{
|
||||
// 이동방향(Left) + 전진(F) => Monitor Left (Bad)
|
||||
// 이동방향(Left) + 후진(B) => Monitor Right (Good)
|
||||
isMonitorLeft = (prevDir == AgvDirection.Forward);
|
||||
}
|
||||
else // 제자리 또는 수직 이동
|
||||
{
|
||||
// 판단 불가 시 기존 로직(Backward면 Gateway) 유지
|
||||
return AGVPathResult.CreateFailure("이전 노드와의 방향을 알 수 없습니다");
|
||||
}
|
||||
|
||||
if (isMonitorLeft)
|
||||
{
|
||||
// Monitor Left 상태 (방향 불일치) -> Gateway로 탈출
|
||||
var GateWayNode = FindNode(6);
|
||||
var escPath = pathfinder.FindBasicPath(start, GateWayNode, prev, prevDir);
|
||||
if (!escPath.Success) return AGVPathResult.CreateFailure("버퍼 탈출 경로 실패");
|
||||
|
||||
var lastNode = escPath.Path.Last(); // Should be GW6
|
||||
var lastPrev = escPath.Path[escPath.Path.Count - 2];
|
||||
var lastDir = escPath.DetailedPath.Last().MotorDirection;
|
||||
|
||||
// Gateway 도착 후, Target(Buffer)으로 이동
|
||||
// 여기서부터는 "Monitor Right" 로직(즉, 적절한 방향 진입)을 적용합니다.
|
||||
// 6번에서 Target이 왼쪽이면 Direct(Backward), 오른쪽이면 Overshoot(Forward->Backward)
|
||||
|
||||
|
||||
bool isTargetLeftOfGW = target.Position.X < GateWayNode.Position.X;
|
||||
AGVPathResult entryPath = null;
|
||||
|
||||
//게이트웨이까지 후진으로 이동했다면 모니터방향이 오른쪽이다 => 방향전환필요
|
||||
var gateToTarget = GetPathFromGateway(pathfinder, GateWayNode, target, lastPrev, lastDir);
|
||||
|
||||
escPath.Path.RemoveAt(escPath.Path.Count - 1);
|
||||
escPath.DetailedPath.RemoveAt(escPath.DetailedPath.Count - 1);
|
||||
|
||||
|
||||
var final = CombinePaths(escPath, gateToTarget);
|
||||
ApplyResultToSimulator(final, agv);
|
||||
UpdateAdvancedPathDebugInfo(final);
|
||||
return final;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Monitor Right 상태 (방향 일치) -> 직접 진입 또는 Overshoot
|
||||
bool isTargetLeft = target.Position.X < start.Position.X;
|
||||
|
||||
if (isTargetLeft)
|
||||
{
|
||||
var directPath = pathfinder.FindBasicPath(start, target, prev, AgvDirection.Backward);
|
||||
ApplyResultToSimulator(directPath, agv);
|
||||
UpdateAdvancedPathDebugInfo(directPath);
|
||||
return directPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
//대상이 나보다 우측에 있으니 RFID값이 읽어지는 위치까지 이동후에 다시 반대방향으로 마크스탑 해야 함
|
||||
//그냥 대상 노드까지 이동을 한다.. RFID값이 실제 멈추는 위치 이전에 있으니 그곳까지 이동하고 역방향 마크스탑하면 동일한 위치이다. 위 식은 그 이전노드까지 확실하게 이동하는 코드이다
|
||||
//var overshootNode = target.ConnectedMapNodes.OrderByDescending(n => n.Position.X).FirstOrDefault();
|
||||
//if (overshootNode == null || overshootNode.Position.X <= target.Position.X)
|
||||
// return (false, "Overshoot 공간 부족");
|
||||
|
||||
var path1 = pathfinder.FindBasicPath(start, target, prev, AgvDirection.Forward);
|
||||
if (path1.Path.Count < 2) return AGVPathResult.CreateFailure("Overshoot 경로 생성 실패");
|
||||
|
||||
//목표에서 방향바꿔 마크스탑을 해야한다
|
||||
path1.Path.Add(path1.Path.Last());
|
||||
|
||||
//디테일은 방향바꿔서 추가 필요(속도는 저속처리해준다)
|
||||
var lastDefailt = path1.DetailedPath.Last();
|
||||
path1.DetailedPath.Add(new NodeMotorInfo(lastDefailt.seq + 1, lastDefailt.NodeId, lastDefailt.RfidId, AgvDirection.Backward)
|
||||
{
|
||||
Speed = SpeedLevel.L,
|
||||
IsPass = false,
|
||||
});
|
||||
|
||||
//var p1Last = path1.Path.Last();
|
||||
//var p1Prev = path1.Path[path1.Path.Count > 1 ? path1.Path.Count - 2 : 0]; // Safety check
|
||||
//var p1Dir = path1.DetailedPath.Last().MotorDirection;
|
||||
|
||||
//var path2 = _advancedPathfinder.FindPath(start, target, p1Last, p1Dir, AgvDirection.Backward);
|
||||
|
||||
//if (path2.Success && path2.DetailedPath.Last().NodeId == target.Id)
|
||||
// path2.DetailedPath = path2.DetailedPath.Take(path2.DetailedPath.Count - 1).ToList();
|
||||
|
||||
//var final = CombinePaths(path1, path2);
|
||||
ApplyResultToSimulator(path1, agv);
|
||||
UpdateAdvancedPathDebugInfo(path1);
|
||||
return path1;// (true, path1, "버퍼 우측(Overshoot)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2634,7 +2885,7 @@ namespace AGVSimulator.Forms
|
||||
/// <param name="PrevNode">게이트웨이 진입 전 노드</param>
|
||||
/// <param name="PrevDirection">게이트웨이 진입 전 모터방향</param>
|
||||
/// <returns></returns>
|
||||
private AGVPathResult GetPathFromGateway(MapNode GTNode, MapNode targetNode, MapNode PrevNode, AgvDirection PrevDirection)
|
||||
private AGVPathResult GetPathFromGateway(AGVPathfinder pathFinder, MapNode GTNode, MapNode targetNode, MapNode PrevNode, AgvDirection PrevDirection)
|
||||
{
|
||||
AGVPathResult resultPath = null;
|
||||
|
||||
@@ -2678,13 +2929,14 @@ namespace AGVSimulator.Forms
|
||||
rlt1.Success = true;
|
||||
|
||||
//목적지까지 바로 계산한다
|
||||
var pathtarget = _advancedPathfinder.FindBasicPath(GTNode, targetNode, PrevNode, AgvDirection.Backward);
|
||||
var motdir = targetNode.DockDirection == DockingDirection.Backward ? AgvDirection.Backward : AgvDirection.Forward;
|
||||
var pathtarget = pathFinder.FindBasicPath(GTNode, targetNode, PrevNode, motdir);
|
||||
|
||||
if (targetNode.DockDirection == DockingDirection.Backward && isMonitorLeft)
|
||||
{
|
||||
//턴을 하는
|
||||
turnPatterns = GetTurnaroundPattern(GTNode, targetNode);
|
||||
if (turnPatterns == null || turnPatterns.Any() == false) return new AGVPathResult { Success = false, ErrorMessage = $"회차 패턴 없음: Dir {PrevDirection}" };
|
||||
if (turnPatterns == null || turnPatterns.Any() == false) return new AGVPathResult { Success = false, Message = $"회차 패턴 없음: Dir {PrevDirection}" };
|
||||
foreach (var item in turnPatterns)
|
||||
{
|
||||
var rfidvalue = ushort.Parse(item.Substring(0, 4));
|
||||
@@ -2709,7 +2961,7 @@ namespace AGVSimulator.Forms
|
||||
if (pathtarget.DetailedPath.First().NodeId != rlt1.DetailedPath.Last().NodeId ||
|
||||
pathtarget.DetailedPath.First().MotorDirection != rlt1.DetailedPath.Last().MotorDirection)
|
||||
{
|
||||
new AGVPathResult { Success = false, ErrorMessage = $"게이트웨이 턴 마지막 주소와, 이 후 주소의 시작 노드ID가 일치하지 않습니다" };
|
||||
new AGVPathResult { Success = false, Message = $"게이트웨이 턴 마지막 주소와, 이 후 주소의 시작 노드ID가 일치하지 않습니다" };
|
||||
}
|
||||
|
||||
pathtarget.Path.RemoveAt(0);
|
||||
@@ -2785,120 +3037,6 @@ namespace AGVSimulator.Forms
|
||||
}
|
||||
}
|
||||
|
||||
private (bool result, string message) CalcPathBufferToBuffer(MapNode start, MapNode target, MapNode prev, AgvDirection prevDir, AgvDirection currentDir, VirtualAGV agv)
|
||||
{
|
||||
// Monitor Side 판단 로직
|
||||
// 현재 AGV의 물리적 방향(Monitor Side)이 "Right" 상태여야 버퍼 진입이 용이하다고 가정.
|
||||
// Monitor Left 상태(부적절한 방향)라면 Gateway로 탈출해야 함.
|
||||
|
||||
// 이동 벡터 X 변화량
|
||||
// prev가 없거나 start와 같으면 이동 방향을 알 수 없음 -> 이 경우 보수적으로 기존 로직(Backward면 탈출) 따름
|
||||
int deltaX = 0;
|
||||
if (prev == null) return (false, "이전 노드 정보가 없습니다");
|
||||
else deltaX = start.Position.X - prev.Position.X;
|
||||
|
||||
bool isMonitorLeft = false;
|
||||
|
||||
if (deltaX > 0) // 오른쪽(Forward)으로 이동해 옴 (예: 2 -> 4)
|
||||
{
|
||||
// 이동방향(Right) + 전진(F) => Monitor Right (Good)
|
||||
// 이동방향(Right) + 후진(B) => Monitor Left (Bad)
|
||||
isMonitorLeft = (prevDir == AgvDirection.Backward);
|
||||
}
|
||||
else if (deltaX < 0) // 왼쪽(Reverse)으로 이동해 옴 (예: 4 -> 2)
|
||||
{
|
||||
// 이동방향(Left) + 전진(F) => Monitor Left (Bad)
|
||||
// 이동방향(Left) + 후진(B) => Monitor Right (Good)
|
||||
isMonitorLeft = (prevDir == AgvDirection.Forward);
|
||||
}
|
||||
else // 제자리 또는 수직 이동
|
||||
{
|
||||
// 판단 불가 시 기존 로직(Backward면 Gateway) 유지
|
||||
return (false, "이전 노드와의 방향을 알 수 없습니다");
|
||||
}
|
||||
|
||||
if (isMonitorLeft)
|
||||
{
|
||||
// Monitor Left 상태 (방향 불일치) -> Gateway로 탈출
|
||||
var GateWayNode = FindNode(6);
|
||||
var escPath = _advancedPathfinder.FindBasicPath(start, GateWayNode, prev, prevDir);
|
||||
if (!escPath.Success) return (false, "버퍼 탈출 경로 실패");
|
||||
|
||||
var lastNode = escPath.Path.Last(); // Should be GW6
|
||||
var lastPrev = escPath.Path[escPath.Path.Count - 2];
|
||||
var lastDir = escPath.DetailedPath.Last().MotorDirection;
|
||||
|
||||
// Gateway 도착 후, Target(Buffer)으로 이동
|
||||
// 여기서부터는 "Monitor Right" 로직(즉, 적절한 방향 진입)을 적용합니다.
|
||||
// 6번에서 Target이 왼쪽이면 Direct(Backward), 오른쪽이면 Overshoot(Forward->Backward)
|
||||
|
||||
|
||||
bool isTargetLeftOfGW = target.Position.X < GateWayNode.Position.X;
|
||||
AGVPathResult entryPath = null;
|
||||
|
||||
//게이트웨이까지 후진으로 이동했다면 모니터방향이 오른쪽이다 => 방향전환필요
|
||||
var gateToTarget = GetPathFromGateway(GateWayNode, target, lastPrev, lastDir);
|
||||
|
||||
escPath.Path.RemoveAt(escPath.Path.Count - 1);
|
||||
escPath.DetailedPath.RemoveAt(escPath.DetailedPath.Count - 1);
|
||||
|
||||
|
||||
var final = CombinePaths(escPath, gateToTarget);
|
||||
ApplyResultToSimulator(final, agv);
|
||||
UpdateAdvancedPathDebugInfo(final);
|
||||
return (true, "버퍼 재진입(탈출후)");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Monitor Right 상태 (방향 일치) -> 직접 진입 또는 Overshoot
|
||||
bool isTargetLeft = target.Position.X < start.Position.X;
|
||||
|
||||
if (isTargetLeft)
|
||||
{
|
||||
var directPath = _advancedPathfinder.FindBasicPath(start, target, prev, AgvDirection.Backward);
|
||||
ApplyResultToSimulator(directPath, agv);
|
||||
UpdateAdvancedPathDebugInfo(directPath);
|
||||
return (true, "버퍼 좌측이동(직접진입)");
|
||||
}
|
||||
else
|
||||
{
|
||||
//대상이 나보다 우측에 있으니 RFID값이 읽어지는 위치까지 이동후에 다시 반대방향으로 마크스탑 해야 함
|
||||
//그냥 대상 노드까지 이동을 한다.. RFID값이 실제 멈추는 위치 이전에 있으니 그곳까지 이동하고 역방향 마크스탑하면 동일한 위치이다. 위 식은 그 이전노드까지 확실하게 이동하는 코드이다
|
||||
//var overshootNode = target.ConnectedMapNodes.OrderByDescending(n => n.Position.X).FirstOrDefault();
|
||||
//if (overshootNode == null || overshootNode.Position.X <= target.Position.X)
|
||||
// return (false, "Overshoot 공간 부족");
|
||||
|
||||
var path1 = _advancedPathfinder.FindBasicPath(start, target, prev, AgvDirection.Forward);
|
||||
if (path1.Path.Count < 2) return (false, "Overshoot 경로 생성 실패");
|
||||
|
||||
//목표에서 방향바꿔 마크스탑을 해야한다
|
||||
path1.Path.Add(path1.Path.Last());
|
||||
|
||||
//디테일은 방향바꿔서 추가 필요(속도는 저속처리해준다)
|
||||
var lastDefailt = path1.DetailedPath.Last();
|
||||
path1.DetailedPath.Add(new NodeMotorInfo(lastDefailt.seq + 1, lastDefailt.NodeId, lastDefailt.RfidId, AgvDirection.Backward)
|
||||
{
|
||||
Speed = SpeedLevel.L,
|
||||
IsPass = false,
|
||||
});
|
||||
|
||||
//var p1Last = path1.Path.Last();
|
||||
//var p1Prev = path1.Path[path1.Path.Count > 1 ? path1.Path.Count - 2 : 0]; // Safety check
|
||||
//var p1Dir = path1.DetailedPath.Last().MotorDirection;
|
||||
|
||||
//var path2 = _advancedPathfinder.FindPath(start, target, p1Last, p1Dir, AgvDirection.Backward);
|
||||
|
||||
//if (path2.Success && path2.DetailedPath.Last().NodeId == target.Id)
|
||||
// path2.DetailedPath = path2.DetailedPath.Take(path2.DetailedPath.Count - 1).ToList();
|
||||
|
||||
//var final = CombinePaths(path1, path2);
|
||||
ApplyResultToSimulator(path1, agv);
|
||||
UpdateAdvancedPathDebugInfo(path1);
|
||||
return (true, "버퍼 우측(Overshoot)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// p1+p2
|
||||
/// </summary>
|
||||
|
||||
@@ -371,7 +371,7 @@ namespace Project
|
||||
{
|
||||
// 경로 실패시 디버깅 정보 초기화
|
||||
//_pathDebugLabel.Text = $"경로: 실패 - {advancedResult.ErrorMessage}";
|
||||
Message = $"경로를 찾을 수 없습니다:\n{advancedResult.ErrorMessage}";
|
||||
Message = $"경로를 찾을 수 없습니다:\n{advancedResult.Message}";
|
||||
advancedResult = null;
|
||||
}
|
||||
|
||||
|
||||
273
경로계산백업.md
273
경로계산백업.md
@@ -0,0 +1,273 @@
|
||||
ML 0001 0001 0001(FS) 0016(FS) 0012(FS) 0010(BL) 0012(BL) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
ML 0016 0001 0016(BS) 0001(BS) [MARKSTOP]
|
||||
ML 0012 0001 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
ML 0010 0001 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
ML 0009 0001 0009(BL) 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
ML 0015 0001 0015(BS) 0009(BL) 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
ML 0017 0001 0017(BS) 0009(BL) 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
ML 0007 0001 0007(FR) 0009(BR) 0010(BL) 0012(BL) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
ML 0013 0001 [G1312ML] 0016(BS) 0001(BS) [MARKSTOP]
|
||||
ML 0019 0001 0019(FS) [G1312ML] 0016(BS) 0001(BS) [MARKSTOP]
|
||||
ML 0008 0001 0008(FS) 0019(FS) [G1312ML] 0016(BS) 0001(BS) [MARKSTOP]
|
||||
ML 0011 0001 0011(BS) [G0601ML] [MARKSTOP]
|
||||
ML 0006 0001 [G0601ML] [MARKSTOP]
|
||||
ML 0005 0001 0005(BR) [G0601ML] [MARKSTOP]
|
||||
ML 0003 0001 0003(BL) 0005(BR) [G0601ML] [MARKSTOP]
|
||||
ML 0004 0001 0004(BL) 0003(BL) 0005(BR) [G0601ML] [MARKSTOP]
|
||||
ML 0002 0001 0002(BS) 0004(BL) 0003(BL) 0005(BR) [G0601ML] [MARKSTOP]
|
||||
ML 0034 0001 0034(BS) 0002(BS) 0004(BL) 0003(BL) 0005(BR) [G0601ML] [MARKSTOP]
|
||||
ML 0033 0001 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) 0005(BR) [G0601ML] [MARKSTOP]
|
||||
ML 0032 0001 0032(BS) 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) 0005(BR) [G0601ML] [MARKSTOP]
|
||||
ML 0031 0001 0031(BS) 0032(BS) 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) 0005(BR) [G0601ML] [MARKSTOP]
|
||||
|
||||
|
||||
ML 0001 0019 0001(FS) 0016(FS) [G1213ML] 0019(BS) [MARKSTOP]
|
||||
ML 0016 0019 0016(FS) [G1213ML] 0019(BS) [MARKSTOP]
|
||||
ML 0012 0019 [G1213ML] 0019(BS) [MARKSTOP]
|
||||
ML 0010 0019 0010(FR) 0009(BR) 0007(BS) 0013(BL) 0019(BS) [MARKSTOP]
|
||||
ML 0009 0019 0009(BR) 0007(BS) 0013(BL) 0019(BS) [MARKSTOP]
|
||||
ML 0015 0019 0015(BS) 0009(BR) 0007(BS) 0013(BL) 0019(BS) [MARKSTOP]
|
||||
ML 0017 0019 0017(BS) 0009(BR) 0007(BS) 0013(BL) 0019(BS) [MARKSTOP]
|
||||
ML 0007 0019 0007(BS) 0013(BL) 0019(BS) [MARKSTOP]
|
||||
ML 0013 0019 0013(BL) 0019(BS) [MARKSTOP]
|
||||
ML 0019 0019 [동작없음] [MARKSTOP]
|
||||
ML 0008 0019 0008(FS) 0019(BS) [MARKSTOP]
|
||||
ML 0011 0019 0011(BS) 0006(BR) 0007(BS) 0013(BL) 0019(BS) [MARKSTOP]
|
||||
ML 0006 0019 0006(BR) 0007(BS) 0013(BL) 0019(BS) [MARKSTOP]
|
||||
ML 0005 0019 [G0519ML] [MARKSTOP]
|
||||
ML 0003 0019 0003(BL) [G0519ML] [MARKSTOP]
|
||||
ML 0004 0019 0004(BL) 0003(BL) [G0519ML] [MARKSTOP]
|
||||
ML 0002 0019 0002(BS) 0004(BL) 0003(BL) [G0519ML] [MARKSTOP]
|
||||
ML 0034 0019 0034(BS) 0002(BS) 0004(BL) 0003(BL) [G0519ML] [MARKSTOP]
|
||||
ML 0033 0019 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) [G0519ML] [MARKSTOP]
|
||||
ML 0032 0019 0032(BS) 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) [G0519ML] [MARKSTOP]
|
||||
ML 0031 0019 0031(BS) 0032(BS) 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) [G0519ML] [MARKSTOP]
|
||||
|
||||
|
||||
|
||||
ML 0001 0034 0001(FS) 0016(FS) 0012(FS) 0010(FR) 0009(BR) 0007(BS) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(BS) 0034 [MARKSTOP]0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
|
||||
ML 0001 0008 0001(FS) 0016(FS) [G1213ML] 0019(BR) 0008(BS) [MARKSTOP]
|
||||
ML 0016 0008 0016(FS) [G1213ML] 0019(BR) 0008(BS) [MARKSTOP]
|
||||
ML 0012 0008 [G1213ML] 0019(BR) 0008(BS) [MARKSTOP]
|
||||
ML 0010 0008 0010(FR) 0009(BR) 0007(BS) 0013(BL) 0019(BR) 0008(BS) [MARKSTOP]
|
||||
ML 0009 0008 0009(BR) 0007(BS) 0013(BL) 0019(BR) 0008(BS) [MARKSTOP]
|
||||
ML 0015 0008 0015(BS) 0009(BR) 0007(BS) 0013(BL) 0019(BR) 0008(BS) [MARKSTOP]
|
||||
ML 0017 0008 0017(BS) 0009(BR) 0007(BS) 0013(BL) 0019(BR) 0008(BS) [MARKSTOP]
|
||||
ML 0007 0008 0007(BS) 0013(BL) 0019(BR) 0008(BS) [MARKSTOP]
|
||||
ML 0013 0008 0013(BS) 0019(FS) 0008(BS) [MARKSTOP]
|
||||
ML 0019 0008 0019(BS) 0008(BS) [MARKSTOP]
|
||||
ML 0008 0008 [마크센서가 감지안되는 경우] 0008(FS) 0019(BS) 0008(BS) [MARKSTOP]
|
||||
ML 0011 0008 0011(BS) 0006(BR) 0007(BS) 0013(BL) 0019(BR) 0008(BS) [MARKSTOP]
|
||||
ML 0006 0008 0006(BR) 0007(BS) 0013(BL) 0019(BR) 0008(BS) [MARKSTOP]
|
||||
ML 0005 0008 0005(BR) 0006(BR) 0007(BS) 0013(BL) 0019(BR) 0008(BS) [MARKSTOP]
|
||||
ML 0003 0008 0003(BL) 0005(BR) 0006(BR) 0007(BS) 0013(BL) 0019(BR) 0008(BS) [MARKSTOP]
|
||||
ML 0004 0008 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0007(BS) 0013(BL) 0019(BR) 0008(BS) [MARKSTOP]
|
||||
ML 0002 0008 0002(BS) 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0007(BS) 0013(BL) 0019(BR) 0008(BS) [MARKSTOP]
|
||||
ML 0034 0008 0034(BS) 0002(BS) 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0007(BS) 0013(BL) 0019(BR) 0008(BS) [MARKSTOP]
|
||||
ML 0033 0008 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0007(BS) 0013(BL) 0019(BR) 0008(BS) [MARKSTOP]
|
||||
ML 0032 0008 0032(BS) 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0007(BS) 0013(BL) 0019(BR) 0008(BS) [MARKSTOP]
|
||||
ML 0031 0008 0031(BS) 0032(BS) 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0007(BS) 0013(BL) 0019(BR) 0008(BS) [MARKSTOP]
|
||||
|
||||
ML 0001 0015 0001(FS) 0016(FS) 0012(FS) 0010(FR) 0009(FS) 0015(FS) [MARKSTOP]
|
||||
ML 0016 0015 0016(FS) 0012(FS) 0010(FR) 0009(FS) 0015(FS) [MARKSTOP]
|
||||
ML 0012 0015 0012(FS) 0010(FR) 0009(FS) 0015(FS) [MARKSTOP]
|
||||
ML 0010 0015 0010(FR) 0009(FS) 0015(FS) [MARKSTOP]
|
||||
ML 0009 0015 0009(FS) 0015(FS) [MARKSTOP]
|
||||
ML 0015 0015 [동작없음] [MARKSTOP]
|
||||
ML 0017 0015 0017(BS) 0009(FS) 0015(FS) [MARKSTOP]
|
||||
ML 0007 0015 0007(FL) 0009(FS) 0015(FS) [MARKSTOP]
|
||||
ML 0013 0015 [G1309ML] 0015(FS) [MARKSTOP]
|
||||
ML 0019 0015 0019(FS) [G1309ML] 0015(FS) [MARKSTOP]
|
||||
ML 0008 0015 0008(FS) 0019(FS) [G1309ML] 0015(FS) [MARKSTOP]
|
||||
ML 0011 0015 0011(BS) 0006(BR) 0007(FL) 0009(FS) 0015(FS) [MARKSTOP]
|
||||
ML 0006 0015 0006(BR) 0007(FL) 0009(FS) 0015(FS) [MARKSTOP]
|
||||
ML 0005 0015 0005(BR) 0006(BR) 0007(FL) 0009(FS) 0015(FS) [MARKSTOP]
|
||||
ML 0003 0015 0003(BL) 0005(BR) 0006(BR) 0007(FL) 0009(FS) 0015(FS) [MARKSTOP]
|
||||
ML 0004 0015 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0007(FL) 0009(FS) 0015(FS) [MARKSTOP]
|
||||
ML 0002 0015 0002(BS) 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0007(FL) 0009(FS) 0015(FS) [MARKSTOP]
|
||||
ML 0034 0015 0034(BS) 0002(BS) 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0007(FL) 0009(FS) 0015(FS) [MARKSTOP]
|
||||
ML 0033 0015 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0007(FL) 0009(FS) 0015(FS) [MARKSTOP]
|
||||
ML 0032 0015 0032(BS) 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0007(FL) 0009(FS) 0015(FS) [MARKSTOP]
|
||||
ML 0031 0015 0031(BS) 0032(BS) 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0007(FL) 0009(FS) 0015(FS) [MARKSTOP]
|
||||
|
||||
ML 0001 0011 0001(FS) 0016(FS) 0012(FS) 0010(FR) 0009(BR) 0007(BS) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0016 0011 0016(FS) 0012(FS) 0010(FR) 0009(BR) 0007(BS) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0012 0011 0012(FS) 0010(FR) 0009(BR) 0007(BS) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0010 0011 0010(FR) 0009(BR) 0007(BS) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0009 0011 0009(BR) 0007(BS) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0015 0011 0015(BS) 0009(BR) 0007(BS) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0017 0011 0017(BS) 0009(BR) 0007(BS) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0007 0011 0007(BS) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0013 0011 0013(BS) 0007(FR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0019 0011 0019(FS) 0013(BS) 0007(FR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0008 0011 0008(FS) 0019(FS) 0013(BS) 0007(FR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0011 0011 [동작없음] [MARKSTOP]
|
||||
ML 0006 0011 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0005 0011 0005(BR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0003 0011 0003(BL) 0005(BR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0004 0011 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0002 0011 0002(BS) 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0034 0011 0034(BS) 0002(BS) 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0033 0011 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0032 0011 0032(BS) 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
ML 0031 0011 0031(BS) 0032(BS) 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) 0005(BR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
|
||||
|
||||
ML 0001 0019 0001(FS) 0016(FS) [G1213ML] 0019(BS) [MARKSTOP]
|
||||
ML 0016 0019 0016(FS) [G1213ML] 0019(BS) [MARKSTOP]
|
||||
ML 0012 0019 [G1213ML] 0019(BS) [MARKSTOP]
|
||||
ML 0010 0019 0010(FR) 0009(BR) 0007(BS) 0013(BL) 0019(BS) [MARKSTOP]
|
||||
ML 0009 0019 0009(BR) 0007(BS) 0013(BL) 0019(BS) [MARKSTOP]
|
||||
ML 0015 0019 0015(BS) 0009(BR) 0007(BS) 0013(BL) 0019(BS) [MARKSTOP]
|
||||
ML 0017 0019 0017(BS) 0009(BR) 0007(BS) 0013(BL) 0019(BS) [MARKSTOP]
|
||||
ML 0007 0019 0007(BS) 0013(BL) 0019(BS) [MARKSTOP]
|
||||
ML 0013 0019 0013(BL) 0019(BS) [MARKSTOP]
|
||||
ML 0019 0019 [동작없음] [MARKSTOP]
|
||||
ML 0008 0019 0008(FS) 0019(BS) [MARKSTOP]
|
||||
ML 0011 0019 0011(BS) 0006(BR) 0007(BS) 0013(BL) 0019(BS) [MARKSTOP]
|
||||
ML 0006 0019 0006(BR) 0007(BS) 0013(BL) 0019(BS) [MARKSTOP]
|
||||
ML 0005 0019 [G0519ML] [MARKSTOP]
|
||||
ML 0003 0019 0003(BL) [G0519ML] [MARKSTOP]
|
||||
ML 0004 0019 0004(BL) 0003(BL) [G0519ML] [MARKSTOP]
|
||||
ML 0002 0019 0002(BS) 0004(BL) 0003(BL) [G0519ML] [MARKSTOP]
|
||||
ML 0034 0019 0034(BS) 0002(BS) 0004(BL) 0003(BL) [G0519ML] [MARKSTOP]
|
||||
ML 0033 0019 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) [G0519ML] [MARKSTOP]
|
||||
ML 0032 0019 0032(BS) 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) [G0519ML] [MARKSTOP]
|
||||
ML 0031 0019 0031(BS) 0032(BS) 0033(BS) 0034(BS) 0002(BS) 0004(BL) 0003(BL) [G0519ML] [MARKSTOP]
|
||||
|
||||
|
||||
ML 0001 0034 0001(FS) 0016(FS) 0012(FS) 0010(FR) 0009(BR) 0007(BS) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(BS) 0034 [MARKSTOP]
|
||||
ML 0016 0034 0016(FS) 0012(FS) 0010(FR) 0009(BR) 0007(BS) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(BS) 0034 [MARKSTOP]
|
||||
ML 0012 0034 0012(FS) 0010(FR) 0009(BR) 0007(BS) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(BS) 0034 [MARKSTOP]
|
||||
ML 0010 0034 0010(FR) 0009(BR) 0007(BS) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(BS) 0034 [MARKSTOP]
|
||||
ML 0009 0034 0009(BR) 0007(BS) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(BS) 0034 [MARKSTOP]
|
||||
ML 0015 0034 0015(BS) 0009(BR) 0007(BS) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(BS) 0034 [MARKSTOP]
|
||||
ML 0017 0034 0017(BS) 0009(BR) 0007(BS) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(BS) 0034 [MARKSTOP]
|
||||
ML 0007 0034 0007(BS) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(BS) 0034 [MARKSTOP]
|
||||
ML 0013 0034 0013(BS) 0007(FR) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(BS) 0034 [MARKSTOP]
|
||||
ML 0019 0034 0019(FS) 0013(BS) 0007(FR) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(BS) 0034 [MARKSTOP]
|
||||
ML 0008 0034 0008(FS) 0019(FS) 0013(BS) 0007(FR) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(BS) 0034 [MARKSTOP]
|
||||
ML 0011 0034 0011(BS) 0006(FR) 0005(BL) 0003(BS) 0004(BS) 0002(BS) 0034 [MARKSTOP]
|
||||
ML 0006 0034 0006(FR) 0005(BL) 0003(BS) 0004(BS) 0002(BS) 0034 [MARKSTOP]
|
||||
ML 0005 0034 0005(BL) 0003(BS) 0004(BS) 0002(BS) 0034 [MARKSTOP]
|
||||
ML 0003 0034 0003(BS) 0004(BS) 0002(BS) 0034 [MARKSTOP]
|
||||
ML 0004 0034 0004(BS) 0002(BS) 0034 [MARKSTOP]
|
||||
ML 0002 0034 0002(BS) 0034 [MARKSTOP]
|
||||
ML 0034 0034 [동작없음] [MARKSTOP]
|
||||
ML 0033 0034 0033(FS) 0034 [MARKSTOP]
|
||||
ML 0032 0034 0032(FS) 0033(FS) 0034 [MARKSTOP]
|
||||
ML 0031 0034 0031(FS) 0032(FS) 0033(FS) 0034 [MARKSTOP]
|
||||
|
||||
|
||||
|
||||
MR 0001 0001 0001(BS) 0016(BS) 0012(BS) 0010(FS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0016 0001 0016(FS) 0001(BS) [MARKSTOP]
|
||||
MR 0012 0001 0012(FS) 0016(FS) 0001(BS) [MARKSTOP]
|
||||
MR 0010 0001 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0009 0001 0009(FR) 0007(BS) 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0015 0001 0015(BS) 0009(FR) 0007(BS) 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0017 0001 0017(FS) 0009(FR) 0007(BS) 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0007 0001 0007(BS) 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0013 0001 [G1312MR] 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0019 0001 0019(BS) [G1312MR] 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0008 0001 0008(BS) 0019(BS) [G1312MR] 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0011 0001 0011(FS) 0006(FR) 0013(BL) 0007(BS) 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0006 0001 0006(FR) 0013(BL) 0007(BS) 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0005 0001 0005(FR) 0006(FR) 0013(BL) 0007(BS) 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0003 0001 0003(FL) 0005(FR) 0006(FR) 0013(BL) 0007(BS) 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0004 0001 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BL) 0007(BS) 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0002 0001 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BL) 0007(BS) 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0034 0001 0034(FS) 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BL) 0007(BS) 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0033 0001 0033(FS) 0034(FS) 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BL) 0007(BS) 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0032 0001 0032(FS) 0033(FS) 0034(FS) 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BL) 0007(BS) 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
MR 0031 0001 0031(FS) 0032(FS) 0033(FS) 0034(FS) 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BL) 0007(BS) 0010(BS) 0012(BS) 0016(BS) 0001(BS) [MARKSTOP]
|
||||
|
||||
MR 0001 0019 [G1610MR] 0007(BS) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0016 0019 0016(BS) 0012(BS) 0010(BL) 0007(BS) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0012 0019 0012(BS) 0010(BL) 0007(BS) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0010 0019 0010(BL) 0007(BS) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0009 0019 0009(FR) 0007(BS) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0015 0019 0015(BS) 0009(FR) 0007(BS) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0017 0019 0017(FS) 0009(FR) 0007(BS) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0007 0019 0007(BS) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0013 0019 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0019 0019 [동작없음] [MARKSTOP]
|
||||
MR 0008 0019 0008(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0011 0019 0011(FS) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0006 0019 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0005 0019 0005(FR) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0003 0019 0003(FL) 0005(FR) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0004 0019 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0002 0019 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0034 0019 0034(FS) 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0033 0019 0033(FS) 0034(FS) 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0032 0019 0032(FS) 0033(FS) 0034(FS) 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0031 0019 0031(FS) 0032(FS) 0033(FS) 0034(FS) 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
|
||||
MR 0001 0011 [G1610MR] 0007(BS) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0016 0011 0016(BS) 0012(BS) 0010(BL) 0007(BS) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0012 0011 0012(BS) 0010(BL) 0007(BS) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0010 0011 0010(BL) 0007(BS) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0009 0011 0009(FR) 0007(BS) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0015 0011 0015(BS) 0009(FR) 0007(BS) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0017 0011 0017(FS) 0009(FR) 0007(BS) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0007 0011 0007(BS) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0013 0011 0013(BS) 0007(FR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0019 0011 0019(BS) 0013(BS) 0007(FR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0008 0011 0008(BS) 0019(BS) 0013(BS) 0007(FR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0011 0011 [동작없음] [MARKSTOP]
|
||||
MR 0006 0011 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0005 0011 0005(FR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0003 0011 0003(FL) 0005(FR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0004 0011 0004(FL) 0003(FL) 0005(FR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0002 0011 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0034 0011 0034(FS) 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0033 0011 0033(FS) 0034(FS) 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0032 0011 0032(FS) 0033(FS) 0034(FS) 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
MR 0031 0011 0031(FS) 0032(FS) 0033(FS) 0034(FS) 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(BR) 0011(BS) [MARKSTOP]
|
||||
|
||||
|
||||
MR 0001 0019 [G1610MR] 0007(BS) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0016 0019 0016(BS) 0012(BS) 0010(BL) 0007(BS) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0012 0019 0012(BS) 0010(BL) 0007(BS) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0010 0019 0010(BL) 0007(BS) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0009 0019 0009(FR) 0007(BS) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0015 0019 0015(BS) 0009(FR) 0007(BS) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0017 0019 0017(FS) 0009(FR) 0007(BS) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0007 0019 0007(BS) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0013 0019 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0019 0019 [동작없음] [MARKSTOP]
|
||||
MR 0008 0019 0008(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0011 0019 0011(FS) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0006 0019 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0005 0019 0005(FR) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0003 0019 0003(FL) 0005(FR) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0004 0019 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0002 0019 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0034 0019 0034(FS) 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0033 0019 0033(FS) 0034(FS) 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0032 0019 0032(FS) 0033(FS) 0034(FS) 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
MR 0031 0019 0031(FS) 0032(FS) 0033(FS) 0034(FS) 0002(FS) 0004(FL) 0003(FL) 0005(FR) 0006(FR) 0013(BS) 0007(FR) 0006(BR) 0013(BS) 0019(BS) [MARKSTOP]
|
||||
|
||||
MR 0001 0034 [G1610MR] 0007(BS) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(FS) 0034(BS) [MARKSTOP]
|
||||
MR 0016 0034 0016(BS) 0012(BS) 0010(BL) 0007(BS) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(FS) 0034(BS) [MARKSTOP]
|
||||
MR 0012 0034 0012(BS) 0010(BL) 0007(BS) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(FS) 0034(BS) [MARKSTOP]
|
||||
MR 0010 0034 0010(BL) 0007(BS) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(FS) 0034(BS) [MARKSTOP]
|
||||
MR 0009 0034 0009(FR) 0007(BS) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(FS) 0034(BS) [MARKSTOP]
|
||||
MR 0015 0034 0015(BS) 0009(FR) 0007(BS) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(FS) 0034(BS) [MARKSTOP]
|
||||
MR 0017 0034 0017(FS) 0009(FR) 0007(BS) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(FS) 0034(BS) [MARKSTOP]
|
||||
MR 0007 0034 0007(BS) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(FS) 0034(BS) [MARKSTOP]
|
||||
MR 0013 0034 0013(BS) 0007(FR) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(FS) 0034(BS) [MARKSTOP]
|
||||
MR 0019 0034 0019(BS) 0013(BS) 0007(FR) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(FS) 0034(BS) [MARKSTOP]
|
||||
MR 0008 0034 0008(BS) 0019(BS) 0013(BS) 0007(FR) 0006(BL) 0005(BL) 0003(BS) 0004(BS) 0002(FS) 0034(BS) [MARKSTOP]
|
||||
MR 0011 0034 0011(FS) 0006(FR) 0005(BL) 0003(BS) 0004(BS) 0002(FS) 0034(BS) [MARKSTOP]
|
||||
MR 0006 0034 0006(FR) 0005(BL) 0003(BS) 0004(BS) 0002(FS) 0034(BS) [MARKSTOP]
|
||||
MR 0005 0034 0005(BL) 0003(BS) 0004(BS) 0002(FS) 0034(BS) [MARKSTOP]
|
||||
MR 0003 0034 0003(BS) 0004(BS) 0002(FS) 0034(BS) [MARKSTOP]
|
||||
MR 0004 0034 0004(BS) 0002(FS) 0034(BS) [MARKSTOP]
|
||||
MR 0002 0034 0002(FS) 0034(BS) [MARKSTOP]
|
||||
MR 0034 0034 [동작없음] [MARKSTOP]
|
||||
MR 0033 0034 0033(BS) 0034(BS) [MARKSTOP]
|
||||
MR 0032 0034 0032(BS) 0033(BS) 0034(BS) [MARKSTOP]
|
||||
MR 0031 0034 0031(BS) 0032(BS) 0033(BS) 0034(BS) [MARKSTOP]
|
||||
Reference in New Issue
Block a user