Refactor AGV error handling: Standardize AGVErrorCode usage and update SetRunStepError

This commit is contained in:
backuppc
2026-01-28 16:45:24 +09:00
parent 16d51a2712
commit 00cc0ef5b7
12 changed files with 94 additions and 84 deletions

View File

@@ -41,9 +41,8 @@ namespace Project
public string Memo;
public eResult ResultCode { get; set; }
public eECode ResultErrorCode;
public eECode RunStepErrorCode { get; set; }
public ENIGProtocol.AGVErrorCode RunStepErrorCode { get; set; }
public string ResultMessage { get; set; }
public Boolean isError { get; set; }
public int retry = 0;
public DateTime retryTime;
public Device.Socket.Message RecvMessage;
@@ -191,9 +190,8 @@ namespace Project
JobEndTime = DateTime.Parse("1982-11-23");
Memo = string.Empty;
isError = false;
ResultCode = eResult.NoError;
RunStepErrorCode = eECode.NOERROR;
RunStepErrorCode = ENIGProtocol.AGVErrorCode.None;
ResultMessage = string.Empty;
}

View File

@@ -65,20 +65,6 @@ namespace Project
DION,
NOTALLOWUP,
// Operational Errors
CART_EXIST,
MARK_TIMEOUT,
MARK_SENSOR_FAIL,
LIFT_ERROR,
AGV_SPEED_SET_FAIL,
AGV_RUN_FAIL,
AGV_STOP_FAIL,
PATH_INTEGRITY_FAIL,
TURN_FAIL,
NO_CHARGEPOINT,
NOTSET_CHARGEPOINT,
ALREADY_CHARGE,
}
public enum eResult : byte

View File

