diff --git a/Cs_HMI/AGVLogic/AGVMapEditor/Forms/MainForm.cs b/Cs_HMI/AGVLogic/AGVMapEditor/Forms/MainForm.cs index a08a434..b442903 100644 --- a/Cs_HMI/AGVLogic/AGVMapEditor/Forms/MainForm.cs +++ b/Cs_HMI/AGVLogic/AGVMapEditor/Forms/MainForm.cs @@ -623,22 +623,8 @@ namespace AGVMapEditor.Forms if (result.Success) { - this._mapCanvas.Nodes = result.Nodes; - // 맵 캔버스에 데이터 설정 - _mapCanvas.Nodes = this._mapCanvas.Nodes; - _mapCanvas.Labels = result.Labels; // 추가 - _mapCanvas.Images = result.Images; // 추가 - _mapCanvas.Marks = result.Marks; - _mapCanvas.Magnets = result.Magnets; - // RfidMappings 제거됨 - MapNode에 통합 - - // 🔥 맵 설정 적용 (배경색, 그리드 표시) - if (result.Settings != null) - { - _mapCanvas.BackColor = System.Drawing.Color.FromArgb(result.Settings.BackgroundColorArgb); - _mapCanvas.ShowGrid = result.Settings.ShowGrid; - } + _mapCanvas.SetMapLoadResult(result); // 현재 파일 경로 업데이트 _currentMapFile = filePath; @@ -651,8 +637,8 @@ namespace AGVMapEditor.Forms UpdateNodeList(); RefreshNodeConnectionList(); - // 맵 로드 후 자동으로 맵에 맞춤 - _mapCanvas.FitToNodes(); + + UpdateStatusBar($"맵 파일을 성공적으로 로드했습니다: {Path.GetFileName(filePath)}"); } @@ -696,7 +682,11 @@ namespace AGVMapEditor.Forms ShowGrid = _mapCanvas.ShowGrid }; - if (MapLoader.SaveMapToFile(filePath, this._mapCanvas.Nodes, _mapCanvas.Labels, _mapCanvas.Images, _mapCanvas.Marks, _mapCanvas.Magnets, settings)) + if (MapLoader.SaveMapToFile(filePath, + _mapCanvas.Nodes, _mapCanvas.Labels, + _mapCanvas.Images, _mapCanvas.Marks, + _mapCanvas.Magnets, + settings)) { // 현재 파일 경로 업데이트 _currentMapFile = filePath; @@ -874,7 +864,7 @@ namespace AGVMapEditor.Forms else displayText = $"[{node.Id}] {item.StationType}"; } - else if(node.Type == NodeType.Label) + else if (node.Type == NodeType.Label) { var item = node as MapLabel; displayText = $"{item.Type.ToString().ToUpper()} - {item.Text}"; diff --git a/Cs_HMI/AGVLogic/AGVMapEditor/Forms/MainForm.resx b/Cs_HMI/AGVLogic/AGVMapEditor/Forms/MainForm.resx index ab2891a..eed8d00 100644 --- a/Cs_HMI/AGVLogic/AGVMapEditor/Forms/MainForm.resx +++ b/Cs_HMI/AGVLogic/AGVMapEditor/Forms/MainForm.resx @@ -145,15 +145,15 @@ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 - YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHrSURBVDhPldBNiBJhHMfx/ykKKohQ18tEhygyWGQRJwh1 - fcHAt7qJ3TOj6FJ0akP06C1kz0EdDLbS3IbaxJdhA2V3jaRiYfPeIXPGeTss/mqElfbZ2HY/p/8883wf - Hh6iAxh/5I+pq7M32fV9aeIcp4pzd8xZWb0UUZuOrjnrTe6s3OAOdpgqzn5WRYegNs5X9Y1rUJrcg1Hd - PhzVTqfYvf+ktC4s6J9SML7dhbH1CNp6BMP3J7Ufdctxdu+U0r44o4qOmto8B30jDuN7Dq1ncXTLN7BV - vwWtMw/pw1FI72iRbXdRGtyC1r2O1vMEDMPAeDzG+usU9K+38UugL4MqnWGbKbk1Y5FrNiEajaLT6UDX - dfR6PdSfRmBs3odUpZH0lni2m9BW7Nxo5VQ5kUggnU6jUCigVCqhujgPY/MhBsu0LS3T2rBCj9l2Kh6P - T2Ke5+H3+xGLxaCuRSDX7JCFExhUiGObqZ3Y7XbD5/PBarWaV34yeEPbUoUuS2XqDl9Shu0mkskk8vn8 - JPZ6vbBYLDDX5QpdkV6RYM54QUd+LtFVtqVwOIxcLrcn3jFcotDf37sEAgFks1m4XC54PJ498b4ymQz6 - /T6cTidCodDhYpN5QLvdRrFYnDwY+/+/gsHgPfPaNpvt8PEfvwFcBQshDC9YfwAAAABJRU5ErkJggg== + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHrSURBVDhPldBLaBNBHMfx/0kUVBBJ0lxWPIhihBJKyAqS + pHkQIS+9hXg3RhQviicrITnmJqFnQQ8RqiamRqkhj6VCQtuIQaVQcxdq3d3s61Dy0w002KnU9nP67+x8 + h2GIDmD0kT+mLk/fZNf3pQkznCrM3DFnZflSRG05euast7izcpM72GGqMP1ZFRw1tXm+qq9dg9LiHgwb + dnFYP51i9/6T0r4wp39Kwfh2F8bGI2irEYjvTmo/Gpbj7N4JpXNxShUcdbV1DvpaHMb3HNrP4uiVb2Cj + cQtadxbSh6OQ3tM82+6iNLk5rXcd7ecJGIaB0WiE1dcp6F9v41eNvmxV6QzbTMjtKYtct9Wi0Si63S50 + XUe/30fjaQTG+n1IVRpKb4lnuzFtyc4Nl06VE4kE0uk0CoUCSqUSqvOzMNYfYnORtqVFWhEr9JhtJ+Lx + +DjmeR5+vx+xWAzqSgRy3Q65dgJbFeLYZmIndrvd8Pl8sFqt5pWfbL6hbalCl6Uy9cSXlGG7sWQyiXw+ + P469Xi8sFgvMdblCV6RXVDNnvKAjPxfoKttSOBxGLpfbE+8QFyj09/cugUAA2WwWLpcLHo9nT7yvTCaD + wWAAp9OJUCh0uNhkHtDpdFAsFscPxv7/r2AweM+8ts1mO3z8x29OYwsb4/6fnQAAAABJRU5ErkJggg== @@ -174,75 +174,75 @@ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 - YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHISURBVDhPnZJfb9JgFMb3JbzV+C2WNPtk3jgXs0vdsuAW - 7zajKaEXTWjhvShpilBIA+GvtAWxIyAgJjNBW6WQx5yXtBPREX2SpnnPe57fc9KevXa7jVarhUajgXq9 - jlqthmq1ikqlQu97e7vUbDaxWq22nuFwyCGGYdwNoWQyDAYDnux5XgyhmmVZd0NoZGqmNDqXy+XKfD5H - GIa8TkDTNP8OodRfGn8UCoVBEAT49PEbCmzK7/r9PvL5/J8hlByNvFwuQebP0wAvjlycPHLwNruG9Ho9 - 6LoOVVU3IZZl+bPZjDctFguenHji4vJZH6/PPJwfdWOI67rQNG0TUiqVDkzT9KfTdZNdv+HJVycfIJ5f - b0EcxwFjbBNSLBYFXdf9yWTCm7qtL0gcOnh1egtJHLow1PV9p9OBoigQRfEWYhiGoGmaPx6PtyBvEh5e - HvdwcfwOYbjkU2QyGaRSqQcxgJTL5QTGmD8ajWLI2WMbF0+7uDrt4us8iM2iKD7cMEdijAnZbNanTeQf - rnmDy+fv8T0Id5sjKYoipNNpnzYx+sVkVlV1tzmSLMv7siz7tGC2bf+bOZIkSfuSJPn/ZY5EkGQyef/3 - eqSfzGgWuCCdbTAAAAAASUVORK5CYII= + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHISURBVDhPnZJfa9pgFMb7JXa7sW9RkH2y3awro5ddS3Et + u2vHRsRcBEw0iEHiYpSg+Hcm0blUtOocdOCWbEZ5xnnlTXFule2BEN7znuf3HJKz12630Wq10Gg0UK/X + UavVUK1WUalU6P1gb5eazSZWq9XWMxwOGUTX9fshlEyGwWDAkj3PiyBUsyzrfgiNTM2URudyuVyZz+cI + w5DVCWia5t8hlMobDcP4aRjGIAgCfL75DkOdsrt+v49CofBnCCXzkZfLJcj8ZRrg1aGLk6cO3mfWkF6v + h3w+D0VRNiGWZfmz2Yw1LRYLlhx/7uLyuI+3Zx7OD7sRxHVd5HK5TUipVHpimqY/na6b7PotS746+QTh + /HoL4jgOVFXdhBSLxZimaf5kMmFN3dZXxA8cvDm9g8QPXOjK+r7T6UCWZQiCcAfRdT2WzWb98Xi8BXkX + 9/D6qIeLow8IwyWbIp1OI5lMPooAJE3TYplMxh+NRhHk7JmNixddXJ128W0eRGZBEB5vmLlUVWUQ2kT2 + 4Zq3uHz5ET+CcLeZS5blWCqV8mkT+S8ms6Iou81ckiTtS5Lk04LZtv1vZi5RFPdFUfT/y8xFkEQi8fD3 + Otcvn84Wo7k6b1AAAAAASUVORK5CYII= iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 - YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAG6SURBVDhPnZLditpQFIXnJXrb0rcYCH2wXhSZwly2U0rp - O0TMRUr+zkUyaUSjBEWNwR9ExGpVvCiDzAl6cljlnCGZWtuRdkEI2Xuvb23IvkiSBP1+H71eD91uF51O - B+12G61WS7yfXZxTHMfgnJ88i8VCQoIgeBoikoVhPp/L5NlsVkBELYqipyFiZTEs0sR3s9ls7XY7MMZk - XQDDMPw7RKT+Mriv1WrzNE3BfnwHSxzZm06nqFarf4aI5HzlLMsgzPxug8y+QvblNVhiy95kMoHv+7As - 6xgSRRHdbrdy6HA4yOTMKoF778C/fgS3rwrIeDyG67rHkEaj8SoMQ7rZbB4g3zoymd/egFc/n0BGoxEI - IceQer2u+L5P1+v1A2QZg5kl8NsPjxCrBBYbsj8YDGCaJlRVfYQEQaC4rktXq9UpJPgETq5xb16DZ0xu - Yds2KpXKiwIg5HmeQgihy+WygByMN+DOW6TuDfZ0V5hVVX15ZM5FCFEcx6HiEiVk0QPz3oPv0/PmXKZp - KoZhUHGJ+S8WZsuyzptz6bp+qes6FQc2HA7/zZxL07RLTdPof5lzCUi5XH7+ez3XT8GUHaqSv5fjAAAA + 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 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 - 049v7HkwfwEdnYukisiEOQAAAABJRU5ErkJggg== + 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 @@ -263,15 +263,15 @@ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 - YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHxSURBVDhP7Y7Ni1JhGMVvXadgWrWzRcS0S0FyFYHgOH4g - fgQXQl0NRIr1SqB9zGh34XAXs7gwgRfMVUILsY22mhmlDHTcRGQrbXVnkCKkSIOmtMk88QyMmDj/QQde - eDjndw4vx/3XWHa7/Qxj7IEkSd1sNgtRFNuMsSuMMa0oiip5lBFD7D9lnU53yu12y6lUCp1OB6qqolqt - IplM7oqiWGu1WgPy6vU6iCGWOuMBo9F4LRqNfms2m91AILDldDqHoVAIiqIgkUiAbvIoy+VybWKpMzkg - S5LUVxQl7PF45k0m0/dKpYJGo4FarYZCoQDyKCOGWOqMBwwGw+1wONxLp9P3BUEoWyyWA4fDAUEQDh/d - 5FFGDLHUGQ/o9fpLXq/3vSzLvXw+/7VYLCKTySAWiyEejx/epXWGV9cv/Hm5NIfnjvmDp4unH40HOI7j - zWbzZZfLtRcMBvcjkUifMfbL5/Mt+/1+6+Nl6+Ddvavob25g1Crhx7O7eBPRDV9YNXcmR47Vto1Xf25u - AIoXWD0LrC/gi7yILQu/O83OVHlJMxq9LWJSvaQW5E+zM1W28R/2n9wEkloMVjh0Vzi0b/Eo2fmP0+xM - 7fjPrb2+cfH3p9UFfE7MQQ2eQMWlGW7bTj6cZo/Vjv98vGzl9+jb9KOj8l/xBxqBzigbjwAAAABJRU5E + 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== @@ -287,18 +287,18 @@ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 - YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAKYSURBVDhPhZBfSFNRHMfvY0RPEfQSFD2tngKj9WBZmDqX - em1t6tJtDV2WSRZGKN1ScV7JyhVZMnuYIJua0aXQ1Oamzj/DtnvuMlKocJ47aMWI9FwrCC4njrqX66Iv - nJfD9/P9fX8/itpUyFOm5nvNHOi1JMJ9VjnkschzLlMi2FXMBTvOqpO+lBI8Jpvgtsb5HjMIdZ9jwi6j - asapUwW79EzQaQDTncXxgKPQpuTWRSZvwKUsH0UHwDLSAyjVCKJUHYGr+fNwZX/gAc1OOQrj/rvarU14 - j5kjk8NRpAIQXQIQ1QsiugHEtesClK4JolQqRH/sm2ynga9Vyyl5CngsiVB3CROJIR2AqG4Dlmo34Wog - oioApVO+Fi3jZfMTSp4K91rksNOoEkTpcnJqcgXSSICokrQYachW+dvyZCVPvXWbZXIwYiQgWJaukDAe - oovrfyKqiECpmAT42NytAbOu0kTwiZ7hxTXNRgtUtQ7HkA2IqFyAq+d5ER0fvpnFjDDZW1eY7dRzsx0G - QA4lQKloE6wQxFUrgQFE9LvYzz2vm3J/D9Vlzil5KuAoUE8/1sUn2mmWX/q1V4hJJ0llAUqGCETpBJ5w - 6P7MD9Rgf+0J/KIm3ajMoMbbCmwBBx333TkNvM05DNmXvOH6TGb8Hi0vDNbjWNiJl6bv47FmGr8sV+9W - ZlB+Vqt+Y9dyoy25CXKsMbtGHryds+JtysOfxlvx1w89+NuiG3/0NuIB6zHZkyoklbirGabJ9hIcnXHg - +PtuvBx8hOeeWjGbpfr86kLadqU/pZ5XpZvG7Gfw4tAtvDDUgKceFuGBysPfnxkO7lB6/6l+2xGTuywN - jzZqcJ9V/cVtTNul9PxX/eVHd3aVHLK7LBnbkn9/ARsspRTlWFT8AAAAAElFTkSuQmCC + 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 @@ -307,7 +307,7 @@ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - wwAADsMBx2+oZAAAAU1JREFUOE9j+E8GYEAGIIGrzz+AJYrW6IIxCMw6cO1/x/oTYFw2fQsKRjEE2eTp + wQAADsEBuJFr7QAAAU1JREFUOE9j+E8GYEAGIIGrzz+AJYrW6IIxCMw6cO1/x/oTYFw2fQsKRjEE2eTp p73/Tz3pgSz0/+nbryi4c/eb/+EFE8FycANAtk475fV/0nH3//1HXP53HrCHu+TA/b9w/OnbL7ABKIbA DJhw1PV/9wGH/y17rP/XbTNHMWD3HQgGGQDDQYnVCANgoGGHxf+qzcbIQnADtt36A8Ybr//BNAAUYCCA HIgwZ6NrXn3lN6YBoJBGBzBnwzTCNMMM8AzNBquDGwASxGcrCC+5CMGg2EAxABS3bz5+wzAAm+Z55yAG @@ -319,7 +319,7 @@ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - wwAADsMBx2+oZAAAAhFJREFUOE/dz71rU1EYBvAO7t38E1xaizQfIh1cBHFyEV2sDiJoyCApFBVBO6QN + wQAADsEBuJFr7QAAAhFJREFUOE/dz71rU1EYBvAO7t38E1xaizQfIh1cBHFyEV2sDiJoyCApFBVBO6QN iIGSqCDSKpSKtlb8QBCHEge1rW1iE733Jmlsmhh6g839vud+nHseSapJXFwFX3g45wzv7z1vT8//Ufwr X6acGna23p3tyhmn8Hpoo/h0YO/GE1/vH3nr611cPLynDQgvghZjHgAGsGZ27y6po/o+ZFc+hKzfqa1e JtWlkTL/fPBkB3gWkBklsKpxGKVJGJvTMMvzYK4E5ulgVAOjyu7bbYBR2xYWgrk2kH8cUD1HgVFKQi8m @@ -334,7 +334,7 @@ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - wwAADsMBx2+oZAAAAUZJREFUOE+lkzFLw0AYhvt3HP0DLqWDdCuECjooBRdBXJwEZ3VRBykUBCfRpSi4 + wQAADsEBuJFr7QAAAUZJREFUOE+lkzFLw0AYhvt3HP0DLqWDdCuECjooBRdBXJwEZ3VRBykUBCfRpSi4 uXTSIYNiRSgFBxU6qHSoCMHiyfPJm9zFgKAHT0Mu9z53+fK15P4wSv5gojcYuvbNo3EU37tW587YaF8a q3unAYGEG4JInl7e3HKrFsCc5rlunj+7+spuJpFAiw4HiynN7mwqamxX3OsoMUEg4Ydjs0Ds9+cNBFud yK4SwOg9cbWFtUzA+7Lg4uHTkGgnrqdwf9YbG4UCJiWQhJ0JcwoJjrsfocA/+m8QlmA6WsoEfuH8owoF @@ -369,7 +369,7 @@ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - wwAADsMBx2+oZAAAAyZJREFUOE9tku1PW1Ucx+8r/Tt86wvRxRjjEmPMULeZNAsQXczipoCdDhxZlg0Z + wQAADsEBuJFr7QAAAyZJREFUOE9tku1PW1Ucx+8r/Tt86wvRxRjjEmPMULeZNAsQXczipoCdDhxZlg0Z m7CisBYfthcDXLrBlJktUbIBGQ+hAn2cq3uga3lqBxQuUCil9OHc9t7bj2mjy3B+k29OTnK+n9/35Byp +NTowffN7iGDxb1hsLgxWNzC0OoWBotHGMwusafF4dt5fPzHF8uuPyf9n3Y32cfXtsR6NCG0aELhX69v 5S0IyFvU31yg/MJ06r2q/uf/m5f2Njsi8VRGc81l8SyouOdUHI8zjM4o3PYnUVTonUxT3zPPp+en089A diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs index f1c88dc..bff437e 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs @@ -23,7 +23,7 @@ namespace AGVNavigationCore.Controls private const int NODE_SIZE = 24; private const int NODE_RADIUS = NODE_SIZE / 2; private const int GRID_SIZE = 20; - private const float CONNECTION_WIDTH = 2.0f; + private const float CONNECTION_WIDTH = 1.0f; private const int SNAP_DISTANCE = 10; private const int AGV_SIZE = 40; private const int CONNECTION_ARROW_SIZE = 8; @@ -355,6 +355,28 @@ namespace AGVNavigationCore.Controls } } + /// + /// Map file loading 결과를 셋팅합니다 + /// + /// + public void SetMapLoadResult(MapLoader.MapLoadResult result) + { + this.Nodes = result.Nodes; + this.Labels = result.Labels; // 추가 + this.Images = result.Images; // 추가 + this.Marks = result.Marks; + this.Magnets = result.Magnets; + + // 🔥 맵 설정 적용 (배경색, 그리드 표시) + if (result.Settings != null) + { + this.BackColor = Color.FromArgb(result.Settings.BackgroundColorArgb); + this.ShowGrid = result.Settings.ShowGrid; + } + + this.FitToNodes(); + } + /// /// 노드 목록 /// @@ -598,7 +620,7 @@ namespace AGVNavigationCore.Controls _gridBrush = new SolidBrush(Color.LightGray); // 펜 - _connectionPen = new Pen(Color.DarkBlue, CONNECTION_WIDTH); + _connectionPen = new Pen(Color.White, CONNECTION_WIDTH); _connectionPen.DashStyle = DashStyle.Dash; _connectionPen.EndCap = LineCap.ArrowAnchor; diff --git a/Cs_HMI/AGVLogic/AGVSimulator/Forms/SimulatorForm.cs b/Cs_HMI/AGVLogic/AGVSimulator/Forms/SimulatorForm.cs index 09f7c04..0ca0f59 100644 --- a/Cs_HMI/AGVLogic/AGVSimulator/Forms/SimulatorForm.cs +++ b/Cs_HMI/AGVLogic/AGVSimulator/Forms/SimulatorForm.cs @@ -108,7 +108,6 @@ namespace AGVSimulator.Forms } private UnifiedAGVCanvas _simulatorCanvas; - private List _mapNodes; private AGVPathfinder _advancedPathfinder; private List _agvList; private SimulationState _simulationState; @@ -158,7 +157,7 @@ namespace AGVSimulator.Forms _config = SimulatorConfig.Load(); // 데이터 초기화 - _mapNodes = new List(); + _agvList = new List(); _simulationState = new SimulationState(); _currentMapFilePath = string.Empty; @@ -305,14 +304,14 @@ namespace AGVSimulator.Forms private void OnAddAGV_Click(object sender, EventArgs e) { - if (_mapNodes == null || _mapNodes.Count == 0) + if (_simulatorCanvas.Nodes == null || _simulatorCanvas.Nodes.Count == 0) { MessageBox.Show("먼저 맵을 로드해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } var agvId = $"AGV{_agvList.Count + 1:D2}"; - var startPosition = _mapNodes.First().Position; // 첫 번째 노드에서 시작 + var startPosition = _simulatorCanvas.Nodes.First().Position; // 첫 번째 노드에서 시작 var newAGV = new VirtualAGV(agvId, startPosition); _agvList.Add(newAGV); @@ -396,7 +395,7 @@ namespace AGVSimulator.Forms if (_advancedPathfinder == null) { - _advancedPathfinder = new AGVPathfinder(_mapNodes); + _advancedPathfinder = new AGVPathfinder(_simulatorCanvas.Nodes); } // 현재 AGV 방향 가져오기 @@ -420,11 +419,11 @@ namespace AGVSimulator.Forms // 도킹 검증이 없는 경우 추가 검증 수행 if (advancedResult.DockingValidation == null || !advancedResult.DockingValidation.IsValidationRequired) { - advancedResult.DockingValidation = DockingValidator.ValidateDockingDirection(advancedResult, _mapNodes); + advancedResult.DockingValidation = DockingValidator.ValidateDockingDirection(advancedResult, _simulatorCanvas.Nodes); } //마지막대상이 버퍼라면 시퀀스처리를 해야한다 - if(targetNode.Type == NodeType.Buffer) + if(targetNode.StationType == StationType.Buffer) { var lastDetailPath = advancedResult.DetailedPath.Last(); if(lastDetailPath.NodeId == targetNode.Id) //마지막노드 재확인 @@ -598,13 +597,13 @@ namespace AGVSimulator.Forms /// private MapNode FindClosestNode(Point position) { - if (_mapNodes == null || _mapNodes.Count == 0) + if (_simulatorCanvas.Nodes == null || _simulatorCanvas.Nodes.Count == 0) return null; MapNode closestNode = null; double closestDistance = double.MaxValue; - foreach (var node in _mapNodes) + foreach (var node in _simulatorCanvas.Nodes) { var distance = Math.Sqrt(Math.Pow(node.Position.X - position.X, 2) + Math.Pow(node.Position.Y - position.Y, 2)); @@ -645,7 +644,7 @@ namespace AGVSimulator.Forms var currentDirection = directionItem?.Direction ?? AgvDirection.Forward; // 중복 RFID 확인 - var existingNode = _mapNodes?.FirstOrDefault(n => n.RfidId.Equals(rfidId, StringComparison.OrdinalIgnoreCase)); + var existingNode = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.RfidId.Equals(rfidId, StringComparison.OrdinalIgnoreCase)); if (existingNode != null) { // 이미 존재하는 노드로 이동 @@ -723,15 +722,14 @@ namespace AGVSimulator.Forms Id = newNodeId, RfidId = rfidId, Position = new Point(newX, newY), - Type = NodeType.Normal, IsActive = true }; // 맵에 추가 - if (_mapNodes == null) - _mapNodes = new List(); + if (_simulatorCanvas.Nodes == null) + _simulatorCanvas.Nodes = new List(); - _mapNodes.Add(newNode); + _simulatorCanvas.Nodes.Add(newNode); // 이전 노드와 연결 생성 if (_lastScannedNode != null) @@ -748,7 +746,7 @@ namespace AGVSimulator.Forms selectedAGV.SetPosition(newNode, currentDirection); // 캔버스 업데이트 - _simulatorCanvas.Nodes = _mapNodes; + _simulatorCanvas.Nodes = _simulatorCanvas.Nodes; // 화면을 새 노드 위치로 이동 _simulatorCanvas.PanToNode(newNode.Id); @@ -763,7 +761,7 @@ namespace AGVSimulator.Forms // UI 업데이트 UpdateNodeComboBoxes(); - _statusLabel.Text = $"노드 생성: {newNode.Id} (RFID: {rfidId}) [{GetDirectionSymbol(currentDirection)}] - 총 {_mapNodes.Count}개"; + _statusLabel.Text = $"노드 생성: {newNode.Id} (RFID: {rfidId}) [{GetDirectionSymbol(currentDirection)}] - 총 {_simulatorCanvas.Nodes.Count}개"; _rfidTextBox.Text = ""; Program.WriteLine($"[맵 스캔] 노드 생성 완료: {newNode.Id} (RFID: {rfidId}) at ({newX}, {newY}), 방향: {currentDirection}"); @@ -864,7 +862,7 @@ namespace AGVSimulator.Forms } // RFID에 해당하는 노드 직접 찾기 - var targetNode = _mapNodes?.FirstOrDefault(n => n.RfidId.Equals(rfidId, StringComparison.OrdinalIgnoreCase)); + var targetNode = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.RfidId.Equals(rfidId, StringComparison.OrdinalIgnoreCase)); if (targetNode == null) { MessageBox.Show($"RFID '{rfidId}'에 해당하는 노드를 찾을 수 없습니다.\n\n사용 가능한 RFID 목록:\n{GetAvailableRfidList()}", @@ -947,10 +945,10 @@ namespace AGVSimulator.Forms private string GetAvailableRfidList() { - if (_mapNodes == null || _mapNodes.Count == 0) + if (_simulatorCanvas.Nodes == null || _simulatorCanvas.Nodes.Count == 0) return "매핑된 RFID가 없습니다."; - var nodesWithRfid = _mapNodes.Where(n => n.HasRfid()).ToList(); + var nodesWithRfid = _simulatorCanvas.Nodes.Where(n => n.HasRfid()).ToList(); if (nodesWithRfid.Count == 0) return "RFID가 할당된 노드가 없습니다."; @@ -977,13 +975,13 @@ namespace AGVSimulator.Forms { Console.WriteLine($"Map File Load : {filePath}"); - _mapNodes = result.Nodes; + _simulatorCanvas.Nodes = result.Nodes; _currentMapFilePath = filePath; // RFID 자동 할당 제거 - 에디터에서 설정한 값 그대로 사용 // 시뮬레이터 캔버스에 맵 설정 - _simulatorCanvas.Nodes = _mapNodes; + _simulatorCanvas.SetMapLoadResult(result);//.Nodes = _simulatorCanvas.Nodes; // 맵 설정 적용 (배경색, 그리드 표시) if (result.Settings != null) @@ -1051,9 +1049,9 @@ namespace AGVSimulator.Forms _startNodeCombo.Items.Clear(); _targetNodeCombo.Items.Clear(); - if (_mapNodes != null) + if (_simulatorCanvas.Nodes != null) { - foreach (var node in _mapNodes) + foreach (var node in _simulatorCanvas.Nodes) { if (node.IsActive && node.HasRfid()) { @@ -1109,7 +1107,7 @@ namespace AGVSimulator.Forms // RFID 위치 설정 관련 var hasSelectedAGV = _agvListCombo.SelectedItem != null; - var hasRfidNodes = _mapNodes != null && _mapNodes.Any(n => n.HasRfid()); + var hasRfidNodes = _simulatorCanvas.Nodes != null && _simulatorCanvas.Nodes.Any(n => n.HasRfid()); _setPositionButton.Enabled = hasSelectedAGV && hasRfidNodes; _rfidTextBox.Enabled = hasSelectedAGV && hasRfidNodes; @@ -1182,7 +1180,7 @@ namespace AGVSimulator.Forms // 경로 예측 기반 LiftCalculator를 사용하여 리프트 방향 계산 var liftInfo = AGVNavigationCore.Utils.LiftCalculator.CalculateLiftInfoWithPathPrediction( - currentPos, prevPos.Value, agv.CurrentDirection, _mapNodes); + currentPos, prevPos.Value, agv.CurrentDirection, _simulatorCanvas.Nodes); // 이동 각도 계산 (표시용) var moveAngleRad = Math.Atan2(dy, dx); @@ -1226,7 +1224,7 @@ namespace AGVSimulator.Forms // 경로 예측 기반 LiftCalculator를 사용하여 리프트 방향 계산 var liftInfo = AGVNavigationCore.Utils.LiftCalculator.CalculateLiftInfoWithPathPrediction( - currentPos, targetPos.Value, agv.CurrentDirection, _mapNodes); + currentPos, targetPos.Value, agv.CurrentDirection, _simulatorCanvas.Nodes); // 도킹 방향 정보 추가 string dockingInfo = dockingDirection == DockingDirection.Forward ? "전진도킹" : "후진도킹"; @@ -1259,7 +1257,7 @@ namespace AGVSimulator.Forms /// private string GetRfidByNodeId(string nodeId) { - var node = _mapNodes?.FirstOrDefault(n => n.Id == nodeId); + var node = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.Id == nodeId); return node?.HasRfid() == true ? node.RfidId : nodeId; } @@ -1268,7 +1266,7 @@ namespace AGVSimulator.Forms /// private string GetDisplayName(string nodeId) { - var node = _mapNodes?.FirstOrDefault(n => n.Id == nodeId); + var node = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.Id == nodeId); if (node != null && !string.IsNullOrEmpty(node.RfidId)) { return node.RfidId; @@ -1541,7 +1539,7 @@ namespace AGVSimulator.Forms private async void toolStripButton1_Click(object sender, EventArgs e) { // 맵과 AGV 확인 - if (_mapNodes == null || _mapNodes.Count == 0) + if (_simulatorCanvas.Nodes == null || _simulatorCanvas.Nodes.Count == 0) { MessageBox.Show("맵 데이터가 없습니다. 먼저 맵을 로드해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Warning); @@ -1557,8 +1555,7 @@ namespace AGVSimulator.Forms } // 도킹 타겟 노드 찾기 - var dockingTargets = _mapNodes.Where(n => - n.Type == NodeType.Charging || n.Type == NodeType.Loader || n.Type == NodeType.UnLoader || n.Type == NodeType.Clearner || n.Type == NodeType.Buffer).ToList(); + var dockingTargets = _simulatorCanvas.Nodes.Where(n => n.isDockingNode).ToList(); if (dockingTargets.Count == 0) { @@ -1666,7 +1663,7 @@ namespace AGVSimulator.Forms private PathTestLogItem CreateTestResultFromUI(MapNode prevNode, MapNode targetNode, string directionName, (bool result, string message) calcResult) { - var currentNode = _mapNodes.FirstOrDefault(n => n.Id == + var currentNode = _simulatorCanvas.Nodes.FirstOrDefault(n => n.Id == (_agvListCombo.SelectedItem as VirtualAGV)?.CurrentNodeId); var logItem = new PathTestLogItem @@ -1675,7 +1672,7 @@ namespace AGVSimulator.Forms MotorDirection = directionName, CurrentPosition = GetNodeDisplayName(currentNode), TargetPosition = GetNodeDisplayName(targetNode), - DockingPosition = targetNode.Type == NodeType.Charging ? "충전기" : "장비" + DockingPosition = targetNode.StationType == StationType.Charger ? "충전기" : "장비" }; if (calcResult.result) @@ -1685,7 +1682,7 @@ namespace AGVSimulator.Forms if (currentPath != null && currentPath.Success) { // 도킹 검증 - var dockingValidation = DockingValidator.ValidateDockingDirection(currentPath, _mapNodes); + var dockingValidation = DockingValidator.ValidateDockingDirection(currentPath, _simulatorCanvas.Nodes); if (dockingValidation.IsValid) { @@ -1726,7 +1723,7 @@ namespace AGVSimulator.Forms var pairs = new List<(MapNode, MapNode)>(); var processedPairs = new HashSet(); - foreach (var nodeA in _mapNodes) + foreach (var nodeA in _simulatorCanvas.Nodes) { if (nodeA.ConnectedMapNodes == null || nodeA.ConnectedMapNodes.Count == 0) continue; @@ -1952,9 +1949,9 @@ namespace AGVSimulator.Forms if (result == DialogResult.Yes) { // 기존 맵 데이터 삭제 - _mapNodes?.Clear(); - _mapNodes = new List(); - _simulatorCanvas.Nodes = _mapNodes; + _simulatorCanvas.Nodes?.Clear(); + _simulatorCanvas.Nodes = new List(); + _simulatorCanvas.Nodes = _simulatorCanvas.Nodes; _currentMapFilePath = string.Empty; UpdateNodeComboBoxes(); _statusLabel.Text = "맵 초기화 완료 - 스캔 모드 시작"; @@ -1978,16 +1975,16 @@ namespace AGVSimulator.Forms _isMapScanMode = false; btMakeMap.Text = "맵 생성"; btMakeMap.BackColor = SystemColors.Control; - _statusLabel.Text = $"맵 스캔 완료 - {_mapNodes?.Count ?? 0}개 노드 생성됨"; + _statusLabel.Text = $"맵 스캔 완료 - {_simulatorCanvas.Nodes?.Count ?? 0}개 노드 생성됨"; - Program.WriteLine($"[맵 스캔] 스캔 모드 종료 - 총 {_mapNodes?.Count ?? 0}개 노드"); + Program.WriteLine($"[맵 스캔] 스캔 모드 종료 - 총 {_simulatorCanvas.Nodes?.Count ?? 0}개 노드"); // 맵 저장 권장 - if (_mapNodes != null && _mapNodes.Count > 0) + if (_simulatorCanvas.Nodes != null && _simulatorCanvas.Nodes.Count > 0) { var saveResult = MessageBox.Show( $"맵 스캔이 완료되었습니다.\n\n" + - $"생성된 노드: {_mapNodes.Count}개\n\n" + + $"생성된 노드: {_simulatorCanvas.Nodes.Count}개\n\n" + "맵을 저장하시겠습니까?", "맵 저장", MessageBoxButtons.YesNo, @@ -2011,11 +2008,11 @@ namespace AGVSimulator.Forms try { // MapLoader의 표준 저장 메서드 사용 (AGVMapEditor와 동일한 형식) - bool success = MapLoader.SaveMapToFile(filePath, _mapNodes); + bool success = MapLoader.SaveMapToFile(filePath, _simulatorCanvas.Nodes); if (success) { - Program.WriteLine($"[맵 저장] 파일 저장 완료: {filePath} ({_mapNodes.Count}개 노드)"); + Program.WriteLine($"[맵 저장] 파일 저장 완료: {filePath} ({_simulatorCanvas.Nodes.Count}개 노드)"); } else { @@ -2032,7 +2029,7 @@ namespace AGVSimulator.Forms private void btMapSaveAs_Click(object sender, EventArgs e) { // 맵 데이터 확인 - if (_mapNodes == null || _mapNodes.Count == 0) + if (_simulatorCanvas.Nodes == null || _simulatorCanvas.Nodes.Count == 0) { MessageBox.Show("저장할 맵 데이터가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information); diff --git a/Cs_HMI/Project/PUB.cs b/Cs_HMI/Project/PUB.cs index 5ab0f34..e41d1a8 100644 --- a/Cs_HMI/Project/PUB.cs +++ b/Cs_HMI/Project/PUB.cs @@ -31,7 +31,7 @@ namespace Project public static bool AutRebootAlreay = false; public static bool DriveSpeed = false; public static AGVNavigationCore.Controls.UnifiedAGVCanvas _mapCanvas; - public static List _mapNodes; + //public static List _mapNodes; /// /// 다음 작업 명령 (PickOn/PickOff) @@ -612,18 +612,21 @@ namespace Project #region VirtualAGV 실제 데이터 동기화 public static MapNode FindByNodeID(string nodeidx) { + var _mapNodes = PUB._mapCanvas.Nodes; if (_mapNodes == null || _mapNodes.Any() == false) return null; if (nodeidx.isEmpty()) return null; return _mapNodes.Where(t => t.Id.Equals(nodeidx)).FirstOrDefault(); } public static MapNode FindByRFID(string rfidValue) { + var _mapNodes = PUB._mapCanvas.Nodes; if (_mapNodes == null || _mapNodes.Any() == false) return null; if (rfidValue.isEmpty()) return null; return _mapNodes.Where(t => t.RfidId.Equals(rfidValue)).FirstOrDefault(); } public static List FindByNodeAlias(string alias) { + var _mapNodes = PUB._mapCanvas.Nodes; if (_mapNodes == null || _mapNodes.Any() == false) return null; if (alias.isEmpty()) return null; var lst = _mapNodes.Where(t => t.AliasName.Equals(alias)); @@ -632,6 +635,7 @@ namespace Project } public static List FindByNodeType(AGVNavigationCore.Models.MapNode type) { + var _mapNodes = PUB._mapCanvas.Nodes; if (_mapNodes == null || _mapNodes.Any() == false) return null; var lst = _mapNodes.Where(t => t.Type.Equals(type)); if (lst.Any() == false) return null; @@ -645,6 +649,7 @@ namespace Project /// 업데이트 성공 여부 public static bool UpdateAGVFromRFID(string rfidId, AgvDirection motorDirection = AgvDirection.Forward) { + var _mapNodes = PUB._mapCanvas.Nodes; if (_virtualAGV == null || _mapNodes == null) return false; // RFID에 해당하는 노드 찾기 @@ -670,6 +675,7 @@ namespace Project /// 업데이트 성공 여부 public static bool UpdateAGVToNode(string nodeId, AgvDirection motorDirection = AgvDirection.Forward) { + var _mapNodes = PUB._mapCanvas.Nodes; if (_virtualAGV == null || _mapNodes == null) return false; var node = _mapNodes.FirstOrDefault(n => n.Id == nodeId); diff --git a/Cs_HMI/Project/StateMachine/Step/_Util.cs b/Cs_HMI/Project/StateMachine/Step/_Util.cs index a52993e..e9498af 100644 --- a/Cs_HMI/Project/StateMachine/Step/_Util.cs +++ b/Cs_HMI/Project/StateMachine/Step/_Util.cs @@ -292,7 +292,7 @@ namespace Project // 만약 수행되지 않았다면 여기서 수행. if (pathResult.DockingValidation == null) { - pathResult.DockingValidation = AGVNavigationCore.Utils.DockingValidator.ValidateDockingDirection(pathResult, PUB._mapNodes); + pathResult.DockingValidation = AGVNavigationCore.Utils.DockingValidator.ValidateDockingDirection(pathResult, PUB._mapCanvas.Nodes); } // 검증 결과 확인 diff --git a/Cs_HMI/Project/StateMachine/_AGV.cs b/Cs_HMI/Project/StateMachine/_AGV.cs index 10d99d8..0931bac 100644 --- a/Cs_HMI/Project/StateMachine/_AGV.cs +++ b/Cs_HMI/Project/StateMachine/_AGV.cs @@ -60,11 +60,15 @@ namespace Project //if (chg_run && PUB.AGV.system1.agv_run) PUB.Speak("이동을 시작 합니다"); VAR.BOOL[eVarBool.AGVDIR_BACK] = PUB.AGV.data.Direction == 'B'; + var syncDir = PUB.AGV.data.Direction == 'B' ? AgvDirection.Backward : AgvDirection.Forward; // [Sync] Update VirtualAGV Direction - var syncDir = PUB.AGV.data.Direction == 'B' ? AgvDirection.Backward : AgvDirection.Forward; - if (PUB._virtualAGV.CurrentDirection != syncDir) - PUB.UpdateAGVDirection(syncDir); + if (PUB._virtualAGV != null) + { + if (PUB._virtualAGV.CurrentDirection != syncDir) + PUB.UpdateAGVDirection(syncDir); + } + // [Sync] Update VirtualAGV State AGVState syncState = AGVState.Idle; @@ -72,7 +76,7 @@ namespace Project else if (PUB.AGV.system1.Battery_charging) syncState = AGVState.Charging; else if (PUB.AGV.system1.agv_run) syncState = AGVState.Moving; - if (PUB._virtualAGV.GetCurrentState() != syncState) + if (PUB._virtualAGV != null && PUB._virtualAGV.GetCurrentState() != syncState) PUB.UpdateAGVState(syncState); if (VAR.BOOL[eVarBool.AGV_ERROR] != (agv_err > 0)) @@ -156,9 +160,9 @@ namespace Project case arDev.Narumi.DataType.TAG: { //자동 실행 중이다. - PUB.log.Add($"AGV 태그수신 : {PUB.AGV.data.TagNo}"); - PUB.Result.LastTAG = PUB.AGV.data.TagNo.ToString(); - + + PUB.Result.LastTAG = PUB.AGV.data.TagNo.ToString("0000"); + PUB.log.Add($"AGV 태그수신 : {PUB.AGV.data.TagNo} LastTag:{PUB.Result.LastTAG}"); //POT/NOT 보면 일단 바로 멈추게한다 if (PUB.Result.CurrentPos == ePosition.POT || PUB.Result.CurrentPos == ePosition.NOT) { @@ -168,7 +172,7 @@ namespace Project } //virtual agv setting - var CurrentNode = PUB._mapNodes.FirstOrDefault(t => t.RfidId.Equals(PUB.Result.LastTAG, StringComparison.OrdinalIgnoreCase)); + var CurrentNode = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.RfidId.Equals(PUB.Result.LastTAG, StringComparison.OrdinalIgnoreCase)); if (CurrentNode == null) { //없는 노드는 자동으로 추가한다 @@ -184,10 +188,10 @@ namespace Project }; // 맵 노드 리스트에 추가 - PUB._mapNodes.Add(newNode); + PUB._mapCanvas.Nodes.Add(newNode); // 캔버스에 노드 반영 (재설정) - PUB._mapCanvas.Nodes = PUB._mapNodes; + PUB._mapCanvas.Nodes = PUB._mapCanvas.Nodes; // 로그 기록 PUB.log.AddI($"RFID:{PUB.Result.LastTAG} 노드를 자동 추가했습니다 (NodeId: {newNodeId})"); @@ -217,7 +221,7 @@ namespace Project } //이 후 상황을 예측한다 - if (PUB._mapCanvas != null) + if (PUB._mapCanvas != null && PUB._virtualAGV != null) { var nextAction = PUB._virtualAGV.Predict(); var message = $"[다음 행동 예측]\n\n" + diff --git a/Cs_HMI/Project/StateMachine/_Xbee.cs b/Cs_HMI/Project/StateMachine/_Xbee.cs index e483d6d..a60030a 100644 --- a/Cs_HMI/Project/StateMachine/_Xbee.cs +++ b/Cs_HMI/Project/StateMachine/_Xbee.cs @@ -48,7 +48,7 @@ namespace Project if (data.Length > 4) { var currTag = System.Text.Encoding.Default.GetString(data, 1, data.Length - 1); - var node = PUB._mapNodes.FirstOrDefault(t => t.RfidId == currTag); + var node = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.RfidId == currTag); if (node == null) { PUB.log.AddE($"[{logPrefix}-SetCurrent] 노드정보를 찾을 수 없습니다 RFID:{currTag}"); @@ -111,7 +111,7 @@ namespace Project if (data.Length > 4) { var currTag = System.Text.Encoding.Default.GetString(data, 1, data.Length - 1); - var targetNode = PUB._mapNodes.FirstOrDefault(t => t.RfidId == currTag); + var targetNode = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.RfidId == currTag); //자동상태가아니라면 처리하지 않는다. @@ -131,7 +131,7 @@ namespace Project } ///출발지 - var startNode = PUB._mapNodes.FirstOrDefault(t => t.RfidId == PUB._virtualAGV.CurrentNode.Id); + var startNode = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.RfidId == PUB._virtualAGV.CurrentNode.Id); PUB._virtualAGV.StartNode = startNode; if (startNode == null) { @@ -150,7 +150,7 @@ namespace Project { //경로예측을 화면에 표시해준다. PUB._virtualAGV.SetPath(rltGoto.result); - var pathWithRfid = rltGoto.result.GetSimplePath().Select(nodeId => PUB._virtualAGV.GetRfidByNodeId(PUB._mapNodes, nodeId)).ToList(); + var pathWithRfid = rltGoto.result.GetSimplePath().Select(nodeId => PUB._virtualAGV.GetRfidByNodeId(PUB._mapCanvas.Nodes, nodeId)).ToList(); PUB.log.Add($"경로예측결과:{pathWithRfid}"); } } @@ -260,7 +260,7 @@ namespace Project AGVNavigationCore.PathFinding.Planning.AGVPathfinder _advancedPathfinder = null; (AGVNavigationCore.PathFinding.Core.AGVPathResult result, string message) CalcPath(MapNode startNode, MapNode targetNode) { - var _mapNodes = PUB._mapNodes; + var _mapNodes = PUB._mapCanvas.Nodes; // 시작 RFID가 없으면 AGV 현재 위치로 설정 if (startNode == null || targetNode == null) return (null, "시작 RFID와 목표 RFID를 선택해주세요."); diff --git a/Cs_HMI/Project/ViewForm/fAuto.cs b/Cs_HMI/Project/ViewForm/fAuto.cs index a349220..4b69b6e 100644 --- a/Cs_HMI/Project/ViewForm/fAuto.cs +++ b/Cs_HMI/Project/ViewForm/fAuto.cs @@ -40,11 +40,7 @@ namespace Project.ViewForm private void InitializeMapCanvas() { - PUB._mapCanvas = new AGVNavigationCore.Controls.UnifiedAGVCanvas(); - PUB._mapCanvas.Dock = DockStyle.Fill; - PUB._mapCanvas.ShowGrid = false; - PUB._mapCanvas.BackColor = Color.FromArgb(32, 32, 32); - PUB._mapCanvas.ForeColor = Color.White; + // RfidMappings 제거 - MapNode에 통합됨 // 이벤트 연결 @@ -116,7 +112,7 @@ namespace Project.ViewForm } // 1. 경로 생성 - var pathFinder = new AGVNavigationCore.PathFinding.Planning.AGVPathfinder(PUB._mapNodes); + var pathFinder = new AGVNavigationCore.PathFinding.Planning.AGVPathfinder(PUB._mapCanvas.Nodes); // 현재위치에서 목표위치까지 var result = pathFinder.FindPath(PUB._virtualAGV.CurrentNode, targetNode); @@ -178,12 +174,12 @@ namespace Project.ViewForm if (result.Success) { - if (PUB._mapNodes == null) PUB._mapNodes = new List(); - else PUB._mapNodes.Clear(); - PUB._mapNodes.AddRange(result.Nodes); + if (PUB._mapCanvas.Nodes == null) PUB._mapCanvas.Nodes = new List(); + else PUB._mapCanvas.Nodes.Clear(); + PUB._mapCanvas.Nodes.AddRange(result.Nodes); // 맵 캔버스에 데이터 설정 - PUB._mapCanvas.Nodes = PUB._mapNodes; + PUB._mapCanvas.Nodes = PUB._mapCanvas.Nodes; PUB._mapCanvas.MapFileName = filePath.FullName; // 🔥 맵 설정 적용 (배경색, 그리드 표시) @@ -194,9 +190,9 @@ namespace Project.ViewForm } // 🔥 가상 AGV 초기화 (첫 노드 위치에 생성) - if (PUB._virtualAGV == null && PUB._mapNodes.Count > 0) + if (PUB._virtualAGV == null && PUB._mapCanvas.Nodes.Count > 0) { - var startNode = PUB._mapNodes.FirstOrDefault(n => n.IsNavigationNode()); + var startNode = PUB._mapCanvas.Nodes.FirstOrDefault(n => n.IsNavigationNode()); if (startNode != null) { PUB._virtualAGV = new VirtualAGV(PUB.setting.MCID, startNode.Position, AgvDirection.Forward); diff --git a/Cs_HMI/Project/fMain.cs b/Cs_HMI/Project/fMain.cs index 6a3a4d2..28048ac 100644 --- a/Cs_HMI/Project/fMain.cs +++ b/Cs_HMI/Project/fMain.cs @@ -61,6 +61,15 @@ namespace Project usbdet.DeviceArrived += Usbdet_DeviceArrived; usbdet.DeviceRemoved += Usbdet_DeviceRemoved; + + PUB._mapCanvas = new AGVNavigationCore.Controls.UnifiedAGVCanvas(); + PUB._mapCanvas.Dock = DockStyle.Fill; + PUB._mapCanvas.ShowGrid = false; + PUB._mapCanvas.BackColor = Color.FromArgb(32, 32, 32); + PUB._mapCanvas.ForeColor = Color.White; + + + this.panTopMenu.MouseMove += LbTitle_MouseMove; this.panTopMenu.MouseUp += LbTitle_MouseUp; this.panTopMenu.MouseDown += LbTitle_MouseDown; @@ -776,7 +785,7 @@ namespace Project var od = new OpenFileDialog { - Filter = "AGV Map Files (*.agvmap)|*.agvmap|All Files (*.*)|*.*", + Filter = "AGV Map Files (*.agvmap;*.json)|*.agvmap;*.json|All Files (*.*)|*.*", DefaultExt = "agvmap", FileName = PUB._mapCanvas.MapFileName, }; @@ -801,24 +810,9 @@ namespace Project if (result.Success) { var _mapCanvas = PUB._mapCanvas; - PUB._mapNodes = result.Nodes; + _mapCanvas.SetMapLoadResult(result); PUB.log.Add($"Set _mapNodes"); - // 맵 캔버스에 데이터 설정 - _mapCanvas.Nodes = result.Nodes; - _mapCanvas.Labels = result.Labels; - _mapCanvas.Images = result.Images; - _mapCanvas.Marks = result.Marks; - _mapCanvas.Magnets = result.Magnets; - // RfidMappings 제거됨 - MapNode에 통합 - - // 🔥 맵 설정 적용 (배경색, 그리드 표시) - if (result.Settings != null) - { - _mapCanvas.BackColor = System.Drawing.Color.FromArgb(result.Settings.BackgroundColorArgb); - _mapCanvas.ShowGrid = result.Settings.ShowGrid; - } - // 설정에 마지막 맵 파일 경로 저장 PUB.setting.LastMapFile = filePath; PUB.setting.Save(); @@ -860,7 +854,7 @@ namespace Project } var _mapCanvas = PUB._mapCanvas; - var _mapNodes = PUB._mapNodes; + // 🔥 현재 캔버스 설정을 맵 파일에 저장 var settings = new MapLoader.MapSettings @@ -869,7 +863,11 @@ namespace Project ShowGrid = _mapCanvas.ShowGrid }; - if (MapLoader.SaveMapToFile(filePath, _mapNodes, _mapCanvas.Labels, _mapCanvas.Images, _mapCanvas.Marks, _mapCanvas.Magnets, settings)) + if (MapLoader.SaveMapToFile(filePath, + _mapCanvas.Nodes, _mapCanvas.Labels, + _mapCanvas.Images, _mapCanvas.Marks, + _mapCanvas.Magnets, + settings)) { // 설정에 마지막 맵 파일 경로 저장 PUB.setting.LastMapFile = filePath; diff --git a/Cs_HMI/SubProject/AGV/Structure/ErrorFlag.cs b/Cs_HMI/SubProject/AGV/Structure/ErrorFlag.cs index b97669d..e0db600 100644 --- a/Cs_HMI/SubProject/AGV/Structure/ErrorFlag.cs +++ b/Cs_HMI/SubProject/AGV/Structure/ErrorFlag.cs @@ -24,6 +24,12 @@ namespace arDev Charger_pos_error, line_out_error = 4, + spare_5 , + spare_6 , + spare_7 , + spare_8 , + spare_9 , + /// /// 기동시 자석 감지 에러 /// diff --git a/Cs_HMI/SubProject/AGV/Structure/Signals.cs b/Cs_HMI/SubProject/AGV/Structure/Signals.cs index 5eea990..f41bed1 100644 --- a/Cs_HMI/SubProject/AGV/Structure/Signals.cs +++ b/Cs_HMI/SubProject/AGV/Structure/Signals.cs @@ -19,7 +19,7 @@ namespace arDev public enum eflag { front_gate_out = 0, - rear_sensor_out, + rear_gte_out, mark_sensor_1, mark_sensor_2, front_left_sensor, @@ -47,7 +47,7 @@ namespace arDev } public Boolean front_gate_out { get { return GetValue(eflag.front_gate_out); } } - public Boolean rear_sensor_out { get { return GetValue(eflag.rear_sensor_out); } } + public Boolean rear_sensor_out { get { return GetValue(eflag.rear_gte_out); } } public Boolean mark_sensor_1 { get { return GetValue(eflag.mark_sensor_1); } } public Boolean mark_sensor_2 { get { return GetValue(eflag.mark_sensor_2); } } public Boolean mark_sensor { get { return mark_sensor_1 || mark_sensor_2; } } diff --git a/통신 프로토콜_AGV_V350_LF_25.01.10.xlsx b/통신 프로토콜_AGV_V350_LF_25.01.10.xlsx new file mode 100644 index 0000000..91da91a Binary files /dev/null and b/통신 프로토콜_AGV_V350_LF_25.01.10.xlsx differ