This commit is contained in:
backuppc
2025-11-05 14:28:32 +09:00
parent 77a9d40662
commit 1eb59a0127
5 changed files with 253 additions and 229 deletions

View File

@@ -41,13 +41,13 @@ This README provides a comprehensive overview of the `ENIGProtocol` library, mak
public enum DeviceType : byte public enum DeviceType : byte
{ {
ACS = 0, ACS = 0,
AGV1 = 10, AGV1 = 10+1,
AGV2 = 11, AGV2 = 10+2,
BUFFER1 = 20, BUFFER1 = 20+1,
BUFFER2 = 21, BUFFER2 = 20+2,
BUFFER3 = 22, BUFFER3 = 20+3,
BUFFER4 = 23, BUFFER4 = 20+4,
BUFFER5 = 24, BUFFER5 = 20+5,
DOOR = 30, DOOR = 30,
} }
``` ```
@@ -82,6 +82,9 @@ public enum DeviceType : byte
- H -> E | Move : cmd(100) : 대상태그까지 이동(자동이동) - H -> E | Move : cmd(100) : 대상태그까지 이동(자동이동)
- Target[1] = {DeviceType} - Target[1] = {DeviceType}
- TagID[4] = 0000 - TagID[4] = 0000
- H -> E | Move : cmd(107) : 대상별칭까지 이동(자동이동)
- Target[1] = {DeviceType}
- TagID[4] = 0000
- H -> E | Stop : cmd(101) : 멈춤 - H -> E | Stop : cmd(101) : 멈춤
- H -> E | Reset : cmd(102) : 오류 소거 - H -> E | Reset : cmd(102) : 오류 소거
- H -> E | Charge On: cmd(103) : 충전실행(충전기 이동 후 자동 충전 진행) - H -> E | Charge On: cmd(103) : 충전실행(충전기 이동 후 자동 충전 진행)

View File

@@ -4,7 +4,10 @@ using System.Text;
namespace ENIGProtocol namespace ENIGProtocol
{ {
public enum AGVCommands /// <summary>
/// host -> eq
/// </summary>
public enum AGVCommandHE : byte
{ {
Goto = 100, Goto = 100,
Stop = 101, Stop = 101,
@@ -14,4 +17,22 @@ namespace ENIGProtocol
MarkStop, MarkStop,
LiftControl LiftControl
} }
/// <summary>
/// eq -> host
/// </summary>
public enum AGVCommandEH : byte
{
Error = 1,
Arrived = 2,
ReadRFID = 3,
Status=9,
}
public enum AGVErrorCode : byte
{
PredictFix,
TurnTimeout,
TurnError,
}
} }

View File