@@ -183,6 +183,10 @@ namespace Project.Device
/// <param name="errcode"></param>
public void SendError(ENIGProtocol.AGVErrorCode errcode, string errormessage)
{
// Update global error state so it persists in Status messages
PUB.Result.RunStepErrorCode = errcode;
PUB.Result.ResultMessage = errormessage;
var id = PUB.setting.XBE_ID;
byte cmd = (byte)ENIGProtocol.AGVCommandEH.Error;
if (errormessage.Length > 30) errormessage = errormessage.Substring(0, 29);

View File

@@ -80,7 +80,7 @@ namespace Project
else if (turnState == arDev.eNarumiTurn.Right || turnState == arDev.eNarumiTurn.RightIng)
{
//비정상 상태 (우회전 중?)
SetRunStepError($"[{funcname}] 턴 방향 불일치(Current:{turnState}). 우회전 상태에서 좌회전을 시도할 수 없습니다.", eECode.TURN_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.TURN_FAIL, $"[{funcname}] 턴 방향 불일치(Current:{turnState}). 우회전 상태에서 좌회전을 시도할 수 없습니다.");
}
else
{
@@ -114,7 +114,7 @@ namespace Project
if (PUB.AGV.TurnInformation.Runtime.TotalSeconds > overtime)
{
//30초동안 AGV까 움직이지 않았다면 오류 처리한다.
SetRunStepError($"[{funcname}] {overtime}초이내 턴 감지 안됨", eECode.TURN_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.TURN_FAIL, $"[{funcname}] {overtime}초이내 턴 감지 안됨");
return false;
}
else PUB._mapCanvas.SetAlertMessage($"턴 진행 중({PUB.AGV.TurnInformation.Runtime.TotalSeconds:N0}/{overtime})");
@@ -162,7 +162,7 @@ namespace Project
{
if (seqtime.TotalSeconds > 20)
{
SetRunStepError($"[{funcname}] 리프트가 내려가지 않습니다", eECode.LIFT_ERROR);
SetRunStepError(ENIGProtocol.AGVErrorCode.LIFT_ERROR, $"[{funcname}] 리프트가 내려가지 않습니다");
}
else PUB._mapCanvas.SetAlertMessage($"리프트 하강 확인 중({seqtime.TotalSeconds:N0}/20)");
return false;
@@ -197,7 +197,7 @@ namespace Project
{
if (ret >= arDev.eNarumiCommandResult.Error)
{
SetRunStepError($"[{funcname}] AGV속도설정이 완료되지 않았습니다", eECode.AGV_SPEED_SET_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.AGV_SPEED_SET_FAIL);
}
return false;
}
@@ -215,7 +215,7 @@ namespace Project
{
if(seqtime.TotalSeconds > 3)
{
SetRunStepError($"[{funcname}] AGV이동이 확인되지 않습니다", eECode.AGV_RUN_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.AGV_RUN_FAIL);
}
return false;
}
@@ -237,7 +237,7 @@ namespace Project
{
if (seqtime.TotalSeconds > 3)
{
SetRunStepError($"[{funcname}] MARK STOP신호가 확인되지 않습니다", eECode.MARK_TIMEOUT);
SetRunStepError(ENIGProtocol.AGVErrorCode.MARK_TIMEOUT);
return false;
}
}
@@ -253,7 +253,7 @@ namespace Project
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
if (ts.TotalSeconds > 10)
{
SetRunStepError($"[{funcname}] AGV가 멈추지 않아 강제종료 합니다", eECode.AGV_STOP_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.AGV_STOP_FAIL);
return false;
}
return false;

View File

@@ -70,7 +70,7 @@ namespace Project
{
if (seqtime.TotalSeconds > 20)
{
SetRunStepError($"[{funcname}] 리프트가 동작하지 않습니다", eECode.LIFT_ERROR);
SetRunStepError(ENIGProtocol.AGVErrorCode.LIFT_ERROR, $"[{funcname}] 리프트가 동작하지 않습니다");
}
return false;
}
@@ -92,7 +92,7 @@ namespace Project
{
if (ret >= arDev.eNarumiCommandResult.Error)
{
SetRunStepError($"[{funcname}] AGV속도설정이 완료되지 않았습니다", eECode.AGV_SPEED_SET_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.AGV_SPEED_SET_FAIL);
}
return false;
}
@@ -116,7 +116,7 @@ namespace Project
if (seqtime.TotalSeconds > 3)
{
//구동이확인되지 않으면 오류처리를 한다.
SetRunStepError($"[{funcname}] AGV 전진 구동이 확인되지 않습니다", eECode.AGV_RUN_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.AGV_RUN_FAIL);
}
return false;
}
@@ -132,7 +132,7 @@ namespace Project
{
if (seqtime.TotalSeconds > 3)
{
SetRunStepError($"[{funcname}] MARK STOP신호가 확인되지 않습니다", eECode.MARK_TIMEOUT);
SetRunStepError(ENIGProtocol.AGVErrorCode.MARK_TIMEOUT);
}
return false;
}
@@ -146,7 +146,7 @@ namespace Project
{
if (seqtime.TotalSeconds > 15)
{
SetRunStepError($"[{funcname}] AGV가 멈추지 않아 강제종료 합니다", eECode.AGV_STOP_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.AGV_STOP_FAIL);
}
return false;
}
@@ -176,7 +176,7 @@ namespace Project
else if (turnState == arDev.eNarumiTurn.Left || turnState == arDev.eNarumiTurn.LeftIng)
{
//비정상 상태 (좌회전 중?)
SetRunStepError($"[{funcname}] 턴 방향 불일치(Current:{turnState}). 좌회전 상태에서 우회전을 시도할 수 없습니다.", eECode.TURN_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.TURN_FAIL, $"[{funcname}] 턴 방향 불일치(Current:{turnState}). 좌회전 상태에서 우회전을 시도할 수 없습니다.");
}
else
{
@@ -208,7 +208,7 @@ namespace Project
return false;
}
SetRunStepError($"[{funcname}] Turn 구동 실패", eECode.TURN_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.TURN_FAIL);
}
return false;
}
@@ -222,7 +222,7 @@ namespace Project
{
if (seqtime.TotalSeconds > 25)
{
SetRunStepError($"[{funcname}] Turn 완료 실패 (Timeout)", eECode.TURN_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.TURN_FAIL);
}
return false;
}

View File

