
LS PLC(XGT 시리즈)와 외부 장치 간 통신을 구현하기 위해서는 단순히 소켓을 열고 데이터를 보내는 것만으로는 정상적인 통신이 이루어지지 않습니다. LS PLC의 데이터 전송은 헤더(Header) + 프레임(Frame) 구조로 이루어져 있으며, 이 두 요소를 정확한 형식으로 조합해야만 PLC가 해당 데이터를 정상적인 명령으로 인식하고 처리할 수 있습니다.
특히 읽기(Read)와 쓰기(Write) 명령은 프레임 부분만 달라질 뿐, 공통적으로 동일한 헤더 구조를 사용합니다.따라서 통신 구현의 첫 단계는 읽기·쓰기 명령 이전에 헤더 구조를 정확히 이해하고 생성하는 것입니다.
이 글에서는 LS PLC XGT 통신에서 반드시 포함되어야 하는 전용 서비스 헤더 구조를 먼저 살펴보고, 이를 기반으로 이후 글에서 읽기 / 쓰기 명령 프레임을 단계적으로 만들어 보도록 하겠습니다.
1. LS PLC XGT 전용 서비스 헤더 구조
LS PLC와의 통신은 [헤더(Header) + 명령어 프레임(Instruction Frame)]의 결합으로 이루어집니다.
헤더는 통신 대상과 데이터의 길이를 정의하는 핵심 부분입니다.

헤더 상세 명세 (20바이트)
| 바이트 인덱스 | 항목 (Item) | 크기(Byte) | 설정값 | 상세 설명 |
|---|---|---|---|---|
| 0 ~ 7 | Company ID | 8 | LSIS-XGT | 고정 문자열 (ASCII 코드값) |
| 8 ~ 9 | Reserved | 2 | 0x0000 | 예약 영역 (고정값) |
| 10 ~ 11 | PLC Info | 2 | 0x0000 | Client(PC) 접속 시 고정값 |
| 12 | CPU Info | 1 | 0xA0 | XGK / XGB 모델 기준 고정 |
| 13 | Source of Frame | 1 | 0x33 | PC(Client)에서 전송 시 고정 |
| 14 ~ 15 | Invoke ID | 2 | 0x0001 | 프레임 식별 번호 (임의 설정 가능) |
| 16 ~ 17 | Length | 2 | 가변 | 헤더 이후 데이터 프레임의 바이트 수 |
| 18 | FEnet Position | 1 | 0x00 | 통신 모듈 슬롯 번호 (CPU 직결 시 0) |
| 19 | BCC (Checksum) | 1 | 계산값 | 중요: 헤더 0 ~ 18번 바이트 합계의 하위 1바이트 |
2. C# 기반 헤더 및 BCC 체크 코드
C#에서 byte 배열을 다루어 헤더를 생성하는 공통 함수 예시입니다.
구조체(Struct)와 마샬링(Marshalling) 기술을 활용하여, 복잡한 바이트 연산 없이도 직관적으로 헤더를 생성하고 BCC 체크섬을 계산하여 전체 프레임을 완성하는 과정을 보여줍니다.
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct _LS_FENET_HEADER
{
// --- 헤더 멤버 구성 (20바이트 고정) ---
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public char[] CompanyID; // [0~7] Company ID ("LSIS-XGT", ASCII, 고정)
public ushort Reserved; // [8~9] Reserved (0x0000 고정)
public ushort PLC_Info; // [10~11] PLC Info (Client PC 접속 시 0x0000 고정)
public byte CPU_Info; // [12] CPU Info (0xA0 : XGK / XGB)
public byte Source_of_Frame; // [13] Source of Frame (0x33 : PC → PLC)
public ushort InVoke_ID; // [14~15] Invoke ID (요청/응답 식별자, 임의 설정)
public ushort Length; // [16~17] Length (헤더 이후 바디 데이터 길이, Byte)
public byte FEnet_Position; // [18] FEnet Position (CPU 직결 시 0x00)
public byte Reserved_BCC; // [19] BCC (0~18 Byte 합계의 하위 1 Byte)
// 데이터 준비부터 전송용 배열 생성까지 한 번에 처리
public byte[] GetSendBuffer(List<byte> body)
{
// 1. 헤더 기본 정보 설정
this.CompanyID = "LSIS-XGT".ToCharArray();
this.Reserved = 0x00;
this.PLC_Info = 0x00;
this.CPU_Info = 0xA0; // XGK 기준
this.Source_of_Frame = 0x33; // 클라이언트 -> 서버
this.InVoke_ID = 0x01;
this.Length = (ushort)body.Count; // 바디(데이터)의 길이를 헤더에 기록
this.FEnet_Position = 0x00;
this.Reserved_BCC = 0x00; // 계산 전 초기화
// 2. 구조체를 바이트 배열로 변환 (마샬링)
byte[] headerBytes = new byte[Marshal.SizeOf(this)];
unsafe
{
fixed (byte* ptr = headerBytes)
{
Marshal.StructureToPtr(this, (IntPtr)ptr, false);
}
}
// 3. BCC 체크섬 계산 (헤더의 0번~18번 바이트까지 합산)
int checkSum = 0;
for (int i = 0; i < headerBytes.Length - 1; i++)
{
checkSum += headerBytes[i];
}
headerBytes[headerBytes.Length - 1] = (byte)(checkSum & 0xFF);
// 4. 최종 패킷 조립 (헤더 20바이트 + 바디 n바이트)
byte[] fullPacket = new byte[headerBytes.Length + body.Count];
Array.Copy(headerBytes, 0, fullPacket, 0, headerBytes.Length);
Array.Copy(body.ToArray(), 0, fullPacket, headerBytes.Length, body.Count);
return fullPacket;
}
}
마무리 및 요약
위의 구현 방식은 LS PLC 통신의 표준을 C#의 객체지향적 특징을 살려 정리한 것입니다.
- 정교한 구조체 설계: LayoutKind.Sequential과 Pack = 1을 사용하여 메모리 상의 데이터 순서가 PLC 규격과 1:1로 일치하도록 보장했습니다.
- 효율적인 마샬링: unsafe 블록과 fixed 포인터를 사용하여 구조체 데이터를 바이트 배열로 빠르게 변환함으로써 성능 손실을 최소화했습니다.
- 유연한 데이터 결합: GetSendBuff 함수 하나로 헤더 생성, BCC 계산, 그리고 실제 명령어 프레임 결합까지 처리할 수 있어 코드의 재사용성이 높습니다.
이제 이 공통 헤더 구조체를 사용해서, 개별 변수 읽기 및 쓰기 명령어 프레임을 조립하는 단계로 나아갈 수 있습니다.
'실무 코드 보관소 > C#' 카테고리의 다른 글
| [LS PLC 통신 2-4] C# 실전: 이더넷 개별 쓰기 방식 구현 (2) | 2021.12.07 |
|---|---|
| [LS PLC 통신 2-3] C# 실전: 이더넷 연속 데이터 읽기 완벽 정리 (1) | 2021.12.07 |
| [미쓰비시 PLC 3-1] C# 실전: 이더넷 실시간 통신 소스 공유(ASCII+BIN) (5) | 2021.11.23 |
| C# 비트연산 한 번에 정리 – AND, OR, XOR 실무 예제 모음 (2) | 2021.11.19 |
| [ C# ] 키보드 기능키 ( Num Lock, Caps Lock , Scroll Lock ) 확인하기 (0) | 2021.11.16 |