@@ -1,55 +1,55 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
namespace ENIG namespace ENIG
{ {
// 장비 타입 정의 // 장비 타입 정의
public enum DeviceType public enum DeviceType
{ {
ACS = 0, ACS = 0,
AGV1 = 10, AGV1 = 10,
AGV2 = 11, AGV2 = 11,
BUFFER1 = 20, BUFFER1 = 20,
BUFFER2 = 21, BUFFER2 = 21,
BUFFER3 = 22, BUFFER3 = 22,
BUFFER4 = 23, BUFFER4 = 23,
BUFFER5 = 24, BUFFER5 = 24,
DOOR = 30, DOOR = 30,
} }
public partial class EEProtocol public partial class EEProtocol
{ {
// 패킷 수신 이벤트 정의 // 패킷 수신 이벤트 정의
// 데이터 수신 이벤트 정의 // 데이터 수신 이벤트 정의
public event EventHandler<DataEventArgs> OnDataReceived; public event EventHandler<DataEventArgs> OnDataReceived;
public event EventHandler<MessageEventArgs> OnMessage; public event EventHandler<MessageEventArgs> OnMessage;
// CRC16 계산을 위한 테이블 // CRC16 계산을 위한 테이블
private static readonly ushort[] CRC16_TABLE = new ushort[256]; private static readonly ushort[] CRC16_TABLE = new ushort[256];
// CRC16 테이블 초기화 // CRC16 테이블 초기화
public EEProtocol() public EEProtocol()
{ {
const ushort polynomial = 0x7979; const ushort polynomial = 0x7979;
for (ushort i = 0; i < CRC16_TABLE.Length; i++) for (ushort i = 0; i < CRC16_TABLE.Length; i++)
{ {
ushort value = 0; ushort value = 0;
ushort temp = i; ushort temp = i;
for (byte j = 0; j < 8; j++) for (byte j = 0; j < 8; j++)
{ {
if (((value ^ temp) & 0x0001) != 0) if (((value ^ temp) & 0x0001) != 0)
{ {
value = (ushort)((value >> 1) ^ polynomial); value = (ushort)((value >> 1) ^ polynomial);
} }
else else
{ {
value >>= 1; value >>= 1;
} }
temp >>= 1; temp >>= 1;
} }
CRC16_TABLE[i] = value; CRC16_TABLE[i] = value;
} }
//// CRC 테이블 출력 //// CRC 테이블 출력
@@ -62,162 +62,162 @@ namespace ENIG
// } // }
// Console.Write($"0x{CRC16_TABLE[i]:X4}, "); // Console.Write($"0x{CRC16_TABLE[i]:X4}, ");
//} //}
//Console.WriteLine(); //Console.WriteLine();
} }
// CRC16 계산 메서드 // CRC16 계산 메서드
public ushort CalculateCRC16(byte[] data) public ushort CalculateCRC16(byte[] data)
{ {
ushort crc = 0xFFFF; ushort crc = 0xFFFF;
for (int i = 0; i < data.Length; i++) for (int i = 0; i < data.Length; i++)
{ {
byte index = (byte)(crc ^ data[i]); byte index = (byte)(crc ^ data[i]);
crc = (ushort)((crc >> 8) ^ CRC16_TABLE[index]); crc = (ushort)((crc >> 8) ^ CRC16_TABLE[index]);
} }
return crc; return crc;
} }
// 패킷 생성 메서드 // 패킷 생성 메서드
public byte[] CreatePacket(byte id, byte command, byte[] data) public byte[] CreatePacket(byte id, byte command, byte[] data)
{ {
var packet = new Packet var packet = new Packet
{ {
ID = id, ID = id,
Command = command, Command = command,
Data = data ?? new byte[0], Data = data ?? new byte[0],
Length = (byte)(1 + 1 + (data?.Length ?? 0)) // ID + Command + Data 길이 Length = (byte)(1 + 1 + (data?.Length ?? 0)) // ID + Command + Data 길이
}; };
// 패킷 조립 // 패킷 조립
List<byte> packetData = new List<byte>(); List<byte> packetData = new List<byte>();
packetData.Add(Packet.STX); packetData.Add(Packet.STX);
packetData.Add(packet.Length); packetData.Add(packet.Length);
packetData.Add(packet.ID); packetData.Add(packet.ID);
packetData.Add(packet.Command); packetData.Add(packet.Command);
if (packet.Data != null) if (packet.Data != null)
packetData.AddRange(packet.Data); packetData.AddRange(packet.Data);
// CRC16 계산 // CRC16 계산
packet.CRC16 = CalculateCRC16(packetData.Skip(1).ToArray()); // STX 제외하고 계산 packet.CRC16 = CalculateCRC16(packetData.Skip(1).ToArray()); // STX 제외하고 계산
packetData.AddRange(BitConverter.GetBytes(packet.CRC16)); packetData.AddRange(BitConverter.GetBytes(packet.CRC16));
packetData.Add(Packet.ETX); packetData.Add(Packet.ETX);
return packetData.ToArray(); return packetData.ToArray();
} }
//패킷테스트 //패킷테스트
public void PacketTest(byte[] rawData) public void PacketTest(byte[] rawData)
{ {
var hexstr = string.Join(" ", rawData.Select(t => t.ToString("X2"))); var hexstr = string.Join(" ", rawData.Select(t => t.ToString("X2")));
RaiseMessage( $"TestPacket : {hexstr}"); RaiseMessage( $"TestPacket : {hexstr}");
ParsePacket(rawData); ParsePacket(rawData);
} }
//메세지 발생 //메세지 발생
public void RaiseMessage(string message, bool isError = false) public void RaiseMessage(string message, bool isError = false)
{ {
OnMessage?.Invoke(this, new MessageEventArgs { IsError = isError, Message = message }); OnMessage?.Invoke(this, new MessageEventArgs { IsError = isError, Message = message });
} }
// 패킷 파싱 메서드 // 패킷 파싱 메서드
public bool ParsePacket(byte[] rawData) public bool ParsePacket(byte[] rawData)
{ {
try try
{ {
if (rawData.Length < 7) // 최소 패킷 크기 if (rawData.Length < 7) // 최소 패킷 크기
{ {
var hexstring = string.Join(" ", rawData.Select(t => t.ToString("X2"))); var hexstring = string.Join(" ", rawData.Select(t => t.ToString("X2")));
RaiseMessage($"Too Short Data:{hexstring}"); RaiseMessage($"Too Short Data:{hexstring}");
return false; return false;
} }
if (rawData[0] != Packet.STX || rawData[rawData.Length - 1] != Packet.ETX) if (rawData[0] != Packet.STX || rawData[rawData.Length - 1] != Packet.ETX)
{ {
var hexstring = string.Join(" ", rawData.Select(t => t.ToString("X2"))); var hexstring = string.Join(" ", rawData.Select(t => t.ToString("X2")));
RaiseMessage($"STX/ETX Error Data:{hexstring}"); RaiseMessage($"STX/ETX Error Data:{hexstring}");
return false; return false;
} }
byte length = rawData[1]; byte length = rawData[1];
if (length + 5 != rawData.Length) // STX + Length + CRC16(2) + ETX = 5 if (length + 5 != rawData.Length) // STX + Length + CRC16(2) + ETX = 5
{ {
var hexstring = string.Join(" ", rawData.Select(t => t.ToString("X2"))); var hexstring = string.Join(" ", rawData.Select(t => t.ToString("X2")));
RaiseMessage($"Length Error ({length+5} != {rawData.Length}) Data:{hexstring}"); RaiseMessage($"Length Error ({length+5} != {rawData.Length}) Data:{hexstring}");
return false; return false;
} }
// CRC16 검증 // CRC16 검증
byte[] dataForCrc = rawData.Skip(1).Take(length + 1).ToArray(); byte[] dataForCrc = rawData.Skip(1).Take(length + 1).ToArray();
ushort calculatedCrc = CalculateCRC16(dataForCrc); ushort calculatedCrc = CalculateCRC16(dataForCrc);
ushort receivedCrc = BitConverter.ToUInt16(rawData, rawData.Length - 3); ushort receivedCrc = BitConverter.ToUInt16(rawData, rawData.Length - 3);
if (receivedCrc != 0xFFFF && calculatedCrc != receivedCrc) //FF 무시 if (receivedCrc != 0xFFFF && calculatedCrc != receivedCrc) //FF 무시
{ {
RaiseMessage($"CRC Error ID:{rawData[2]:X2},CMD:{rawData[3]:X2}", true); RaiseMessage($"CRC Error ID:{rawData[2]:X2},CMD:{rawData[3]:X2}", true);
return false; return false;
} }
// 패킷 생성 // 패킷 생성
var packet = new Packet var packet = new Packet
{ {
Length = length, Length = length,
ID = rawData[2], ID = rawData[2],
Command = rawData[3], Command = rawData[3],
Data = rawData.Skip(4).Take(length - 2).ToArray(), // ID와 Command 길이(2) 제외 Data = rawData.Skip(4).Take(length - 2).ToArray(), // ID와 Command 길이(2) 제외
CRC16 = receivedCrc, CRC16 = receivedCrc,
RawData = rawData, RawData = rawData,
}; };
// 이벤트 발생 // 이벤트 발생
OnDataReceived?.Invoke(this, new DataEventArgs { ReceivedPacket = packet }); OnDataReceived?.Invoke(this, new DataEventArgs { ReceivedPacket = packet });
return true; return true;
} }
catch catch
{ {
return false; return false;
} }
} }
// 데이터 수신 처리 메서드 (시리얼 포트에서 데이터를 받았을 때 호출) // 데이터 수신 처리 메서드 (시리얼 포트에서 데이터를 받았을 때 호출)
private List<byte> buffer = new List<byte>(); private List<byte> buffer = new List<byte>();
public void ProcessReceivedData(byte[] data) public void ProcessReceivedData(byte[] data)
{ {
buffer.AddRange(data); buffer.AddRange(data);
while (buffer.Count > 0) while (buffer.Count > 0)
{ {
// STX 찾기 // STX 찾기
int stxIndex = buffer.FindIndex(b => b == Packet.STX); int stxIndex = buffer.FindIndex(b => b == Packet.STX);
if (stxIndex == -1) if (stxIndex == -1)
{ {
buffer.Clear(); buffer.Clear();
break; break;
} }
// 불필요한 데이터 제거 // 불필요한 데이터 제거
if (stxIndex > 0) if (stxIndex > 0)
buffer.RemoveRange(0, stxIndex); buffer.RemoveRange(0, stxIndex);
// 패킷 길이 확인을 위한 최소 데이터 확인 // 패킷 길이 확인을 위한 최소 데이터 확인
if (buffer.Count < 2) if (buffer.Count < 2)
break; break;
int expectedLength = buffer[1] + 5; // 전체 패킷 길이 int expectedLength = buffer[1] + 5; // 전체 패킷 길이
if (buffer.Count < expectedLength) if (buffer.Count < expectedLength)
break; break;
// 패킷 추출 및 처리 // 패킷 추출 및 처리
byte[] packetData = buffer.Take(expectedLength).ToArray(); byte[] packetData = buffer.Take(expectedLength).ToArray();
buffer.RemoveRange(0, expectedLength); buffer.RemoveRange(0, expectedLength);
ParsePacket(packetData); ParsePacket(packetData);
} }
} }
} }
} }