@@ -32,7 +32,7 @@ namespace Project
if (VAR.BOOL[eVarBool.FLAG_CHARGEONA] == true || PUB.AGV.system1.Battery_charging == true ||
VAR.BOOL[eVarBool.FLAG_CHARGEONM] == true)
{
SetRunStepError($"현재 충전중이므로 충전시퀀스를 시작할 수 없습니다", eECode.ALREADY_CHARGE);
SetRunStepError(ENIGProtocol.AGVErrorCode.ALREADY_CHARGE);
return false;
}
@@ -60,7 +60,7 @@ namespace Project
var targetnode = PUB.FindByRFID(PUB.setting.NodeMAP_RFID_Charger);
if (targetnode == null)
{
SetRunStepError($"충전기 노드가 설정되지 않았습니다", eECode.NOTSET_CHARGEPOINT);
SetRunStepError(ENIGProtocol.AGVErrorCode.NOTSET_CHARGEPOINT);
return false;
}
@@ -69,7 +69,7 @@ namespace Project
var curnode = PUB._virtualAGV.CurrentNode;
if (PUB.AGV.signal1.mark_sensor == false || curnode.Id != targetnode.Id)
{
SetRunStepError($"충전기위치가 아니므로 충전을 시작할 수 없습니다(현재위치:{curnode.RfidId})", eECode.NO_CHARGEPOINT);
SetRunStepError(ENIGProtocol.AGVErrorCode.NO_CHARGEPOINT, $"충전기위치가 아니므로 충전을 시작할 수 없습니다(현재위치:{curnode.RfidId})");
return false;
}
@@ -108,7 +108,7 @@ namespace Project
PUB.counter.Save();
VAR.BYTE[eVarByte.CHARGE_CMDCNT] = 0;
SetRunStepError($"충전명령 재전송 횟수 초과", eECode.MESSAGE_ERROR);
SetRunStepError(ENIGProtocol.AGVErrorCode.CHARGE_RETRY_OVER);
return false;
}
else

View File

@@ -38,7 +38,7 @@ namespace Project
//마크센서가 감지된상태여야 완전한위치로 가정한다
if(PUB.AGV.signal1.mark_sensor == false)
{
SetRunStepError($"[{funcname}] 마크센서가 감지되지 않습니다", eECode.MARK_SENSOR_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.MARK_SENSOR_FAIL, $"[{funcname}] 마크센서가 감지되지 않습니다");
}
PUB.sm.UpdateRunStepSeq();
return false;
@@ -51,7 +51,7 @@ namespace Project
//가지러가야하는 경우이므로 카트가 없어야한다.
if (PUB.AGV.signal2.cart_detect1 || PUB.AGV.signal2.cart_detect2)
{
SetRunStepError($"[{funcname}] 카트가 존재하여 진입을 할 수 없습니다", eECode.CART_EXIST);
SetRunStepError(ENIGProtocol.AGVErrorCode.CART_EXIST, $"[{funcname}] 카트가 존재하여 진입을 할 수 없습니다");
return false;
}
}
@@ -61,14 +61,14 @@ namespace Project
if (PUB.AGV.signal2.cart_detect1 == false || PUB.AGV.signal2.cart_detect2 == false)
{
var errmsg = $"[{funcname}] 카트감지센서가 인식되지 않았습니다";
SetRunStepError(errmsg, eECode.CART_EXIST);
SetRunStepError(ENIGProtocol.AGVErrorCode.CART_EXIST, errmsg);
return false;
}
}
else
{
var errmsg = $"[{funcname}] 알수없는 작업형태입니다({PUB.NextWorkCmd})";
SetRunStepError(errmsg, eECode.NOMODELV);
SetRunStepError(ENIGProtocol.AGVErrorCode.UnknownCommand, errmsg);
return false;
}
@@ -93,7 +93,7 @@ namespace Project
if (ts.TotalSeconds > 10)
{
var errmsg = $"[{funcname}] 리프트다운이 확인되지 않습니다";
SetRunStepError(errmsg, eECode.LIFT_ERROR);
SetRunStepError(ENIGProtocol.AGVErrorCode.LIFT_ERROR, errmsg);
}
return false;
}
@@ -119,7 +119,7 @@ namespace Project
{
if (ret >= arDev.eNarumiCommandResult.Error)
{
SetRunStepError("AGV속도설정이 완료되지 않았습니다", eECode.AGV_SPEED_SET_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.AGV_SPEED_SET_FAIL);
}
return false;
}
@@ -143,7 +143,7 @@ namespace Project
if (seqTime.TotalMilliseconds > 1000)
{
//구동이확인되지 않으면 오류처리를 한다.
SetRunStepError("AGV 구동이 확인되지 않습니다", eECode.AGV_RUN_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.AGV_RUN_FAIL);
}
return false;
}
@@ -159,7 +159,7 @@ namespace Project
{
if (seqTime.TotalMilliseconds > 3000)
{
SetRunStepError("MARK STOP신호가 확인되지 않습니다", eECode.MARK_TIMEOUT);
SetRunStepError(ENIGProtocol.AGVErrorCode.MARK_TIMEOUT);
}
return false;
}
@@ -173,7 +173,7 @@ namespace Project
{
if (seqTime.TotalSeconds > 10)
{
SetRunStepError("AGV가 멈추지 않아 강제종료 합니다", eECode.AGV_STOP_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.AGV_STOP_FAIL);
}
return false;
}
@@ -188,7 +188,7 @@ namespace Project
{
if (seqTime.TotalSeconds > 5)
{
SetRunStepError("마크센서가 감지되지 않았습니다", eECode.MARK_SENSOR_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.MARK_SENSOR_FAIL);
}
return false;
}

