connect → startReader → send → (프레임 수신 콜백) → 모드 기준으로 계속/종료
데이터는 STX(0x02) ~ ETX(0x03) + LRC(1byte) 프레이밍
두 가지 수신 기준을 모두 지원:
LRC(XOR)은 STX 포함/제외 두 방식 모두 검증(벤더 사양 혼재 대응)
매 프레임마다 ACK(0x06) 자동 전송(기존 Kovan 리더가 프레임마다 ACK 요구하는 패턴 대응)
ReceiveMode로 종료 조건을 제어:
단건 조회는 StopAfterFirstFrame, 가맹점 다운로드는 StopAfterEndFrame(서버가 *E…로 끝내줌), 스트리밍은 Continuous
while (isReaderRunning && isOpen()) {
// 1바이트 블로킹 읽기 (IO 디스패처)
localInputStream.read(singleByteBuffer)
// STX를 기다렸다가, STX가 오면 프레임 버퍼 초기화+수집 시작
// 'I' 포맷이면 3..6 ASCII 길이(예: "0123")를 파싱해 STX..ETX 길이를 알 수 있음
// 길이를 알면 그 길이만큼 딱 모은 뒤 LRC 1바이트를 추가로 읽어서 "완성 프레임"
// 길이를 못 알면, ETX를 만나면 LRC 1바이트 추가로 읽어서 "완성 프레임"
}
길이 헤더 파싱
→ specFormatByte == ‘I’ 이고 바이트가 7개 이상 모이면 collected[3..6]을 ASCII 정수로 변환 - STX..ETX 길이로 해석. 그 길이만큼 모이면 LRC 1byte를 추가로 읽어 완성
ETX 기반
→ 길이를 못 얻은 경우 ETX 도착 시 LRC 1Byte를 추가로 읽어 완성
val etxIndex = fullFrameBytes.indexOf(ETX)
val readLrc = fullFrameBytes.last() & 0xFF
val lrcInclStx = xor(fullFrame, from=0, to=etxIndex) // STX 포함
val lrcExclStx = xor(fullFrame, from=1, to=etxIndex) // STX 제외
val valid = readLrc == lrcInclStx || readLrc == lrcExclStx
장비마다 LRC 정의가 STX 포함/제외 혼용되는 경우가 있어 둘다 허용
유효/무효와 관계 없이 (프로토콜 요구에 맞춰) ACK(0x06)를 전송하도록 구현되어 있음
(무효 LRC 시에도 계속 받을지 여부는 ReceiveMode에 따름)
private val supervisorJob = SupervisorJob()
private val ioCoroutineScope = CoroutineScope(Dispatchers.IO + supervisorJob)
→ 뭐가 만들어지나?
요즘은 계속해서 개발 진행을 하고 있는데 재미있다! 하지만 가끔 블로그를 써야하는데 주제를 뭐로 할 지 항상 고민인 것 같다. 그래서 앞으로는 개발하면서 궁금하거나 개발한 건에 대해서 작성을 해볼 예정이다!!