View File

@@ -1,18 +1,18 @@
using System; using System;
namespace ENIG namespace ENIG
{ {
public partial class EEProtocol public partial class EEProtocol
{ {
public class MessageEventArgs : EventArgs public class MessageEventArgs : EventArgs
{ {
public string Message { get; set; } public string Message { get; set; }
public bool IsError { get; set; } public bool IsError { get; set; }
} }
public class DataEventArgs : EventArgs public class DataEventArgs : EventArgs
{ {
public Packet ReceivedPacket { get; set; } public Packet ReceivedPacket { get; set; }
} }
} }
} }

View File

@@ -1,20 +1,20 @@
namespace ENIG namespace ENIG
{ {
// 패킷 구조체 // 패킷 구조체
public class Packet public class Packet
{ {
public const byte STX = 0x02; public const byte STX = 0x02;
public const byte ETX = 0x03; public const byte ETX = 0x03;
public byte Length { get; set; } public byte Length { get; set; }
public byte ID { get; set; } public byte ID { get; set; }
public byte Command { get; set; } public byte Command { get; set; }
public byte[] Data { get; set; } public byte[] Data { get; set; }
public ushort CRC16 { get; set; } public ushort CRC16 { get; set; }
public byte[] RawData { get; set; } public byte[] RawData { get; set; }
public Packet() public Packet()
{ {
Data = new byte[0]; Data = new byte[0];
} }
} }
} }