View File

@@ -65,7 +65,7 @@ namespace Project
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
if (ts.TotalSeconds > 10)
{
SetRunStepError($"[{funcname}] 리프트({liftCmd})이 확인되지 않습니다", eECode.LIFT_ERROR);
SetRunStepError(ENIGProtocol.AGVErrorCode.LIFT_ERROR, $"[{funcname}] 리프트({liftCmd})이 확인되지 않습니다");
}
return false;
}
@@ -89,7 +89,7 @@ namespace Project
{
if (ret >= arDev.eNarumiCommandResult.Error)
{
SetRunStepError($"[{funcname}] AGV속도설정이 완료되지 않았습니다", eECode.AGV_SPEED_SET_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.AGV_SPEED_SET_FAIL);
}
return false;
}
@@ -112,7 +112,7 @@ namespace Project
if (seqTime.TotalMilliseconds > 1000)
{
//구동이확인되지 않으면 오류처리를 한다.
SetRunStepError("AGV 구동이 확인되지 않습니다", eECode.AGV_RUN_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.AGV_RUN_FAIL);
}
return false;
}
@@ -128,7 +128,7 @@ namespace Project
{
if (seqTime.TotalMilliseconds > 3000)
{
SetRunStepError("MARK STOP신호가 확인되지 않습니다", eECode.MARK_TIMEOUT);
SetRunStepError(ENIGProtocol.AGVErrorCode.MARK_TIMEOUT);
}
return false;
}
@@ -142,7 +142,7 @@ namespace Project
{
if (seqTime.TotalSeconds > 10)
{
SetRunStepError("AGV가 멈추지 않아 강제종료 합니다", eECode.AGV_STOP_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.AGV_STOP_FAIL);
}
return false;
}
@@ -156,7 +156,7 @@ namespace Project
{
if (seqTime.TotalSeconds > 5)
{
SetRunStepError("마크센서가 감지되지 않았습니다", eECode.MARK_SENSOR_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.MARK_SENSOR_FAIL);
}
return false;
}

View File

@@ -7,6 +7,9 @@ using Project.StateMachine;
using COMM;
using AR;
using AGVNavigationCore.Utils;
using AGVNavigationCore.PathFinding.Core;
using AGVNavigationCore.Models;
using AGVNavigationCore.PathFinding.Planning;
namespace Project
{
@@ -38,6 +41,32 @@ namespace Project
return true;
}
AGVPathResult CalcPath(MapNode startNode, MapNode targetNode)
{
var nodes = PUB._mapCanvas.Nodes;
var _simulatorCanvas = PUB._mapCanvas;
var _mapNodes = PUB._mapCanvas.Nodes;
// 현재 AGV 방향 가져오기
var selectedAGV = PUB._virtualAGV;
var currentDirection = selectedAGV.CurrentDirection;
// AGV의 이전 위치에서 가장 가까운 노드 찾기
var prevNode = selectedAGV.PrevNode;
var prevDir = selectedAGV.PrevDirection;
// Core Logic으로 이관됨
var pathFinder = new AGVPathfinder(nodes);
var result = pathFinder.CalculatePath(startNode, targetNode, prevNode, prevDir);
//게이트웨이노드를 하이라이트강조 한단
_simulatorCanvas.HighlightNodeId = (result.Gateway?.Id ?? string.Empty);
return result;
}
/// <summary>
/// 실행스텝을 오류로 전환합니다.
/// 로그메세지(에러)가 추가됩니다.
@@ -45,8 +74,13 @@ namespace Project
/// </summary>
/// <param name="ermsg"></param>
/// <param name="ecode"></param>
public void SetRunStepError(string errmsg, eECode ecode)
public void SetRunStepError(ENIGProtocol.AGVErrorCode ecode, string errmsg = "")
{
if (string.IsNullOrEmpty(errmsg))
{
errmsg = ENIGProtocol.AGVUtility.GetAGVErrorMessage(ecode);
}
PUB.AGV.AGVMoveStop(errmsg);
PUB.log.AddE(errmsg);
PUB._mapCanvas.SetAlertMessage(errmsg);
@@ -152,7 +186,7 @@ namespace Project
VAR.I32[eVarInt32.PathValidationError] += 1;
if (VAR.I32[eVarInt32.PathValidationError] > 50)
{
SetRunStepError($"연속 경로 무결성 오류로 인해 중지 합니다", eECode.PATH_INTEGRITY_FAIL);
SetRunStepError(ENIGProtocol.AGVErrorCode.PATH_INTEGRITY_FAIL, $"연속 경로 무결성 오류로 인해 중지 합니다");
}
return false;
}

