add temp1, temp2
This commit is contained in:
3
Cs_HMI/SubProject/AGVControl/.vscode/settings.json
vendored
Normal file
3
Cs_HMI/SubProject/AGVControl/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"dotnet.preferCSharpExtension": true
|
||||
}
|
||||
@@ -49,6 +49,12 @@ namespace AGVControl
|
||||
public List<Point> Path { get; set; }
|
||||
}
|
||||
|
||||
public enum AGVMoveState
|
||||
{
|
||||
Stop = 0,
|
||||
Run
|
||||
}
|
||||
|
||||
public enum AGVActionReasonCode
|
||||
{
|
||||
Unknown = 0,
|
||||
@@ -57,7 +63,8 @@ namespace AGVControl
|
||||
NotOnPath, // 현재 위치가 경로에 없음
|
||||
Arrived, // 경로의 마지막 지점(목적지 도달)
|
||||
Normal, // 정상(다음 RFID 있음)
|
||||
NeedTurn
|
||||
NeedTurn,
|
||||
NoTurnPoint,
|
||||
}
|
||||
|
||||
public class AGVActionPrediction
|
||||
@@ -66,6 +73,7 @@ namespace AGVControl
|
||||
public uint? NextRFID { get; set; }
|
||||
public string Reason { get; set; }
|
||||
public AGVActionReasonCode ReasonCode { get; set; }
|
||||
public AGVMoveState MoveState { get; set; } // RUN 또는 STOP
|
||||
}
|
||||
|
||||
public partial class MapControl : Control
|
||||
@@ -401,35 +409,17 @@ namespace AGVControl
|
||||
var selected_rfid = rfidPoints.Where(t => t.Bounds.Expand(SELECTION_DISTANCE, SELECTION_DISTANCE).Contains(mapPoint)).FirstOrDefault();
|
||||
if (selected_rfid != null)
|
||||
{
|
||||
var oldtagno = selected_rfid.RFIDValue;
|
||||
AR.UTIL.ShowProperty(selected_rfid);
|
||||
if (selected_rfid.RFIDValue != oldtagno)
|
||||
{
|
||||
var items1 = this.rfidConnections.Where(t=>t.StartRFID == oldtagno);
|
||||
foreach (var item in items1)
|
||||
item.StartRFID = selected_rfid.RFIDValue;
|
||||
|
||||
var items2 = this.rfidConnections.Where(t => t.EndRFID == oldtagno);
|
||||
foreach (var item in items2)
|
||||
item.EndRFID = selected_rfid.RFIDValue;
|
||||
}
|
||||
|
||||
UTIL.ShowPropertyDialog(selected_rfid);
|
||||
this.Invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 텍스트 객체 찾기
|
||||
var selected_txt = mapTexts.Where(t => t.Bounds.Expand(SELECTION_DISTANCE, SELECTION_DISTANCE).Contains(mapPoint)).FirstOrDefault();
|
||||
if (selected_txt != null)
|
||||
{
|
||||
var rlt = AR.UTIL.InputBox("input", selected_txt.Text);
|
||||
if (rlt.Item1 == true)
|
||||
{
|
||||
selected_txt.Text = rlt.Item2;
|
||||
this.Invalidate();
|
||||
}
|
||||
UTIL.ShowPropertyDialog(selected_txt);
|
||||
this.Invalidate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -451,7 +441,7 @@ namespace AGVControl
|
||||
}
|
||||
else
|
||||
{
|
||||
zoom /= 1.1f;
|
||||
zoom /= 1.2f;
|
||||
}
|
||||
|
||||
zoom = Math.Max(0.1f, Math.Min(10.0f, zoom));
|
||||
@@ -744,9 +734,17 @@ namespace AGVControl
|
||||
var rfidPoint = FindRFIDPoint(rfidValue);
|
||||
if (rfidPoint != null)
|
||||
{
|
||||
// 이전 위치 저장 (방향 검증용)
|
||||
Point previousPosition = agv.CurrentPosition;
|
||||
uint previousRFID = agv.CurrentRFID;
|
||||
|
||||
// AGV 위치 업데이트
|
||||
agv.CurrentPosition = rfidPoint.Location;
|
||||
agv.CurrentRFID = rfidValue;
|
||||
|
||||
// 이동 경로에 추가
|
||||
agv.AddToMovementHistory(rfidValue, rfidPoint.Location);
|
||||
|
||||
// 목적지가 설정되어 있고 경로가 있는 경우 검증
|
||||
if (agv.TargetPosition != Point.Empty && agv.CurrentPath.Count > 0)
|
||||
{
|
||||
@@ -784,6 +782,52 @@ namespace AGVControl
|
||||
}
|
||||
}
|
||||
|
||||
// 방향 검증 및 정정 (이전 위치가 있고 경로가 있는 경우)
|
||||
if (previousRFID != 0 && agv.CurrentPath.Count > 0)
|
||||
{
|
||||
// RFID 연결 정보 기반 예상 방향 계산
|
||||
Direction? expectedDirection = null;
|
||||
int currentIdx = agv.CurrentPath.FindIndex(p => p == agv.CurrentPosition);
|
||||
if (currentIdx >= 0 && currentIdx < agv.CurrentPath.Count - 1)
|
||||
{
|
||||
// 현재 위치와 다음 위치의 RFID 찾기
|
||||
var currentRFIDPoint = FindRFIDPoint(agv.CurrentRFID);
|
||||
var nextPoint = agv.CurrentPath[currentIdx + 1];
|
||||
var nextRFIDPoint = rfidPoints.FirstOrDefault(p => p.Location == nextPoint);
|
||||
|
||||
if (currentRFIDPoint != null && nextRFIDPoint != null)
|
||||
{
|
||||
// rfidConnections에서 연결 정보 확인
|
||||
var connection = rfidConnections.FirstOrDefault(c =>
|
||||
(c.StartRFID == currentRFIDPoint.RFIDValue && c.EndRFID == nextRFIDPoint.RFIDValue) ||
|
||||
(c.IsBidirectional && c.StartRFID == nextRFIDPoint.RFIDValue && c.EndRFID == currentRFIDPoint.RFIDValue));
|
||||
|
||||
if (connection != null)
|
||||
{
|
||||
// 연결된 경로이므로 방향 결정
|
||||
if (connection.StartRFID == currentRFIDPoint.RFIDValue && connection.EndRFID == nextRFIDPoint.RFIDValue)
|
||||
{
|
||||
expectedDirection = Direction.Forward; // Start -> End 방향
|
||||
}
|
||||
else
|
||||
{
|
||||
expectedDirection = Direction.Backward; // End -> Start 방향
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (expectedDirection.HasValue)
|
||||
{
|
||||
bool directionValid = agv.ValidateAndCorrectDirectionByConnection(expectedDirection.Value, rfidConnections.ToList());
|
||||
if (!directionValid)
|
||||
{
|
||||
// 방향이 정정되었음을 로그로 기록
|
||||
Console.WriteLine($"AGV 방향 정정 (연결 정보 기반): 예상 {expectedDirection.Value} -> 실제 {agv.CurrentDirection}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.Invalidate();
|
||||
return true;
|
||||
}
|
||||
@@ -1233,37 +1277,52 @@ namespace AGVControl
|
||||
var MarkerSize = 5;
|
||||
var half = MarkerSize / 2f;
|
||||
rfid.Bounds = new RectangleF(rfid.Location.X - half, rfid.Location.Y - half, MarkerSize, MarkerSize);
|
||||
|
||||
// 회전 가능 여부에 따라 다른 색상 사용
|
||||
using (var brush = new SolidBrush(rfid.IsRotatable ? Color.Yellow : Color.Green))
|
||||
|
||||
// 종단 RFID는 특별한 색상으로 표시
|
||||
Color pointColor;
|
||||
if (rfid.IsTerminal)
|
||||
{
|
||||
pointColor = Color.Orange; // 종단은 주황색
|
||||
}
|
||||
else if (rfid.IsRotatable)
|
||||
{
|
||||
pointColor = Color.Yellow; // 회전 가능은 노란색
|
||||
}
|
||||
else
|
||||
{
|
||||
pointColor = Color.Green; // 일반은 초록색
|
||||
}
|
||||
|
||||
using (var brush = new SolidBrush(pointColor))
|
||||
{
|
||||
g.FillEllipse(brush, rfid.Bounds);
|
||||
}
|
||||
|
||||
// 고정방향이 있으면 테두리 색상 표시
|
||||
if (rfid.FixedDirection.HasValue)
|
||||
{
|
||||
Color borderColor = rfid.FixedDirection.Value == Direction.Forward ? Color.DeepSkyBlue : Color.Gold; using (var pen = new Pen(borderColor, 2))
|
||||
Color borderColor = rfid.FixedDirection.Value == Direction.Forward ? Color.DeepSkyBlue : Color.Gold;
|
||||
using (var pen = new Pen(borderColor, 2))
|
||||
{
|
||||
g.DrawEllipse(pen, rfid.Bounds.Expand(5, 5));
|
||||
g.DrawEllipse(pen, rfid.Bounds.Expand(5,5));
|
||||
}
|
||||
}
|
||||
|
||||
// 종단 RFID는 특별한 테두리 표시
|
||||
if (rfid.IsTerminal)
|
||||
{
|
||||
using (var pen = new Pen(Color.Red, 3))
|
||||
{
|
||||
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
|
||||
g.DrawEllipse(pen, rfid.Bounds.Expand(8,8));
|
||||
}
|
||||
}
|
||||
|
||||
var str = rfid.RFIDValue.ToString();
|
||||
g.DrawString(str, this.Font, Brushes.DarkGray, rfid.Bounds.X, rfid.Bounds.Y+5);
|
||||
}
|
||||
|
||||
// RFID 값 표시
|
||||
foreach (var rfid in rfidPoints)
|
||||
{
|
||||
var tagstr = $"{rfid.RFIDValue}";
|
||||
var fsize = g.MeasureString(tagstr, Font);
|
||||
var rect = new RectangleF(rfid.Bounds.X - (fsize.Width / 2f) + 3,
|
||||
rfid.Bounds.Y + 6,
|
||||
fsize.Width,
|
||||
fsize.Height);
|
||||
g.DrawString(tagstr, Font, Brushes.WhiteSmoke, rect, new StringFormat
|
||||
{
|
||||
Alignment = StringAlignment.Center,
|
||||
LineAlignment = StringAlignment.Center,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void DrawAGV(Graphics g)
|
||||
@@ -1277,11 +1336,64 @@ namespace AGVControl
|
||||
agv.CurrentPosition.Y - halfsize,
|
||||
agvsize, agvsize);
|
||||
|
||||
// 삼각형 화살표를 위한 포인트 배열
|
||||
Point[] trianglePoints = new Point[3];
|
||||
var arrowSize = halfsize - 5; // 삼각형 크기
|
||||
// AGV 몸체 회전 각도 계산
|
||||
float bodyRotation = 0f;
|
||||
if (agv.CurrentPath != null && agv.CurrentPath.Count > 1)
|
||||
{
|
||||
// 현재 위치에서 다음 목적지 방향 계산
|
||||
int currentIdx = agv.CurrentPath.FindIndex(p => p == agv.CurrentPosition);
|
||||
if (currentIdx >= 0 && currentIdx < agv.CurrentPath.Count - 1)
|
||||
{
|
||||
Point nextPoint = agv.CurrentPath[currentIdx + 1];
|
||||
float deltaX = nextPoint.X - agv.CurrentPosition.X;
|
||||
float deltaY = nextPoint.Y - agv.CurrentPosition.Y;
|
||||
bodyRotation = (float)Math.Atan2(deltaY, deltaX) * 180f / (float)Math.PI;
|
||||
}
|
||||
}
|
||||
|
||||
// AGV의 방향에 따라 삼각형 포인트 계산
|
||||
// 그래픽스 상태 저장
|
||||
var originalTransform = g.Transform;
|
||||
|
||||
// AGV 위치로 이동하고 회전
|
||||
g.TranslateTransform(agv.CurrentPosition.X, agv.CurrentPosition.Y);
|
||||
g.RotateTransform(bodyRotation);
|
||||
|
||||
// 원 그리기 (회전된 상태)
|
||||
Color bgcolor = agv.BatteryLevel > 80 ? Color.Lime : (agv.BatteryLevel > 60 ? Color.Gold : Color.Tomato);
|
||||
using (var circleBrush = new SolidBrush(Color.FromArgb(150, bgcolor)))
|
||||
g.FillEllipse(circleBrush, -halfsize, -halfsize, agvsize, agvsize);
|
||||
using (var circlePen = new Pen(Color.Black, 2))
|
||||
g.DrawEllipse(circlePen, -halfsize, -halfsize, agvsize, agvsize);
|
||||
|
||||
// 리프트 그리기 (회전된 상태, 항상 전진 방향쪽)
|
||||
var liftWidth = circleRect.Width;
|
||||
var liftHeight = (int)(circleRect.Height * 0.4);
|
||||
var liftOffset = halfsize + 1;
|
||||
|
||||
var liftRect = new Rectangle(
|
||||
-liftWidth / 2,
|
||||
-halfsize - liftOffset,
|
||||
liftWidth, liftHeight);
|
||||
|
||||
using (var liftBrush = new SolidBrush(Color.FromArgb(200, Color.DarkGray)))
|
||||
g.FillRectangle(liftBrush, liftRect);
|
||||
using (var liftPen = new Pen(Color.Black, 1))
|
||||
g.DrawRectangle(liftPen, liftRect);
|
||||
|
||||
// 리프트 연결선 그리기
|
||||
using (var connectionPen = new Pen(Color.Black, 2))
|
||||
{
|
||||
g.DrawLine(connectionPen, 0, -halfsize, 0, -halfsize - liftOffset);
|
||||
}
|
||||
|
||||
// 그래픽스 상태 복원
|
||||
g.Transform = originalTransform;
|
||||
|
||||
// 삼각형 화살표 그리기 (현재 이동 방향, 회전하지 않음)
|
||||
Point[] trianglePoints = new Point[3];
|
||||
var arrowSize = halfsize - 5;
|
||||
|
||||
// AGV의 현재 이동 방향에 따라 삼각형 포인트 계산
|
||||
switch (agv.CurrentDirection)
|
||||
{
|
||||
case Direction.Forward:
|
||||
@@ -1296,16 +1408,6 @@ namespace AGVControl
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 원 그리기
|
||||
Color bgcolor = agv.BatteryLevel > 80 ? Color.Lime : (agv.BatteryLevel > 60 ? Color.Gold : Color.Tomato);
|
||||
using (var circleBrush = new SolidBrush(Color.FromArgb(150, bgcolor)))
|
||||
g.FillEllipse(circleBrush, circleRect);
|
||||
using (var circlePen = new Pen(Color.Black, 2))
|
||||
g.DrawEllipse(circlePen, circleRect);
|
||||
|
||||
// 삼각형 화살표 그리기
|
||||
using (var arrowBrush = new SolidBrush(Color.FromArgb(200, Color.White)))
|
||||
g.FillPolygon(arrowBrush, trianglePoints);
|
||||
using (var arrowPen = new Pen(Color.Black, 2))
|
||||
@@ -1563,17 +1665,15 @@ namespace AGVControl
|
||||
lines.Add("[RFID_POINTS]");
|
||||
foreach (var point in rfidPoints)
|
||||
{
|
||||
lines.Add($"{point.Location.X},{point.Location.Y},{point.RFIDValue}");
|
||||
lines.Add($"{point.Location.X},{point.Location.Y},{point.RFIDValue},{point.IsRotatable},{point.FixedDirection},{point.IsTerminal}");
|
||||
}
|
||||
|
||||
// RFID 라인 저장
|
||||
lines.Add("[RFID_LINES]");
|
||||
foreach (var connection in rfidConnections)
|
||||
{
|
||||
var startPoint = rfidPoints.FirstOrDefault(p => p.RFIDValue == connection.StartRFID)?.Location ?? Point.Empty;
|
||||
var endPoint = rfidPoints.FirstOrDefault(p => p.RFIDValue == connection.EndRFID)?.Location ?? Point.Empty;
|
||||
if (startPoint.IsEmpty || endPoint.IsEmpty) continue;
|
||||
|
||||
var startPoint = rfidPoints.First(p => p.RFIDValue == connection.StartRFID).Location;
|
||||
var endPoint = rfidPoints.First(p => p.RFIDValue == connection.EndRFID).Location;
|
||||
lines.Add($"{startPoint.X},{startPoint.Y},{endPoint.X},{endPoint.Y}," +
|
||||
$"{connection.StartRFID},{connection.EndRFID},{connection.IsBidirectional},{connection.Distance}");
|
||||
}
|
||||
@@ -1625,19 +1725,38 @@ namespace AGVControl
|
||||
var validY = int.TryParse(rfidParts[1], out int valY);
|
||||
var validN = uint.TryParse(rfidParts[2], out uint valRfid);
|
||||
|
||||
|
||||
if (validX && validY && validN)
|
||||
{
|
||||
|
||||
|
||||
if (rfidPoints.Where(t => t.RFIDValue == valRfid).Any())
|
||||
{
|
||||
//이미존재한다
|
||||
var newvalue =
|
||||
sb.AppendLine($"rfid중복{valRfid}");
|
||||
}
|
||||
AddRFIDPoint(new Point(valX, valY), valRfid);
|
||||
|
||||
|
||||
var rfidPoint = new RFIDPoint
|
||||
{
|
||||
Location = new Point(valX, valY),
|
||||
RFIDValue = valRfid
|
||||
};
|
||||
|
||||
// 추가 속성 로드 (기본값 처리)
|
||||
if (rfidParts.Length >= 4)
|
||||
{
|
||||
bool isRotatable;
|
||||
bool.TryParse(rfidParts[3], out isRotatable);
|
||||
rfidPoint.IsRotatable = isRotatable;
|
||||
}
|
||||
if (rfidParts.Length >= 5 && !string.IsNullOrEmpty(rfidParts[4]))
|
||||
rfidPoint.FixedDirection = (Direction)Enum.Parse(typeof(Direction), rfidParts[4]);
|
||||
if (rfidParts.Length >= 6)
|
||||
{
|
||||
bool isTerminal;
|
||||
bool.TryParse(rfidParts[5], out isTerminal);
|
||||
rfidPoint.IsTerminal = isTerminal;
|
||||
}
|
||||
|
||||
rfidPoints.Add(rfidPoint);
|
||||
}
|
||||
else sb.AppendLine($"[{section}] {line}");
|
||||
}
|
||||
@@ -1908,7 +2027,8 @@ namespace AGVControl
|
||||
Direction = Direction.Backward,
|
||||
NextRFID = null,
|
||||
Reason = "AGV 위치 미확정(처음 기동)",
|
||||
ReasonCode = AGVActionReasonCode.NoPosition
|
||||
ReasonCode = AGVActionReasonCode.NoPosition,
|
||||
MoveState = AGVMoveState.Run
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1920,7 +2040,8 @@ namespace AGVControl
|
||||
Direction = agv.CurrentDirection,
|
||||
NextRFID = null,
|
||||
Reason = "경로 없음 또는 현재 위치 미확정",
|
||||
ReasonCode = AGVActionReasonCode.NoPath
|
||||
ReasonCode = AGVActionReasonCode.NoPath,
|
||||
MoveState = AGVMoveState.Stop
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1933,7 +2054,8 @@ namespace AGVControl
|
||||
Direction = agv.CurrentDirection,
|
||||
NextRFID = null,
|
||||
Reason = "현재 위치가 경로에 없음",
|
||||
ReasonCode = AGVActionReasonCode.NotOnPath
|
||||
ReasonCode = AGVActionReasonCode.NotOnPath,
|
||||
MoveState = AGVMoveState.Stop
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1948,7 +2070,11 @@ namespace AGVControl
|
||||
{
|
||||
// 목적지 바로 전 위치에서 목적지로 이동할 때의 방향
|
||||
var beforeDest = agv.CurrentPath[agv.CurrentPath.Count - 2];
|
||||
Direction arriveDir = (destPoint.X > beforeDest.X) ? Direction.Forward : Direction.Backward;
|
||||
float arriveDeltaX = destPoint.X - beforeDest.X;
|
||||
float arriveDeltaY = destPoint.Y - beforeDest.Y;
|
||||
Direction arriveDir = (Math.Abs(arriveDeltaX) > Math.Abs(arriveDeltaY)) ?
|
||||
(arriveDeltaX > 0 ? Direction.Forward : Direction.Backward) :
|
||||
(arriveDeltaY > 0 ? Direction.Forward : Direction.Backward);
|
||||
if (arriveDir != destRFID.FixedDirection.Value)
|
||||
{
|
||||
// 목적지 도달 전, 마지막 회전 가능한 RFID를 찾음
|
||||
@@ -1961,7 +2087,7 @@ namespace AGVControl
|
||||
}
|
||||
if (lastRotatableIdx >= 0)
|
||||
{
|
||||
// 회전 가능한 위치에 도달하면 NeedTurn 반환
|
||||
// 회전 가능한 위치에 도달하면 NeedTurn 반환 (STOP)
|
||||
if (idx == lastRotatableIdx)
|
||||
{
|
||||
var rfid = rfidPoints.FirstOrDefault(r => r.Location == agv.CurrentPath[lastRotatableIdx]);
|
||||
@@ -1970,35 +2096,43 @@ namespace AGVControl
|
||||
Direction = agv.CurrentDirection,
|
||||
NextRFID = rfid?.RFIDValue,
|
||||
Reason = "목적지 진입방향 맞추기 위해 회전 필요",
|
||||
ReasonCode = AGVActionReasonCode.NeedTurn
|
||||
ReasonCode = AGVActionReasonCode.NeedTurn,
|
||||
MoveState = AGVMoveState.Stop
|
||||
};
|
||||
}
|
||||
else if (idx < lastRotatableIdx)
|
||||
{
|
||||
// 회전 가능한 위치까지 이동 안내
|
||||
// 회전 가능한 위치까지 이동 안내 (RUN)
|
||||
var rfid = rfidPoints.FirstOrDefault(r => r.Location == agv.CurrentPath[lastRotatableIdx]);
|
||||
float moveDeltaX = agv.CurrentPath[lastRotatableIdx].X - agv.CurrentPosition.X;
|
||||
float moveDeltaY = agv.CurrentPath[lastRotatableIdx].Y - agv.CurrentPosition.Y;
|
||||
Direction moveDir = (Math.Abs(moveDeltaX) > Math.Abs(moveDeltaY)) ?
|
||||
(moveDeltaX > 0 ? Direction.Forward : Direction.Backward) :
|
||||
(moveDeltaY > 0 ? Direction.Forward : Direction.Backward);
|
||||
return new AGVActionPrediction
|
||||
{
|
||||
Direction = (agv.CurrentPath[lastRotatableIdx].X > agv.CurrentPosition.X) ? Direction.Forward : Direction.Backward,
|
||||
Direction = moveDir,
|
||||
NextRFID = rfid?.RFIDValue,
|
||||
Reason = "회전 가능한 위치로 이동 중",
|
||||
ReasonCode = AGVActionReasonCode.Normal
|
||||
ReasonCode = AGVActionReasonCode.Normal,
|
||||
MoveState = AGVMoveState.Run
|
||||
};
|
||||
}
|
||||
}
|
||||
// 회전 가능한 위치가 없음
|
||||
// 회전 가능한 위치가 없음 (STOP)
|
||||
return new AGVActionPrediction
|
||||
{
|
||||
Direction = agv.CurrentDirection,
|
||||
NextRFID = null,
|
||||
Reason = "경로상에 회전 가능한 위치가 없음",
|
||||
ReasonCode = AGVActionReasonCode.Unknown
|
||||
ReasonCode = AGVActionReasonCode.NoTurnPoint,
|
||||
MoveState = AGVMoveState.Stop
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 목적지 도달 시(방향이 맞는 경우)
|
||||
// 5. 목적지 도달 시(방향이 맞는 경우) (STOP)
|
||||
if (idx == agv.CurrentPath.Count - 1)
|
||||
{
|
||||
return new AGVActionPrediction
|
||||
@@ -2006,21 +2140,29 @@ namespace AGVControl
|
||||
Direction = agv.CurrentDirection,
|
||||
NextRFID = null,
|
||||
Reason = "경로의 마지막 지점(목적지 도달)",
|
||||
ReasonCode = AGVActionReasonCode.Arrived
|
||||
ReasonCode = AGVActionReasonCode.Arrived,
|
||||
MoveState = AGVMoveState.Stop
|
||||
};
|
||||
}
|
||||
|
||||
// 6. 일반 경로 주행
|
||||
// 6. 일반 경로 주행 (RUN)
|
||||
Point nextPoint = agv.CurrentPath[idx + 1];
|
||||
var nextRFID = rfidPoints.FirstOrDefault(r => r.Location == nextPoint)?.RFIDValue;
|
||||
Direction nextDir = (nextPoint.X > agv.CurrentPosition.X) ? Direction.Forward : Direction.Backward;
|
||||
|
||||
// X, Y 좌표 모두 고려한 방향 판단
|
||||
float deltaX = nextPoint.X - agv.CurrentPosition.X;
|
||||
float deltaY = nextPoint.Y - agv.CurrentPosition.Y;
|
||||
Direction nextDir = (Math.Abs(deltaX) > Math.Abs(deltaY)) ?
|
||||
(deltaX > 0 ? Direction.Forward : Direction.Backward) :
|
||||
(deltaY > 0 ? Direction.Forward : Direction.Backward);
|
||||
|
||||
return new AGVActionPrediction
|
||||
{
|
||||
Direction = nextDir,
|
||||
NextRFID = nextRFID,
|
||||
Reason = null,
|
||||
ReasonCode = AGVActionReasonCode.Normal
|
||||
ReasonCode = AGVActionReasonCode.Normal,
|
||||
MoveState = AGVMoveState.Run
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace AGVControl.Models
|
||||
{
|
||||
public enum Direction
|
||||
{
|
||||
Forward = 0,
|
||||
Backward = 1
|
||||
}
|
||||
|
||||
public class AGV
|
||||
{
|
||||
public Point CurrentPosition { get; set; }
|
||||
@@ -19,12 +26,17 @@ namespace AGVControl.Models
|
||||
/// </summary>
|
||||
public Direction TargetDirection { get; set; }
|
||||
public bool IsMoving { get; set; }
|
||||
public List<Point> CurrentPath { get; set; }
|
||||
public List<Point> CurrentPath { get; set; } = new List<Point>();
|
||||
public List<Point> PlannedPath { get; set; }
|
||||
public List<string> PathRFIDs { get; set; }
|
||||
public Point TargetPosition { get; set; }
|
||||
public uint TargetRFID { get; set; }
|
||||
|
||||
// 이동 경로 기록을 위한 새로운 속성들
|
||||
public List<uint> MovementHistory { get; set; } = new List<uint>();
|
||||
public List<Point> PositionHistory { get; set; } = new List<Point>();
|
||||
public const int HISTORY_SIZE = 4; // 최근 4개 위치 기록
|
||||
|
||||
public AGV()
|
||||
{
|
||||
CurrentPath = new List<Point>();
|
||||
@@ -45,12 +57,179 @@ namespace AGVControl.Models
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// 이동 경로에 새로운 RFID 추가
|
||||
public void AddToMovementHistory(uint rfidValue, Point position)
|
||||
{
|
||||
// 중복 RFID가 연속으로 들어오는 경우 무시
|
||||
if (MovementHistory.Count > 0 && MovementHistory[MovementHistory.Count - 1] == rfidValue)
|
||||
return;
|
||||
|
||||
public enum Direction
|
||||
{
|
||||
Forward,
|
||||
Backward
|
||||
MovementHistory.Add(rfidValue);
|
||||
PositionHistory.Add(position);
|
||||
|
||||
// 기록 크기 제한
|
||||
if (MovementHistory.Count > HISTORY_SIZE)
|
||||
{
|
||||
MovementHistory.RemoveAt(0);
|
||||
PositionHistory.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
// 연결 정보 기반 실제 이동 방향 계산
|
||||
public Direction? CalculateActualDirectionByConnection(uint currentRFID, uint previousRFID, List<RFIDConnection> connections)
|
||||
{
|
||||
if (connections == null || connections.Count == 0)
|
||||
return null;
|
||||
|
||||
// 이전 RFID에서 현재 RFID로의 연결 확인
|
||||
var connection = connections.FirstOrDefault(c =>
|
||||
(c.StartRFID == previousRFID && c.EndRFID == currentRFID) ||
|
||||
(c.IsBidirectional && c.StartRFID == currentRFID && c.EndRFID == previousRFID));
|
||||
|
||||
if (connection == null)
|
||||
return null; // 연결되지 않은 경로
|
||||
|
||||
// 연결 방향에 따라 실제 이동 방향 결정
|
||||
if (connection.StartRFID == previousRFID && connection.EndRFID == currentRFID)
|
||||
{
|
||||
return Direction.Forward; // Start -> End 방향으로 이동
|
||||
}
|
||||
else
|
||||
{
|
||||
return Direction.Backward; // End -> Start 방향으로 이동
|
||||
}
|
||||
}
|
||||
|
||||
// 연결 정보 기반 방향 불일치 검증 및 정정
|
||||
public bool ValidateAndCorrectDirectionByConnection(Direction expectedDirection, List<RFIDConnection> connections)
|
||||
{
|
||||
if (MovementHistory.Count < 2 || connections == null)
|
||||
return true; // 검증 불가능한 경우
|
||||
|
||||
// 최근 두 RFID 값 가져오기
|
||||
var recentRFIDs = MovementHistory.Skip(Math.Max(0, MovementHistory.Count - 2)).Take(2).ToList();
|
||||
if (recentRFIDs.Count < 2)
|
||||
return true;
|
||||
|
||||
var previousRFID = recentRFIDs[0];
|
||||
var currentRFID = recentRFIDs[1];
|
||||
|
||||
var actualDirection = CalculateActualDirectionByConnection(currentRFID, previousRFID, connections);
|
||||
if (!actualDirection.HasValue)
|
||||
return true; // 연결 정보로 방향 판단 불가
|
||||
|
||||
// 방향이 일치하지 않는 경우
|
||||
if (actualDirection.Value != expectedDirection)
|
||||
{
|
||||
// AGV 방향을 실제 이동 방향으로 정정
|
||||
CurrentDirection = actualDirection.Value;
|
||||
TargetDirection = actualDirection.Value;
|
||||
return false; // 정정됨을 알림
|
||||
}
|
||||
|
||||
return true; // 방향 일치
|
||||
}
|
||||
|
||||
// RFID 순서 기반 실제 이동 방향 계산 (기존 메서드 - 호환성 유지)
|
||||
public Direction? CalculateActualDirectionByRFID()
|
||||
{
|
||||
if (MovementHistory.Count < 2)
|
||||
return null;
|
||||
|
||||
// 최근 두 RFID 값으로부터 실제 이동 방향 계산
|
||||
var recentRFIDs = MovementHistory.Skip(Math.Max(0, MovementHistory.Count - 2)).Take(2).ToList();
|
||||
if (recentRFIDs.Count < 2)
|
||||
return null;
|
||||
|
||||
var prevRFID = recentRFIDs[0];
|
||||
var currentRFID = recentRFIDs[1];
|
||||
|
||||
// RFID 값의 증가/감소로 방향 판단
|
||||
if (currentRFID > prevRFID)
|
||||
{
|
||||
return Direction.Forward; // RFID 값이 증가하면 전진
|
||||
}
|
||||
else if (currentRFID < prevRFID)
|
||||
{
|
||||
return Direction.Backward; // RFID 값이 감소하면 후진
|
||||
}
|
||||
else
|
||||
{
|
||||
return null; // 같은 RFID 값이면 방향 판단 불가
|
||||
}
|
||||
}
|
||||
|
||||
// 경로상 RFID 순서 기반 예상 방향 계산
|
||||
public Direction? CalculateExpectedDirectionByRFID()
|
||||
{
|
||||
if (CurrentPath == null || CurrentPath.Count < 2)
|
||||
return null;
|
||||
|
||||
// 현재 위치의 RFID 찾기
|
||||
var currentRFID = CurrentRFID;
|
||||
if (currentRFID == 0)
|
||||
return null;
|
||||
|
||||
// 경로상 다음 RFID 찾기
|
||||
int currentIdx = CurrentPath.FindIndex(p => p == CurrentPosition);
|
||||
if (currentIdx < 0 || currentIdx >= CurrentPath.Count - 1)
|
||||
return null;
|
||||
|
||||
// 다음 위치의 RFID 찾기 (MapControl에서 RFID 정보 필요)
|
||||
// 이 부분은 MapControl에서 처리하도록 수정 필요
|
||||
return null;
|
||||
}
|
||||
|
||||
// 실제 이동 방향 계산 (기존 메서드 - 호환성 유지)
|
||||
public Direction? CalculateActualDirection()
|
||||
{
|
||||
if (MovementHistory.Count < 2)
|
||||
return null;
|
||||
|
||||
// 최근 두 위치로부터 실제 이동 방향 계산
|
||||
var recentPositions = PositionHistory.Skip(Math.Max(0, PositionHistory.Count - 2)).Take(2).ToList();
|
||||
if (recentPositions.Count < 2)
|
||||
return null;
|
||||
|
||||
var prevPos = recentPositions[0];
|
||||
var currentPos = recentPositions[1];
|
||||
|
||||
// X축 이동이 더 큰 경우
|
||||
if (Math.Abs(currentPos.X - prevPos.X) > Math.Abs(currentPos.Y - prevPos.Y))
|
||||
{
|
||||
return currentPos.X > prevPos.X ? Direction.Forward : Direction.Backward;
|
||||
}
|
||||
// Y축 이동이 더 큰 경우
|
||||
else
|
||||
{
|
||||
return currentPos.Y > prevPos.Y ? Direction.Forward : Direction.Backward;
|
||||
}
|
||||
}
|
||||
|
||||
// 경로상 예상 방향 계산
|
||||
public Direction? CalculateExpectedDirection()
|
||||
{
|
||||
if (CurrentPath == null || CurrentPath.Count < 2)
|
||||
return null;
|
||||
|
||||
int currentIdx = CurrentPath.FindIndex(p => p == CurrentPosition);
|
||||
if (currentIdx < 0 || currentIdx >= CurrentPath.Count - 1)
|
||||
return null;
|
||||
|
||||
var currentPos = CurrentPath[currentIdx];
|
||||
var nextPos = CurrentPath[currentIdx + 1];
|
||||
|
||||
// X축 이동이 더 큰 경우
|
||||
if (Math.Abs(nextPos.X - currentPos.X) > Math.Abs(nextPos.Y - currentPos.Y))
|
||||
{
|
||||
return nextPos.X > currentPos.X ? Direction.Forward : Direction.Backward;
|
||||
}
|
||||
// Y축 이동이 더 큰 경우
|
||||
else
|
||||
{
|
||||
return nextPos.Y > currentPos.Y ? Direction.Forward : Direction.Backward;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PathNode
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace AGVControl.Models
|
||||
public bool IsBidirectional { get; set; } // 양방향 연결 여부
|
||||
public bool IsRotatable { get; set; } // 회전 가능 여부
|
||||
public Direction? FixedDirection { get; set; } // 고정 방향(없으면 null)
|
||||
public bool IsTerminal { get; set; } // 종단 여부
|
||||
public RectangleF Bounds { get; set; }
|
||||
|
||||
public RFIDPoint()
|
||||
@@ -18,6 +19,7 @@ namespace AGVControl.Models
|
||||
IsRotatable = false; // 기본값은 회전 불가능
|
||||
IsBidirectional = true; // 기본값은 양방향
|
||||
FixedDirection = null;
|
||||
IsTerminal = false; // 기본값은 종단 아님
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,6 +200,14 @@ namespace arDev
|
||||
Current_LevelA = LastReceiveBuffer[23]; //<- 23번자료는 byte이므로 소수점이잇는 데이터로 직접 계산한다
|
||||
Current_DataTime = DateTime.Now;
|
||||
|
||||
|
||||
//250620 jwlee 추가
|
||||
int temp1 = (LastReceiveBuffer[27] << 8) | LastReceiveBuffer[28];
|
||||
int temp2 = (LastReceiveBuffer[29] << 8) | LastReceiveBuffer[30];
|
||||
|
||||
Current_temp1 = (temp1 - 2731) / 10f;
|
||||
Current_temp2 = (temp2 - 2731) / 10f;
|
||||
|
||||
CheckManualCharge();
|
||||
|
||||
Recv0 = true;
|
||||
@@ -333,6 +341,16 @@ namespace arDev
|
||||
/// </summary>
|
||||
public int Current_Amp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// BMS 온도값1
|
||||
/// </summary>
|
||||
public double Current_temp1 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// BMS 온도값2
|
||||
/// </summary>
|
||||
public double Current_temp2 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 총 전류량
|
||||
/// </summary>
|
||||
|
||||
@@ -46,12 +46,7 @@
|
||||
<Compile Include="BMSInformationEventArgs.cs" />
|
||||
<Compile Include="BMS.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CommData\CommData.csproj">
|
||||
<Project>{14e8c9a5-013e-49ba-b435-efefc77dd623}</Project>
|
||||
<Name>CommData</Name>
|
||||
</ProjectReference>
|
||||
<Compile Include="RS232.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
485
Cs_HMI/SubProject/BMS/RS232.cs
Normal file
485
Cs_HMI/SubProject/BMS/RS232.cs
Normal file
@@ -0,0 +1,485 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public abstract partial class arRS232 : IDisposable
|
||||
{
|
||||
protected System.IO.Ports.SerialPort _device;
|
||||
protected ManualResetEvent _mre;
|
||||
protected byte[] LastReceiveBuffer = new byte[] { };
|
||||
/// <summary>
|
||||
/// 최종 전송 메세지
|
||||
/// </summary>
|
||||
public byte[] lastSendBuffer = new byte[] { };
|
||||
|
||||
protected List<byte> tempBuffer = new List<byte>();
|
||||
protected Boolean findSTX = false;
|
||||
public string errorMessage { get; set; }
|
||||
public DateTime LastConnTime { get; set; }
|
||||
public DateTime LastConnTryTime { get; set; }
|
||||
public DateTime lastSendTime;
|
||||
/// <summary>
|
||||
/// 메세지 수신시 사용하는 내부버퍼
|
||||
/// </summary>
|
||||
protected List<byte> _buffer = new List<byte>();
|
||||
|
||||
|
||||
// public byte[] LastRecvData;
|
||||
public string LastRecvString
|
||||
{
|
||||
get
|
||||
{
|
||||
if (LastReceiveBuffer == null) return String.Empty;
|
||||
else return System.Text.Encoding.Default.GetString(LastReceiveBuffer);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 마지막으로 데이터를 받은 시간
|
||||
/// </summary>
|
||||
public DateTime lastRecvTime;
|
||||
|
||||
|
||||
public int WriteError = 0;
|
||||
public string WriteErrorMessage = string.Empty;
|
||||
public int WaitTimeout { get; set; } = 1000;
|
||||
public int MinRecvLength { get; set; } = 1;
|
||||
/// <summary>
|
||||
/// 포트이름
|
||||
/// </summary>
|
||||
[Description("시리얼 포트 이름")]
|
||||
[Category("설정"), DisplayName("Port Name")]
|
||||
public string PortName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_device == null) return string.Empty;
|
||||
else return _device.PortName;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (this.IsOpen)
|
||||
{
|
||||
Message?.Invoke(this, new MessageEventArgs("포트가 열려있어 포트이름을 변경할 수 없습니다", true));
|
||||
}
|
||||
else if (String.IsNullOrEmpty(value) == false)
|
||||
_device.PortName = value;
|
||||
else
|
||||
{
|
||||
Message?.Invoke(this, new MessageEventArgs("No PortName", true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int BaudRate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_device == null) return 0;
|
||||
else return _device.BaudRate;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (this.IsOpen)
|
||||
{
|
||||
Message?.Invoke(this, new MessageEventArgs("포트가 열려있어 BaudRate(를) 변경할 수 없습니다", true));
|
||||
}
|
||||
else if (value != 0)
|
||||
_device.BaudRate = value;
|
||||
else Message?.Invoke(this, new MessageEventArgs("No baud rate", true));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public arRS232()
|
||||
{
|
||||
_device = new System.IO.Ports.SerialPort();
|
||||
this.BaudRate = 9600;
|
||||
|
||||
_device.DataReceived += barcode_DataReceived;
|
||||
_device.ErrorReceived += this.barcode_ErrorReceived;
|
||||
_device.WriteTimeout = 5000;
|
||||
_device.ReadTimeout = 5000;
|
||||
// _device.DtrEnable = false;
|
||||
_device.ReadBufferSize = 8192;
|
||||
_device.WriteBufferSize = 8192;
|
||||
|
||||
errorMessage = string.Empty;
|
||||
lastRecvTime = DateTime.Parse("1982-11-23");
|
||||
LastConnTime = DateTime.Parse("1982-11-23");
|
||||
LastConnTryTime = DateTime.Parse("1982-11-23");
|
||||
lastRecvTime = DateTime.Parse("1982-11-23");
|
||||
this._mre = new ManualResetEvent(true);
|
||||
}
|
||||
|
||||
~arRS232()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
|
||||
// Flag: Has Dispose already been called?
|
||||
bool disposed = false;
|
||||
|
||||
// Public implementation of Dispose pattern callable by consumers.
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// Protected implementation of Dispose pattern.
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
// Free any other managed objects here.
|
||||
//
|
||||
}
|
||||
|
||||
_device.DataReceived -= barcode_DataReceived;
|
||||
_device.ErrorReceived -= this.barcode_ErrorReceived;
|
||||
|
||||
// Free any unmanaged objects here.
|
||||
//
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
public Boolean Open()
|
||||
{
|
||||
try
|
||||
{
|
||||
_device.Open();
|
||||
return IsOpen;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorMessage = ex.Message;
|
||||
Message.Invoke(this, new MessageEventArgs(ex.Message, true));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public string GetHexString(Byte[] input)
|
||||
{
|
||||
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||||
foreach (byte b in input)
|
||||
sb.Append(" " + b.ToString("X2"));
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 포트가 열려있는지 확인
|
||||
/// </summary>
|
||||
[Description("현재 시리얼포트가 열려있는지 확인합니다")]
|
||||
[Category("정보"), DisplayName("Port Open")]
|
||||
public Boolean IsOpen
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_device == null) return false;
|
||||
return _device.IsOpen;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Close(Boolean PortClose = true)
|
||||
{
|
||||
if (_device != null && _device.IsOpen)
|
||||
{
|
||||
_device.DiscardInBuffer();
|
||||
_device.DiscardOutBuffer();
|
||||
if (PortClose) _device.Close(); //dispose에서는 포트를 직접 클리어하지 않게 해뒀다.
|
||||
}
|
||||
}
|
||||
protected Boolean RaiseRecvData()
|
||||
{
|
||||
return RaiseRecvData(LastReceiveBuffer.ToArray(), false);
|
||||
}
|
||||
/// <summary>
|
||||
/// 수신받은 메세지를 발생 시킵니다
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public virtual Boolean RaiseRecvData(byte[] Data, bool udpatelastbuffer)
|
||||
{
|
||||
//181206 - 최종수신 메세지 기록
|
||||
lastRecvTime = DateTime.Now;
|
||||
if (udpatelastbuffer && Data != null)
|
||||
{
|
||||
if (LastReceiveBuffer == null || LastReceiveBuffer.Length != Data.Length)
|
||||
{
|
||||
LastReceiveBuffer = new byte[Data.Length];
|
||||
Array.Copy(Data, LastReceiveBuffer, Data.Length);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Message?.Invoke(this, new MessageEventArgs(Data, true)); //recvmessage
|
||||
if (ProcessRecvData(Data) == false)
|
||||
{
|
||||
//Message?.Invoke(this, new MessageEventArgs(Data, true)); //recvmessage
|
||||
Message?.Invoke(this, new MessageEventArgs(this.errorMessage, true)); //errormessage
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.errorMessage = ex.Message;
|
||||
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 수신받은 자료를 처리한다
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public abstract bool ProcessRecvData(byte[] data);
|
||||
|
||||
#region "Internal Events"
|
||||
|
||||
void barcode_ErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e)
|
||||
{
|
||||
Message?.Invoke(this, new MessageEventArgs(e.ToString(), true));
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[] { };
|
||||
void barcode_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
int ReadCount = _device.BytesToRead;
|
||||
|
||||
buffer = new byte[ReadCount];
|
||||
_device.Read(buffer, 0, buffer.Length);
|
||||
|
||||
System.Text.StringBuilder LogMsg = new StringBuilder();
|
||||
|
||||
byte[] remainBuffer;
|
||||
Repeat:
|
||||
if (CustomParser(buffer, out remainBuffer))
|
||||
{
|
||||
//분석완료이므로 받은 데이터를 버퍼에 기록한다
|
||||
if (LastReceiveBuffer == null || (LastReceiveBuffer.Length != tempBuffer.Count))
|
||||
Array.Resize(ref LastReceiveBuffer, tempBuffer.Count);
|
||||
Array.Copy(tempBuffer.ToArray(), LastReceiveBuffer, tempBuffer.Count);
|
||||
tempBuffer.Clear();
|
||||
|
||||
//수신메세지발생
|
||||
RaiseRecvData();
|
||||
if (remainBuffer != null && remainBuffer.Length > 0)
|
||||
{
|
||||
//버퍼를 변경해서 다시 전송을 해준다.
|
||||
Array.Resize(ref buffer, remainBuffer.Length);
|
||||
Array.Copy(remainBuffer, buffer, remainBuffer.Length);
|
||||
goto Repeat; //남은 버퍼가 있다면 진행을 해준다.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsOpen)
|
||||
{
|
||||
//_device.DiscardInBuffer();
|
||||
//_device.DiscardOutBuffer();
|
||||
}
|
||||
errorMessage = ex.Message;
|
||||
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region "External Events"
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 오류 및 기타 일반 메세지
|
||||
/// </summary>
|
||||
public event EventHandler<MessageEventArgs> Message;
|
||||
|
||||
#endregion
|
||||
|
||||
#region "Event Args"
|
||||
|
||||
/// <summary>
|
||||
/// 데이터를 수신할떄 사용함(RAW 포함)
|
||||
/// </summary>
|
||||
public class ReceiveDataEventArgs : EventArgs
|
||||
{
|
||||
private byte[] _buffer = null;
|
||||
|
||||
/// <summary>
|
||||
/// 바이트배열의 버퍼값
|
||||
/// </summary>
|
||||
public byte[] Value { get { return _buffer; } }
|
||||
|
||||
/// <summary>
|
||||
/// 버퍼(바이트배열)의 데이터를 문자로 반환합니다.
|
||||
/// </summary>
|
||||
public string StrValue
|
||||
{
|
||||
get
|
||||
{
|
||||
//return string.Empty;
|
||||
|
||||
if (_buffer == null || _buffer.Length < 1) return string.Empty;
|
||||
else return System.Text.Encoding.Default.GetString(_buffer);
|
||||
}
|
||||
}
|
||||
public ReceiveDataEventArgs(byte[] buffer)
|
||||
{
|
||||
_buffer = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 메세지를 강제 발생
|
||||
/// </summary>
|
||||
/// <param name="mt"></param>
|
||||
/// <param name="message"></param>
|
||||
protected virtual void RaiseMessage(MessageType mt, string message)
|
||||
{
|
||||
this.Message?.Invoke(this, new MessageEventArgs(mt, message));
|
||||
}
|
||||
public enum MessageType
|
||||
{
|
||||
Normal,
|
||||
Error,
|
||||
Send,
|
||||
Recv,
|
||||
}
|
||||
|
||||
public class MessageEventArgs : EventArgs
|
||||
{
|
||||
public MessageType MsgType { get; set; }
|
||||
private string _message = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Recv,Send,Normal,Error 모두 지원
|
||||
/// </summary>
|
||||
public string Message { get { return _message; } }
|
||||
|
||||
private byte[] _data = null;
|
||||
|
||||
/// <summary>
|
||||
/// Recv,Send에서만 값이 존재 합니다
|
||||
/// </summary>
|
||||
public byte[] Data { get { return _data; } }
|
||||
public MessageEventArgs(string Message, bool isError = false)
|
||||
{
|
||||
if (isError) MsgType = MessageType.Error;
|
||||
else MsgType = MessageType.Normal;
|
||||
_message = Message;
|
||||
}
|
||||
public MessageEventArgs(MessageType msgtype, string Message)
|
||||
{
|
||||
MsgType = msgtype;
|
||||
_message = Message;
|
||||
_data = System.Text.Encoding.Default.GetBytes(Message);
|
||||
}
|
||||
|
||||
public MessageEventArgs(byte[] buffer, bool isRecv = true)
|
||||
{
|
||||
if (isRecv) MsgType = MessageType.Recv;
|
||||
else MsgType = MessageType.Send;
|
||||
_data = new byte[buffer.Length];
|
||||
Array.Copy(buffer, _data, Data.Length);
|
||||
_message = System.Text.Encoding.Default.GetString(_data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
protected abstract bool CustomParser(byte[] buf, out byte[] remainBuffer);
|
||||
|
||||
/// <summary>
|
||||
/// 데이터수신시간이 설정값보다 x 2.5를 초과하면 false 가 반환됨
|
||||
/// </summary>
|
||||
public Boolean IsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsOpen == false) return false;
|
||||
if (lastRecvTime.Year == 1982) return false;
|
||||
var ts = DateTime.Now - lastRecvTime;
|
||||
if (ts.TotalSeconds > 5) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
protected bool WriteData(string cmd)
|
||||
{
|
||||
return WriteData(System.Text.Encoding.Default.GetBytes(cmd));
|
||||
}
|
||||
/// <summary>
|
||||
/// 포트에 쓰기(barcode_DataReceived 이벤트로 메세지수신)
|
||||
/// </summary>
|
||||
protected Boolean WriteData(byte[] data)
|
||||
{
|
||||
Boolean bRet = false;
|
||||
|
||||
//171205 : 타임아웃시간추가
|
||||
if (!_mre.WaitOne(WaitTimeout))
|
||||
{
|
||||
errorMessage = $"WriteData:MRE:WaitOne:TimeOut {WaitTimeout}ms";
|
||||
this.Message?.Invoke(this, new MessageEventArgs(errorMessage, true));
|
||||
return false;
|
||||
}
|
||||
|
||||
_mre.Reset();
|
||||
|
||||
//Array.Resize(ref data, data.Length + 2);
|
||||
|
||||
try
|
||||
{
|
||||
lastSendTime = DateTime.Now;
|
||||
if (lastSendBuffer == null) lastSendBuffer = new byte[data.Length]; //171113
|
||||
else Array.Resize(ref lastSendBuffer, data.Length);
|
||||
Array.Copy(data, lastSendBuffer, data.Length);
|
||||
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
_device.Write(data, i, 1);
|
||||
|
||||
//_device.Write(data, 0, data.Length);
|
||||
|
||||
//171113
|
||||
this.Message?.Invoke(this, new MessageEventArgs(data, false));
|
||||
|
||||
bRet = true;
|
||||
WriteError = 0;
|
||||
WriteErrorMessage = string.Empty;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// this.isinit = false;
|
||||
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
|
||||
bRet = false;
|
||||
WriteError += 1; //연속쓰기오류횟수
|
||||
WriteErrorMessage = ex.Message;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_mre.Set();
|
||||
}
|
||||
return bRet;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user