iconDreamine
← 목록

Dreamine.Communication.Serial

stablev1.0.2

RS-232/485 시리얼 통신 — 자동 재연결, 프레임 파싱 지원.

#communication#dreamine#messaging#rs232#serial#serialport#transport
TFM net8.0Package Dreamine.Communication.Serial참조 Dreamine.Communication.Abstractions, Dreamine.Communication.Core

Dreamine.Communication.Serial

Dreamine.Communication.Serial는 Dreamine Communication 계열 패키지의 일부입니다.

이 패키지는 RS232 및 SerialPort 기반 전송 구현체를 제공하며, 시리얼 장비 관련 책임을 상위 애플리케이션 계층과 분리합니다.

➡️ English Version

설명

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에 정의되어 있습니다. 하지만 SerialPortTransportIMessageFrameCodec을 받기 때문에 그대로 사용할 수 있습니다.

이 모드는 장비나 터미널 툴이 다음과 같은 단순 값을 보낼 때 유용합니다.

test1

그리고 아래 요소가 없을 때 사용할 수 있습니다.

  • Dreamine envelope
  • length prefix
  • CRLF
  • LF

시리얼 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 <= 0
  • DataBits가 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.Abstractions
  • Dreamine.Communication.Core
  • System.IO.Ports

대상 프레임워크

net8.0

관련 패키지

  • Dreamine.Communication.Abstractions
  • Dreamine.Communication.Core
  • Dreamine.Communication.Sockets
  • Dreamine.Communication.Serial
  • Dreamine.Communication.RabbitMQ
  • Dreamine.Communication.FullKit
  • Dreamine.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 --> SerialPortInfo

API 문서

타입

SerialCommunicationException

\if KO 시리얼 포트 연결, 송수신 또는 설정 과정에서 발생한 통신 오류를 나타냅니다. \endif \if EN Represents a communication error raised during serial-port connection, transfer, or configuration. \endif

SerialPortStreamAdapter

\if KO 기본 스트림의 비동기 읽기와 쓰기를 캡슐화합니다. \endif \if EN Encapsulates asynchronous reads and writes on a base stream. \endif

SerialPortTransport

\if KO .NET 와 구성 가능한 프레임·프로토콜 어댑터를 사용하는 RS-232 메시지 전송 계층입니다. \endif \if EN Provides RS-232 message transport using .NET with configurable framing and protocol adapters. \endif

SerialPortTransportOptions

\if KO RS-232 시리얼 포트의 회선, 제한 시간 및 버퍼 설정을 구성합니다. \endif \if EN Configures line, timeout, and buffer settings for an RS-232 serial port. \endif

SerialCommunicationException

#ctor Method

\if KO 기본 메시지로 새 시리얼 통신 예외를 초기화합니다. \endif \if EN Initializes a new serial communication exception with the default message. \endif

#ctor Method

\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
#ctor Method

\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. \endif
innerException— \if KO 현재 오류의 원인이 된 예외입니다. \endif \if EN The exception that caused the current error. \endif

SerialPortStreamAdapter

#ctor Method

\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
ReadAsync Method

\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. \endif
cancellationToken— \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

WriteAsync Method

\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. \endif
cancellationToken— \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

BaseStream Property

\if KO 시리얼 포트의 기본 입출력 스트림을 가져옵니다. \endif \if EN Gets the serial port's underlying input/output stream. \endif

_serialPort Field

\if KO serial Port 값을 보관합니다. \endif \if EN Stores the serial port value. \endif

SerialPortTransport

#ctor Method

\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
#ctor Method

\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. \endif
protocolAdapter— \if KO 메시지와 외부 페이로드를 변환할 어댑터입니다. \endif \if EN The adapter that converts messages and external payloads. \endif
frameCodec— \if KO 스트림의 메시지 경계를 처리할 코덱입니다. \endif \if EN The codec that handles message boundaries in the stream. \endif
CleanupSerialPort Method

\if KO 예외를 외부로 전파하지 않고 시리얼 포트를 닫고 해제합니다. \endif \if EN Closes and disposes the serial port without propagating cleanup exceptions. \endif

ConnectAsync Method

\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

DisconnectAsync Method

\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

DisposeAsync Method

\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

ReceiveLoopAsync Method

\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

SendAsync Method

\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. \endif
cancellationToken— \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

SetState Method

\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
ValidateOptions Method

\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
Kind Property

\if KO 시리얼 전송 방식을 가져옵니다. \endif \if EN Gets the serial transport kind. \endif

State Property

\if KO 스레드 안전하게 현재 시리얼 연결 상태를 가져옵니다. \endif \if EN Gets the current serial connection state in a thread-safe manner. \endif

_frameCodec Field

\if KO frame Codec 값을 보관합니다. \endif \if EN Stores the frame codec value. \endif

_options Field

\if KO options 값을 보관합니다. \endif \if EN Stores the options value. \endif

_protocolAdapter Field

\if KO protocol Adapter 값을 보관합니다. \endif \if EN Stores the protocol adapter value. \endif

_receiveLoopCts Field

\if KO receive Loop Cts 값을 보관합니다. \endif \if EN Stores the receive loop cts value. \endif

_receiveLoopTask Field

\if KO receive Loop Task 값을 보관합니다. \endif \if EN Stores the receive loop task value. \endif

_serialPort Field

\if KO serial Port 값을 보관합니다. \endif \if EN Stores the serial port value. \endif

_state Field

\if KO state 값을 보관합니다. \endif \if EN Stores the state value. \endif

MessageReceived Event

\if KO 완전한 프레임을 메시지로 디코딩했을 때 발생합니다. \endif \if EN Occurs when a complete frame has been decoded into a message. \endif

SerialPortTransportOptions

BaudRate Property

\if KO 회선 통신 속도인 보드율을 가져오거나 설정합니다. \endif \if EN Gets or sets the line baud rate. \endif

DataBits Property

\if KO 문자당 데이터 비트 수를 가져오거나 설정합니다. \endif \if EN Gets or sets the number of data bits per character. \endif

Handshake Property

\if KO 흐름 제어 Handshake 방식을 가져오거나 설정합니다. \endif \if EN Gets or sets the flow-control handshake mode. \endif

Parity Property

\if KO 패리티 검사 방식을 가져오거나 설정합니다. \endif \if EN Gets or sets the parity-checking scheme. \endif

PortName Property

\if KO 열 시리얼 포트 이름을 가져오거나 설정합니다. \endif \if EN Gets or sets the serial port name to open. \endif

ReadBufferSize Property

\if KO 수신 버퍼 크기(바이트)를 가져오거나 설정합니다. \endif \if EN Gets or sets the receive buffer size in bytes. \endif

ReadTimeoutMs Property

\if KO 읽기 제한 시간(밀리초)을 가져오거나 설정합니다. 0은 즉시 반환하고 -1은 무한 대기입니다. \endif \if EN Gets or sets the read timeout in milliseconds; zero returns immediately and -1 waits indefinitely. \endif

StopBits Property

\if KO 정지 비트 설정을 가져오거나 설정합니다. \endif \if EN Gets or sets the stop-bit setting. \endif

WriteBufferSize Property

\if KO 송신 버퍼 크기(바이트)를 가져오거나 설정합니다. \endif \if EN Gets or sets the send buffer size in bytes. \endif

WriteTimeoutMs Property

\if KO 쓰기 제한 시간(밀리초)을 가져오거나 설정합니다. 0은 즉시 반환하고 -1은 무한 대기입니다. \endif \if EN Gets or sets the write timeout in milliseconds; zero returns immediately and -1 waits indefinitely. \endif