Dreamine.Communication.Serial
Dreamine.Communication.Serial는 Dreamine Communication 계열 패키지의 일부입니다.
이 패키지는 RS232 및 SerialPort 기반 전송 구현체를 제공하며, 시리얼 장비 관련 책임을 상위 애플리케이션 계층과 분리합니다.
설명
Dreamine Communication을 위한 시리얼 전송 패키지입니다. .NET SerialPort를 열고 관리하며, 포트 스트림을 Dreamine 공통 전송 추상화에 연결하고, 선택된 프로토콜 어댑터와 프레임 코덱을 통해 MessageEnvelope를 송수신합니다.
패키지 역할
Dreamine.Communication.Abstractions
↑
Dreamine.Communication.Core
↑
Dreamine.Communication.Serial
Serial 패키지는 구체적인 시리얼 통신 경계만 담당합니다.
- 시리얼 포트 열기/닫기
- RS232 통신 옵션 적용
SerialPort.BaseStream기반 수신 루프 실행IMessageProtocolAdapter를 통한 송신MessageEnvelope인코딩IMessageFrameCodec을 통한 스트림 데이터 분리 및 기록
애플리케이션 명령 규칙, UI 상태, 라우팅 정책, TCP 연결 로직, RabbitMQ 연결 로직, WPF 전용 로직은 이 패키지의 책임이 아닙니다.
주요 기능
SerialPort기반 Transport- RS232 통신 경계
MessageEnvelope기반 송수신 흐름- 설정 가능한 프로토콜 어댑터
- 설정 가능한 프레임 코덱
Core의 공통 JSON 직렬화 및 프로토콜 어댑터 사용Core의 공통 스트림 프레임 처리 사용- 연결 상태 관리를 포함한 비동기 수신 루프
주요 구성 요소
| 타입 | 역할 |
|---|---|
SerialPortTransport |
RS232 / 시리얼 통신용 IMessageTransport 구현체입니다. |
SerialPortTransportOptions |
시리얼 포트 설정 모델입니다. |
SerialPortStreamAdapter |
SerialPort.BaseStream 접근을 감싸는 작은 어댑터입니다. |
SerialCommunicationException |
시리얼 통신 계층 전용 예외 타입입니다. |
SerialPortTransport
SerialPortTransport는 공통 IMessageTransport 계약을 구현합니다.
기본 생성자 동작은 다음과 같습니다.
Protocol adapter : DreamineEnvelopeProtocolAdapter
Frame codec : LengthPrefixedMessageFrameCodec
이 기본값은 Dreamine 간 시리얼 통신에는 맞습니다. 외부 장비, PLC, 바코드 리더, 검사 장비, 터미널 유틸리티와 통신할 때는 프로토콜 어댑터와 프레임 코덱을 명시적으로 전달하는 편이 맞습니다.
예시:
using System.IO.Ports;
using System.Text;
using Dreamine.Communication.Core.Framing;
using Dreamine.Communication.Core.Protocols;
using Dreamine.Communication.Serial.Options;
using Dreamine.Communication.Serial.Ports;
var transport = new SerialPortTransport(
new SerialPortTransportOptions
{
PortName = "COM3",
BaudRate = 9600,
DataBits = 8,
Parity = Parity.None,
StopBits = StopBits.One,
Handshake = Handshake.None
},
new PlainTextProtocolAdapter(
Encoding.UTF8,
"serial.raw.available",
"Serial.RawAvailable"),
new RawAvailableMessageFrameCodec());
transport.MessageReceived += (_, message) =>
{
var text = Encoding.UTF8.GetString(message.Payload);
Console.WriteLine($"RX: {text}");
};
await transport.ConnectAsync();
SerialPortTransportOptions
| 옵션 | 기본값 | 의미 |
|---|---|---|
PortName |
COM1 |
시리얼 포트 이름입니다. |
BaudRate |
9600 |
통신 속도입니다. |
DataBits |
8 |
데이터 비트 수입니다. 유효 범위는 5~8입니다. |
Parity |
Parity.None |
패리티 설정입니다. |
StopBits |
StopBits.One |
Stop bit 설정입니다. |
Handshake |
Handshake.None |
하드웨어/소프트웨어 흐름 제어 설정입니다. |
ReadTimeoutMs |
3000 |
읽기 타임아웃(ms)입니다. |
WriteTimeoutMs |
3000 |
쓰기 타임아웃(ms)입니다. |
ReadBufferSize |
4096 |
수신 버퍼 크기입니다. |
WriteBufferSize |
4096 |
송신 버퍼 크기입니다. |
프로토콜 어댑터와 프레임 코덱
시리얼 통신도 스트림 기반입니다. Transport 자체는 애플리케이션 메시지가 어디서 시작하고 끝나는지 알 수 없습니다. 그래서 메시지 의미와 메시지 경계 처리를 의도적으로 분리합니다.
| 책임 | 타입 |
|---|---|
바이트와 MessageEnvelope 간 변환 |
IMessageProtocolAdapter |
| 스트림에서 메시지 경계 감지 | IMessageFrameCodec |
| COM 포트 열기/닫기/송신/수신 | SerialPortTransport |
권장 조합
| 상황 | 프로토콜 어댑터 | 프레임 코덱 |
|---|---|---|
| Dreamine 간 시리얼 통신 | DreamineEnvelopeProtocolAdapter |
LengthPrefixedMessageFrameCodec |
메시지 끝에 CRLF 또는 LF가 붙는 텍스트 장비 |
PlainTextProtocolAdapter |
DelimiterMessageFrameCodec |
| 구분자 없이 단순 문자열을 보내는 터미널 툴 또는 장비 | PlainTextProtocolAdapter |
RawAvailableMessageFrameCodec |
| 줄 끝 문자를 포함한 JSON 텍스트를 보내는 장비 | RawJsonProtocolAdapter |
DelimiterMessageFrameCodec |
| 고정 바이너리 프로토콜 장비 | Custom protocol adapter | Custom frame codec |
RawAvailableMessageFrameCodec 지원
RawAvailableMessageFrameCodec은 이 패키지가 아니라 Dreamine.Communication.Core에 정의되어 있습니다. 하지만 SerialPortTransport는 IMessageFrameCodec을 받기 때문에 그대로 사용할 수 있습니다.
이 모드는 장비나 터미널 툴이 다음과 같은 단순 값을 보낼 때 유용합니다.
test1
그리고 아래 요소가 없을 때 사용할 수 있습니다.
- Dreamine envelope
- length prefix
CRLFLF
시리얼 Raw 문자열 설정 예시:
var transport = new SerialPortTransport(
options,
new PlainTextProtocolAdapter(
Encoding.UTF8,
"serial.raw.available",
"Serial.RawAvailable"),
new RawAvailableMessageFrameCodec());
중요한 시리얼 스트림 주의사항
RS232 시리얼 통신은 바이트 스트림입니다. RawAvailableMessageFrameCodec은 한 번의 스트림 읽기에서 반환된 바이트를 하나의 메시지로 처리합니다.
수동 테스트에는 편하지만, 엄격한 메시지 경계 규칙은 아닙니다. 장비 타이밍, 버퍼 동작, Baud rate에 따라 여러 번 보낸 데이터가 합쳐지거나, 한 번 보낸 데이터가 나누어질 수 있습니다.
실제 양산 장비 프로토콜에서는 아래 방식 중 하나를 권장합니다.
CR,LF,CRLF같은 구분자 사용- 길이 prefix 기반 프레임 사용
- 고정 길이 바이너리 프레임 사용
- 장비 전용 Custom
IMessageFrameCodec사용
RawAvailableMessageFrameCodec은 호환성, 단순 툴, 디버깅, 명시적 프레이밍 규칙이 없는 장비 대응 용도로 사용하는 것이 맞습니다.
연결 상태
SerialPortTransport는 공통 ConnectionState enum으로 상태를 보고합니다.
| 상태 | 의미 |
|---|---|
Disconnected |
COM 포트가 닫혀 있습니다. |
Connecting |
Transport가 COM 포트를 여는 중입니다. |
Connected |
COM 포트가 열려 있고 수신 루프가 동작 중입니다. |
Disconnecting |
Transport가 COM 포트를 닫는 중입니다. |
Faulted |
시리얼 연결 또는 수신 루프에서 오류가 발생했습니다. |
오류 처리
Transport는 포트를 열기 전에 옵션을 검증합니다. 잘못된 설정은 초기에 실패합니다.
예시:
- 비어 있는
PortName BaudRate <= 0DataBits가 5~8 범위를 벗어남- 0 이하의 timeout 값
- 0 이하의 buffer size
런타임 포트 오류가 발생하면 Transport 상태가 Faulted로 변경될 수 있습니다. 애플리케이션 수준 복구는 Transport를 닫고, 포트 상태를 수정한 뒤 다시 연결하는 방식이 맞습니다.
사용 예시: 라인 기반 텍스트 장비
using System.IO.Ports;
using System.Text;
using Dreamine.Communication.Core.Framing;
using Dreamine.Communication.Core.Protocols;
using Dreamine.Communication.Serial.Options;
using Dreamine.Communication.Serial.Ports;
var options = new SerialPortTransportOptions
{
PortName = "COM3",
BaudRate = 9600,
DataBits = 8,
Parity = Parity.None,
StopBits = StopBits.One,
Handshake = Handshake.None
};
var transport = new SerialPortTransport(
options,
new PlainTextProtocolAdapter(
Encoding.UTF8,
"serial.plaintext",
"Serial.PlainText"),
new DelimiterMessageFrameCodec(
"\r\n",
Encoding.UTF8,
1024 * 1024));
transport.MessageReceived += (_, message) =>
{
var text = Encoding.UTF8.GetString(message.Payload);
Console.WriteLine($"RX: {text}");
};
await transport.ConnectAsync();
설계 원칙
- 구체 시리얼 구현체를 상위 레이어와 분리합니다.
Dreamine.Communication.Abstractions의 계약에 의존합니다.Core의 프로토콜 어댑터와 프레임 코덱을 재사용합니다.- 시리얼 포트 제어와 Payload 해석을 분리합니다.
- 패키지 책임을 작고 명확하게 유지합니다.
- 단방향 의존성 흐름을 유지합니다.
- 향후 시리얼 장비 및 Custom codec을 추가해도 애플리케이션 로직을 변경하지 않도록 합니다.
의존성
Dreamine.Communication.AbstractionsDreamine.Communication.CoreSystem.IO.Ports
대상 프레임워크
net8.0
관련 패키지
Dreamine.Communication.AbstractionsDreamine.Communication.CoreDreamine.Communication.SocketsDreamine.Communication.SerialDreamine.Communication.RabbitMQDreamine.Communication.FullKitDreamine.Communication.Wpf
라이선스
이 프로젝트는 MIT 라이선스를 따릅니다.
구조 다이어그램
classDiagram
class SerialPortClient {
-SerialPort _port
+PortName string
+BaudRate int
+DataBits int
+Parity Parity
+StopBits StopBits
+IsConnected bool
+ConnectAsync() Task
+DisconnectAsync() Task
+SendAsync(byte[]) Task
+DataReceived event
}
class SerialPortScanner {
<<static>>
+GetAvailablePorts() string[]
+GetPortInfo(string) SerialPortInfo
}
class SerialPortInfo {
+string PortName
+string Description
+string Manufacturer
+bool IsAvailable
}
class ModbusRtuClient {
-SerialPortClient _serial
+ReadHoldingRegisters(int, int) Task~short[]~
+WriteSingleRegister(int, short) Task
+ReadCoils(int, int) Task~bool[]~
}
class ConnectionClientBase {
<<abstract>>
}
ConnectionClientBase <|-- SerialPortClient
ModbusRtuClient --> SerialPortClient
SerialPortScanner --> SerialPortInfoAPI 문서
타입
\if KO 시리얼 포트 연결, 송수신 또는 설정 과정에서 발생한 통신 오류를 나타냅니다. \endif \if EN Represents a communication error raised during serial-port connection, transfer, or configuration. \endif
\if KO 기본 스트림의 비동기 읽기와 쓰기를 캡슐화합니다. \endif \if EN Encapsulates asynchronous reads and writes on a base stream. \endif
\if KO .NET 와 구성 가능한 프레임·프로토콜 어댑터를 사용하는 RS-232 메시지 전송 계층입니다. \endif \if EN Provides RS-232 message transport using .NET with configurable framing and protocol adapters. \endif
\if KO RS-232 시리얼 포트의 회선, 제한 시간 및 버퍼 설정을 구성합니다. \endif \if EN Configures line, timeout, and buffer settings for an RS-232 serial port. \endif
SerialCommunicationException
\if KO 기본 메시지로 새 시리얼 통신 예외를 초기화합니다. \endif \if EN Initializes a new serial communication exception with the default message. \endif
\if KO 지정한 오류 메시지로 새 시리얼 통신 예외를 초기화합니다. \endif \if EN Initializes a new serial communication exception with the specified message. \endif
message— \if KO 오류 원인을 설명하는 메시지입니다. \endif \if EN The message describing the error. \endif\if KO 지정한 오류 메시지와 내부 예외로 새 시리얼 통신 예외를 초기화합니다. \endif \if EN Initializes a new serial communication exception with a message and inner exception. \endif
message— \if KO 오류 원인을 설명하는 메시지입니다. \endif \if EN The message describing the error. \endifinnerException— \if KO 현재 오류의 원인이 된 예외입니다. \endif \if EN The exception that caused the current error. \endifSerialPortStreamAdapter
\if KO 지정한 시리얼 포트로 스트림 어댑터를 초기화합니다. \endif \if EN Initializes the stream adapter with the specified serial port. \endif
serialPort— \if KO 기본 스트림을 제공할 시리얼 포트입니다. \endif \if EN The serial port that provides the base stream. \endif\if KO 기본 스트림에서 제공된 메모리 버퍼로 데이터를 비동기 읽습니다. \endif \if EN Asynchronously reads data from the base stream into the supplied memory buffer. \endif
buffer— \if KO 읽은 데이터를 저장할 버퍼입니다. \endif \if EN The buffer that receives the data. \endifcancellationToken— \if KO 읽기 취소 요청을 감시하는 토큰입니다. \endif \if EN A token used to observe read cancellation. \endif반환: \if KO 읽은 바이트 수를 결과로 제공하는 작업입니다. \endif \if EN A task whose result is the number of bytes read. \endif
\if KO 버퍼를 기본 스트림에 비동기 기록하고 즉시 플러시합니다. \endif \if EN Asynchronously writes a buffer to the base stream and flushes it. \endif
buffer— \if KO 기록할 데이터입니다. \endif \if EN The data to write. \endifcancellationToken— \if KO 쓰기와 플러시 취소 요청을 감시하는 토큰입니다. \endif \if EN A token used to observe write and flush cancellation. \endif반환: \if KO 비동기 쓰기 및 플러시 작업입니다. \endif \if EN A task representing the asynchronous write and flush. \endif
\if KO 시리얼 포트의 기본 입출력 스트림을 가져옵니다. \endif \if EN Gets the serial port's underlying input/output stream. \endif
\if KO serial Port 값을 보관합니다. \endif \if EN Stores the serial port value. \endif
SerialPortTransport
\if KO 기본 Dreamine JSON 프로토콜과 길이 접두사 프레임으로 전송 계층을 초기화합니다. \endif \if EN Initializes the transport with the default Dreamine JSON protocol and length-prefixed framing. \endif
options— \if KO 시리얼 회선 및 버퍼 설정입니다. \endif \if EN The serial line and buffer options. \endif\if KO 시리얼 설정과 사용자 지정 프로토콜 및 프레임 코덱으로 전송 계층을 초기화합니다. \endif \if EN Initializes the transport with serial options and custom protocol and frame codecs. \endif
options— \if KO 시리얼 회선 및 버퍼 설정입니다. \endif \if EN The serial line and buffer options. \endifprotocolAdapter— \if KO 메시지와 외부 페이로드를 변환할 어댑터입니다. \endif \if EN The adapter that converts messages and external payloads. \endifframeCodec— \if KO 스트림의 메시지 경계를 처리할 코덱입니다. \endif \if EN The codec that handles message boundaries in the stream. \endif\if KO 예외를 외부로 전파하지 않고 시리얼 포트를 닫고 해제합니다. \endif \if EN Closes and disposes the serial port without propagating cleanup exceptions. \endif
\if KO 시리얼 포트를 열고 백그라운드 수신 루프를 시작합니다. 포트 열기 자체는 동기 API입니다. \endif \if EN Opens the serial port and starts the background receive loop; opening the port itself is synchronous. \endif
cancellationToken— \if KO 포트를 열기 전 취소 요청을 확인하는 토큰입니다. \endif \if EN A token checked for cancellation before the port is opened. \endif반환: \if KO 연결 시작 작업입니다. \endif \if EN A task representing connection startup. \endif
\if KO 수신 루프를 중지하고 시리얼 포트와 관련 리소스를 닫습니다. \endif \if EN Stops the receive loop and closes the serial port and related resources. \endif
cancellationToken— \if KO 정리 후 연결 해제 취소 여부를 확인하는 토큰입니다. \endif \if EN A token checked for cancellation after cleanup. \endif반환: \if KO 비동기 연결 해제 작업입니다. \endif \if EN A task representing asynchronous disconnection. \endif
\if KO 시리얼 포트 연결과 수신 루프 리소스를 비동기적으로 해제합니다. \endif \if EN Asynchronously releases the serial connection and receive-loop resources. \endif \if KO 비동기 리소스 해제 작업입니다. \endif \if EN A value task representing asynchronous disposal. \endif
\if KO 시리얼 스트림에서 프레임을 계속 읽어 메시지로 디코딩하고 수신 이벤트를 발생시킵니다. \endif \if EN Continuously reads frames from the serial stream, decodes messages, and raises receive events. \endif
cancellationToken— \if KO 수신 루프 종료를 요청하는 토큰입니다. \endif \if EN A token used to request receive-loop termination. \endif반환: \if KO 백그라운드 수신 루프 작업입니다. \endif \if EN A task representing the background receive loop. \endif
\if KO 메시지를 외부 프로토콜과 프레임 형식으로 인코딩해 시리얼 포트로 전송합니다. \endif \if EN Encodes a message using the external protocol and frame format and sends it over the serial port. \endif
message— \if KO 전송할 메시지입니다. \endif \if EN The message to send. \endifcancellationToken— \if KO 프레임 쓰기 취소 요청을 감시하는 토큰입니다. \endif \if EN A token used to observe frame-write cancellation. \endif반환: \if KO 비동기 메시지 전송 작업입니다. \endif \if EN A task representing asynchronous message transmission. \endif
\if KO 원자적 연산으로 현재 연결 상태를 설정합니다. \endif \if EN Sets the current connection state using an atomic operation. \endif
state— \if KO 저장할 새 연결 상태입니다. \endif \if EN The new connection state to store. \endif\if KO 포트 이름, 회선 속성, 제한 시간 및 버퍼 크기의 유효성을 검사합니다. \endif \if EN Validates the port name, line settings, timeouts, and buffer sizes. \endif
options— \if KO 검증할 시리얼 포트 설정입니다. \endif \if EN The serial-port options to validate. \endif\if KO 시리얼 전송 방식을 가져옵니다. \endif \if EN Gets the serial transport kind. \endif
\if KO 스레드 안전하게 현재 시리얼 연결 상태를 가져옵니다. \endif \if EN Gets the current serial connection state in a thread-safe manner. \endif
\if KO frame Codec 값을 보관합니다. \endif \if EN Stores the frame codec value. \endif
\if KO options 값을 보관합니다. \endif \if EN Stores the options value. \endif
\if KO protocol Adapter 값을 보관합니다. \endif \if EN Stores the protocol adapter value. \endif
\if KO receive Loop Cts 값을 보관합니다. \endif \if EN Stores the receive loop cts value. \endif
\if KO receive Loop Task 값을 보관합니다. \endif \if EN Stores the receive loop task value. \endif
\if KO serial Port 값을 보관합니다. \endif \if EN Stores the serial port value. \endif
\if KO state 값을 보관합니다. \endif \if EN Stores the state value. \endif
\if KO 완전한 프레임을 메시지로 디코딩했을 때 발생합니다. \endif \if EN Occurs when a complete frame has been decoded into a message. \endif
SerialPortTransportOptions
\if KO 회선 통신 속도인 보드율을 가져오거나 설정합니다. \endif \if EN Gets or sets the line baud rate. \endif
\if KO 문자당 데이터 비트 수를 가져오거나 설정합니다. \endif \if EN Gets or sets the number of data bits per character. \endif
\if KO 흐름 제어 Handshake 방식을 가져오거나 설정합니다. \endif \if EN Gets or sets the flow-control handshake mode. \endif
\if KO 패리티 검사 방식을 가져오거나 설정합니다. \endif \if EN Gets or sets the parity-checking scheme. \endif
\if KO 열 시리얼 포트 이름을 가져오거나 설정합니다. \endif \if EN Gets or sets the serial port name to open. \endif
\if KO 수신 버퍼 크기(바이트)를 가져오거나 설정합니다. \endif \if EN Gets or sets the receive buffer size in bytes. \endif
\if KO 읽기 제한 시간(밀리초)을 가져오거나 설정합니다. 0은 즉시 반환하고 -1은 무한 대기입니다. \endif \if EN Gets or sets the read timeout in milliseconds; zero returns immediately and -1 waits indefinitely. \endif
\if KO 정지 비트 설정을 가져오거나 설정합니다. \endif \if EN Gets or sets the stop-bit setting. \endif
\if KO 송신 버퍼 크기(바이트)를 가져오거나 설정합니다. \endif \if EN Gets or sets the send buffer size in bytes. \endif
\if KO 쓰기 제한 시간(밀리초)을 가져오거나 설정합니다. 0은 즉시 반환하고 -1은 무한 대기입니다. \endif \if EN Gets or sets the write timeout in milliseconds; zero returns immediately and -1 waits indefinitely. \endif