View File

@@ -46,6 +46,7 @@ namespace Project
{
case ENIGProtocol.AGVCommandHE.SetCurrent: //Set Current Position
Resultclear();
if (data.Length > 4)
{
@@ -75,6 +76,7 @@ namespace Project
case ENIGProtocol.AGVCommandHE.PickOnEnter: // 110
case ENIGProtocol.AGVCommandHE.PickOffEnter: // 111
{
Resultclear();
PUB.log.AddI($"XBEE:작업명령수신:{cmd}");
// 현재 위치 확인 (TargetNode가 아닌 CurrentNode 기준)
@@ -120,6 +122,7 @@ namespace Project
case ENIGProtocol.AGVCommandHE.Charger: // 112
{
Resultclear();
PUB.log.AddI($"XBEE:충전명령수신");
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Charger;
PUB.sm.SetNewRunStep(ERunStep.GOCHARGE);
@@ -128,6 +131,7 @@ namespace Project
case ENIGProtocol.AGVCommandHE.GotoAlias:
case ENIGProtocol.AGVCommandHE.Goto: //move to tag
Resultclear();
var datalength = cmd == ENIGProtocol.AGVCommandHE.GotoAlias ? 2 : 1;
if (data.Length > datalength)
{
@@ -210,10 +214,11 @@ namespace Project
break;
case ENIGProtocol.AGVCommandHE.Reset: //Error Reset
PUB.log.Add($"[{logPrefix}-Reset]");
PUB.AGV.AGVErrorReset();
ResetSystemError();
break;
case ENIGProtocol.AGVCommandHE.Manual: //Manual Move (Direction, speed, runtime)
Resultclear();
var Direction = data[1]; //0=back, 1=forward, 2=left, 3=right
var Speed = data[2]; //0=slow, 1=normal, 2=fast
@@ -234,6 +239,7 @@ namespace Project
break;
case ENIGProtocol.AGVCommandHE.AutoMove:
Resultclear();
var MotDirection = data[1]; //0=back, 1=forward
var MagDirection = data[2]; //0=straight, 1=left, 2=right
var AutSpeed = data[3]; //0=slow, 1=normal, 2=fast
@@ -310,31 +316,6 @@ namespace Project
else PUB.log.Add(e.Message);
}
AGVPathResult CalcPath(MapNode startNode, MapNode targetNode)
{
var nodes = PUB._mapCanvas.Nodes;
var _simulatorCanvas = PUB._mapCanvas;
var _mapNodes = PUB._mapCanvas.Nodes;
// 현재 AGV 방향 가져오기
var selectedAGV = PUB._virtualAGV;
var currentDirection = selectedAGV.CurrentDirection;
// AGV의 이전 위치에서 가장 가까운 노드 찾기
var prevNode = selectedAGV.PrevNode;
var prevDir = selectedAGV.PrevDirection;
// Core Logic으로 이관됨
var pathFinder = new AGVPathfinder(nodes);
var result = pathFinder.CalculatePath(startNode, targetNode, prevNode, prevDir);
//게이트웨이노드를 하이라이트강조 한단
_simulatorCanvas.HighlightNodeId = (result.Gateway?.Id ?? string.Empty);
return result;
}
}
}

View File

@@ -425,7 +425,7 @@ namespace Project
}
if (VAR.BOOL[eVarBool.FLAG_AUTORUN] == false) //자동상태가 아니라면
{
PUB.AGV.AGVErrorReset();
ResetSystemError();
if (Prompt)
{
if (UTIL.MsgQ("AGV상태를 자동으로 전환 할까요?") != DialogResult.Yes) return;
@@ -454,6 +454,13 @@ namespace Project
PUB.log.AddI("Clear Resultclear");
}
public void ResetSystemError()
{
PUB.AGV.AGVErrorReset();
Resultclear();
PUB.log.AddI("System Error Reset");
}
void CheckFreeSpace()
{
@@ -768,7 +775,7 @@ namespace Project
switch (title.ToLower())
{
case "emg":
PUB.AGV.AGVErrorReset();
ResetSystemError();
break;
case "itm":
var itemon = VAR.BOOL[eVarBool.ITEMON];

Submodule Cs_HMI/SubProject/EnigProtocol updated: ae5585c00f...aaea811e4a