UbiAnyCard Ver2 2.0
KICC ED785 단말기 연동 TCP 서버
로딩중...
검색중...
일치하는것 없음
ProtocolAdapter 클래스 참조final

#include <ProtocolAdapter.h>

ProtocolAdapter에 대한 협력 다이어그램:
Collaboration graph

클래스

struct  PendingTx
struct  TxResponse

Public 멤버 함수

std::string onCommand (const std::string &cmdline, const std::weak_ptr< TcpServer::Session > &session)
void onEd785Response (const ED785Worker::Response &resp)
 ProtocolAdapter (ED785Worker &worker, const AppSettings &settings)

Private 멤버 함수

std::string sendAndWait (int gcd, int jcd, const std::string &asciiPayload, uint32_t timeoutMs, const std::weak_ptr< TcpServer::Session > &session)

정적 Private 멤버 함수

static std::string bytesToUtf8String (const std::vector< byte > &buf, size_t len)
static void ParseAndAppendElectronicMoney (std::vector< std::pair< std::string, std::string > > &kv)
static const char * rcToText (int rcd)
static std::pair< std::string, std::string > splitOnce (const std::string &s, char delim)
static std::string trim (const std::string &s)

Private 속성

std::atomic< bool > m_deviceReady {true}
std::string m_lastInitializationError
std::mutex m_pendingMutex
std::map< uint64_t, std::unique_ptr< PendingTx > > m_pendingTxs
AppSettings m_settings
std::mutex m_stateMutex
std::atomic< uint64_t > m_txSeq {0}
ED785Workerm_worker

생성자 & 소멸자 문서화

◆ ProtocolAdapter()

ProtocolAdapter::ProtocolAdapter ( ED785Worker & worker,
const AppSettings & settings )
117 : m_worker(worker), m_settings(settings)
118{
119}
AppSettings m_settings
Definition ProtocolAdapter.h:64
ED785Worker & m_worker
Definition ProtocolAdapter.h:63

다음을 참조함 : m_settings, m_worker.

멤버 함수 문서화

◆ bytesToUtf8String()

std::string ProtocolAdapter::bytesToUtf8String ( const std::vector< byte > & buf,
size_t len )
staticprivate
158{
159 if (len > buf.size())
160 len = buf.size();
161 std::string euckr(reinterpret_cast<const char *>(buf.data()), reinterpret_cast<const char *>(buf.data()) + len);
162 return ConvertEucKrToUtf8(euckr);
163}
static std::string ConvertEucKrToUtf8(const std::string &euckr)
Definition ProtocolAdapter.cpp:5

다음을 참조함 : ConvertEucKrToUtf8().

다음에 의해서 참조됨 : onEd785Response().

이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:
이 함수를 호출하는 함수들에 대한 그래프입니다.:

◆ onCommand()

std::string ProtocolAdapter::onCommand ( const std::string & cmdline,
const std::weak_ptr< TcpServer::Session > & session )
260{
261 const std::string line = trim(cmdline);
262 if (line.empty())
263 return "ERR=EMPTY_COMMAND";
264
265 Logger::info(L"명령 수신: " + std::wstring(line.begin(), line.end()));
266
267 auto [cmd, arg] = splitOnce(line, ' ');
268 std::transform(cmd.begin(), cmd.end(), cmd.begin(), [](unsigned char c)
269 { return (char)std::toupper(c); });
270
271 if (!m_deviceReady.load(std::memory_order_acquire) && cmd != "PING")
272 {
273 std::string detail;
274 {
275 std::lock_guard<std::mutex> stateLock(m_stateMutex);
277 }
278
279 if (!detail.empty())
280 return "ERR=DEVICE_NOT_READY;" + detail;
281
282 return "ERR=DEVICE_NOT_READY";
283 }
284
285 if (cmd == "PING")
286 return "PONG";
287
288 if (cmd == "INIT")
289 {
290 // 단말기 초기화 시 기존 대기 중인 모든 트랜잭션 강제 실패 처리
291 {
292 std::scoped_lock lock(m_pendingMutex);
293 if (!m_pendingTxs.empty())
294 {
295 Logger::warn(std::wstring(L"INIT 명령: 대기 중인 ") + std::to_wstring(m_pendingTxs.size()) + L"개 트랜잭션 강제 취소");
296
297 for (auto &pair : m_pendingTxs)
298 {
299 TxResponse cancelResp;
300 cancelResp.rcd = 0xFF; // RC_FAILURE
301 cancelResp.data = "CANCELED";
302
303 try
304 {
305 pair.second->promise.set_value(std::move(cancelResp));
306 }
307 catch (const std::exception &)
308 {
309 // promise가 이미 설정된 경우 무시
310 }
311 }
312
313 m_pendingTxs.clear();
314 }
315 }
316
317 // 단말기 초기화 (0x14, 0x01) - Fire-and-Forget (응답 없음)
318 ED785Worker::Request req;
319 req.cmd = 0xFB;
320 req.gcd = 0x14;
321 req.jcd = 0x01;
322 req.payload.clear();
323
324 if (!m_worker.enqueue(req))
325 {
326 Logger::error(L"INIT 명령 전송 실패");
327 return "ERR=ENQUEUE_FAILED";
328 }
329
330 Logger::info(L"INIT 명령 전송 완료 (응답 대기 안 함)");
331
332 // INIT은 응답이 없으므로 즉시 성공 반환
333 // 단말정보가 필요하면 TERMINFO 명령을 별도로 호출
334 return "INIT_OK";
335 }
336
337 if (cmd == "TERMINFO")
338 return sendAndWait(0x14, 0x02, "", 10000, session);
339
340 if (cmd == "DISPLAY")
341 return sendAndWait(0x14, 0x03, arg, 10000, session);
342
343 if (cmd == "APPROVE" || cmd == "CANCEL")
344 {
345 std::string kvs = arg;
346 if (cmd == "CANCEL")
347 {
348 if (kvs.find("S01=") == std::string::npos)
349 {
350 if (!kvs.empty() && kvs.back() != ';')
351 kvs.push_back(';');
352 kvs += "S01=D4;";
353 }
354 auto tmp = ParseKvOrdered(kvs);
355 if (!HasKv(tmp, "S12") || !HasKv(tmp, "S13"))
356 {
357 Logger::warn(L"취소 요청 거부: 원승인번호/일자 누락");
358 return "ERR=MISSING_APPROVAL_INFO";
359 }
360 }
361
362 // S23 자동 주입
363 if (kvs.find("S23=") == std::string::npos)
364 {
365 auto now = std::chrono::system_clock::now();
366 std::time_t t = std::chrono::system_clock::to_time_t(now);
367 std::tm tm{};
368 localtime_s(&tm, &t);
369 char ts[16] = {0};
370 std::snprintf(ts, sizeof(ts), "%02d%02d%02d%02d%02d%02d",
371 (tm.tm_year + 1900) % 100, tm.tm_mon + 1, tm.tm_mday,
372 tm.tm_hour, tm.tm_min, tm.tm_sec);
373 uint64_t seq = m_txSeq.load() + 1;
374 char seqbuf[5] = {0};
375 std::snprintf(seqbuf, sizeof(seqbuf), "%04llu", (unsigned long long)(seq % 10000));
376 std::ostringstream s23;
377 s23 << "POS" << ts << seqbuf;
378 if (!kvs.empty() && kvs.back() != ';')
379 kvs.push_back(';');
380 kvs += "S23=";
381 kvs += s23.str();
382 kvs.push_back(';');
383 }
384
385 // S23 길이 제한 (20 바이트)
386 {
387 auto kvList = ParseKvOrdered(kvs);
388 bool changed = false;
389 for (auto &p : kvList)
390 {
391 if (_stricmp(p.first.c_str(), "S23") == 0 && p.second.size() > 20)
392 {
393 p.second = p.second.substr(0, 20);
394 changed = true;
395 }
396 }
397 if (changed)
398 {
399 kvs = JoinKvOrdered(kvList);
400 if (!kvs.empty() && kvs.back() != ';')
401 kvs.push_back(';');
402 }
403 }
404
405 return sendAndWait(0x14, 0x04, kvs, 60000, session);
406 }
407
408 if (cmd == "PRINT")
409 return sendAndWait(0x14, 0x05, arg, 10000, session);
410
411 if (cmd == "READ_SNO")
412 {
413 std::string opt = trim(arg);
414 std::string payload = opt == "R" ? "R" : "";
415 return sendAndWait(0x01, 0x06, payload, 10000, session);
416 }
417
418 return "ERR=UNKNOWN_COMMAND";
419}
static std::string JoinKvOrdered(const std::vector< std::pair< std::string, std::string > > &kvs)
Definition ProtocolAdapter.cpp:72
static std::vector< std::pair< std::string, std::string > > ParseKvOrdered(const std::string &s)
Definition ProtocolAdapter.cpp:31
static bool HasKv(const std::vector< std::pair< std::string, std::string > > &kvs, const std::string &key)
Definition ProtocolAdapter.cpp:64
std::string sendAndWait(int gcd, int jcd, const std::string &asciiPayload, uint32_t timeoutMs, const std::weak_ptr< TcpServer::Session > &session)
Definition ProtocolAdapter.cpp:421
std::atomic< bool > m_deviceReady
Definition ProtocolAdapter.h:73
std::atomic< uint64_t > m_txSeq
Definition ProtocolAdapter.h:71
static std::pair< std::string, std::string > splitOnce(const std::string &s, char delim)
Definition ProtocolAdapter.cpp:121
static std::string trim(const std::string &s)
Definition ProtocolAdapter.cpp:129
std::string m_lastInitializationError
Definition ProtocolAdapter.h:75
std::map< uint64_t, std::unique_ptr< PendingTx > > m_pendingTxs
Definition ProtocolAdapter.h:68
std::mutex m_pendingMutex
Definition ProtocolAdapter.h:67
std::mutex m_stateMutex
Definition ProtocolAdapter.h:74
int jcd
Definition ED785Worker.h:25
std::vector< byte > payload
Definition ED785Worker.h:26
int gcd
Definition ED785Worker.h:24
int cmd
Definition ED785Worker.h:23
Definition ProtocolAdapter.h:25
int rcd
Definition ProtocolAdapter.h:26

다음을 참조함 : ED785Worker::Request::cmd, ProtocolAdapter::TxResponse::data, ED785Worker::Request::gcd, HasKv(), ED785Worker::Request::jcd, JoinKvOrdered(), m_deviceReady, m_lastInitializationError, m_pendingMutex, m_pendingTxs, m_stateMutex, m_txSeq, m_worker, ParseKvOrdered(), ED785Worker::Request::payload, ProtocolAdapter::TxResponse::rcd, sendAndWait(), splitOnce(), trim().

다음에 의해서 참조됨 : wWinMain().

이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:
이 함수를 호출하는 함수들에 대한 그래프입니다.:

◆ onEd785Response()

void ProtocolAdapter::onEd785Response ( const ED785Worker::Response & resp)
516{
517 // EUC-KR → UTF-8 변환
518 std::string dataStr = bytesToUtf8String(resp.data, resp.dataLen);
519
523 {
524 std::string reason = dataStr.empty() ? "PORT_INIT_FAILED" : dataStr;
525 for (char &ch : reason)
526 {
527 if (ch == '\r' || ch == '\n' || ch == ';')
528 ch = ' ';
529 }
530
531 std::string code = "PORT_INIT_FAILED";
532 if (reason == "PORT_OPEN_FAILED")
533 code = "PORT_OPEN_FAILED";
534 else if (reason.rfind("DLL_LOAD_FAILED", 0) == 0)
535 code = "DLL_LOAD_FAILED";
536 else if (reason == "DUMMY_WINDOW_CREATE_FAILED")
537 code = "WINDOW_CREATE_FAILED";
538
539 const std::string detailPayload = "CODE=" + code + ";MESSAGE=" + reason;
540
541 m_deviceReady.store(false, std::memory_order_release);
542 {
543 std::lock_guard<std::mutex> stateLock(m_stateMutex);
544 m_lastInitializationError = detailPayload;
545 }
546
547 std::wstring wReason = Utf8ToWide(reason);
548 if (wReason.empty())
549 wReason.assign(reason.begin(), reason.end());
550 Logger::error(L"ED785 초기화 실패 이벤트 수신: " + wReason);
551
552 std::vector<std::weak_ptr<TcpServer::Session>> targets;
553 {
554 std::scoped_lock lock(m_pendingMutex);
555 targets.reserve(m_pendingTxs.size());
556 for (auto &pending : m_pendingTxs)
557 {
558 TxResponse cancelResp;
559 cancelResp.rcd = 0xFF;
560 cancelResp.data = "DEVICE_NOT_READY";
561 try
562 {
563 pending.second->promise.set_value(cancelResp);
564 }
565 catch (const std::exception &)
566 {
567 }
568 targets.emplace_back(pending.second->session);
569 }
570 m_pendingTxs.clear();
571 }
572
573 for (auto &target : targets)
574 {
575 if (auto session = target.lock())
576 {
577 std::ostringstream os;
578 os << "EVENT=DEVICE_ERROR;" << detailPayload;
579 session->sendLine(os.str());
580 }
581 }
582
583 return;
584 }
585
586 // 모든 ED785 응답 로깅
587 {
588 std::wstringstream ss;
589 ss << L"ED785 응답: cmd=0x" << std::hex << resp.cmd
590 << L" gcd=0x" << resp.gcd
591 << L" jcd=0x" << resp.jcd
592 << L" rcd=" << rcToText(resp.rcd)
593 << L" dataLen=" << std::dec << resp.dataLen;
594 Logger::debug(ss.str());
595 }
596
597 // 0xFB 0x14 0x09 : 단말기 상태 정보 (비동기 이벤트)
598 if (resp.gcd == 0x14 && resp.jcd == 0x09)
599 {
600 std::string statusCode;
601 if (resp.dataLen >= 2)
602 {
603 statusCode.assign(reinterpret_cast<const char *>(resp.data.data()), 2);
604 }
605 else if (!dataStr.empty())
606 {
607 statusCode = dataStr.substr(0, 2);
608 }
609
610 std::wstring wStatus(statusCode.begin(), statusCode.end());
611 std::wstring desc = L"알수없음";
612 if (statusCode == "01")
613 desc = L"신용카드 투입";
614 else if (statusCode == "02")
615 desc = L"서버로 접속 시도";
616 else if (statusCode == "03")
617 desc = L"FALLBACK 상황 발생";
618 else if (statusCode == "04")
619 desc = L"승인 불가능한 카드 인입";
620
621 const char *rcA = rcToText(resp.rcd);
622 std::wstring wRc(rcA, rcA + strlen(rcA));
623
624 std::wstringstream ss;
625 ss << L"단말기 상태 이벤트 수신 (0x14/0x09) - RC=" << wRc
626 << L", STATUS=" << wStatus << L" (" << desc << L")";
627 Logger::info(ss.str());
628
629 std::vector<std::pair<uint64_t, std::weak_ptr<TcpServer::Session>>> targets;
630 {
631 std::scoped_lock lock(m_pendingMutex);
632 targets.reserve(m_pendingTxs.size());
633 for (auto &pending : m_pendingTxs)
634 {
635 targets.emplace_back(pending.first, pending.second->session);
636 }
637 }
638
639 std::string detail = "UNKNOWN";
640 if (statusCode == "01")
641 detail = "CARD_INSERTED";
642 else if (statusCode == "02")
643 detail = "SERVER_CONNECTING";
644 else if (statusCode == "03")
645 detail = "FALLBACK";
646 else if (statusCode == "04")
647 detail = "CARD_REJECTED";
648
649 for (auto &target : targets)
650 {
651 if (auto session = target.second.lock())
652 {
653 std::ostringstream os;
654 os << "EVENT=TERMINAL_STATUS;CODE=" << statusCode << ";DETAIL=" << detail << ";TXID=" << target.first;
655 session->sendLine(os.str());
656 }
657 }
658
659 // 상태 이벤트는 요청 매칭 대상이 아니므로 여기서 처리 종료 (불필요한 경고 로그 방지)
660 return;
661 }
662
663 // 대기 중인 트랜잭션 중 매칭되는 것 찾기
664 std::scoped_lock lock(m_pendingMutex);
665
666 uint64_t matchedTx = 0;
667 PendingTx *matchedPending = nullptr;
668
669 for (auto &pair : m_pendingTxs)
670 {
671 PendingTx *pending = pair.second.get();
672 if (resp.cmd == 0xFB && resp.gcd == pending->expectGcd && resp.jcd == pending->expectJcd)
673 {
674 matchedTx = pair.first;
675 matchedPending = pending;
676 break;
677 }
678 }
679
680 if (matchedPending)
681 {
682 // 매칭된 트랜잭션에 응답 전달
683 TxResponse txResp;
684 txResp.rcd = resp.rcd;
685
686 if (resp.gcd == 0x01 && resp.jcd == 0x06 && resp.hexLen > 0)
687 txResp.data = BytesToHexUpper(resp.hex, resp.hexLen);
688 else
689 txResp.data = dataStr;
690
691 {
692 std::wstringstream ss;
693 ss << L"응답 매칭 성공: TX#" << matchedTx
694 << L" gcd=0x" << std::hex << resp.gcd
695 << L" jcd=0x" << resp.jcd
696 << L" RC=" << rcToText(resp.rcd);
697 Logger::info(ss.str());
698 }
699
700 matchedPending->promise.set_value(std::move(txResp));
701 }
702 else
703 {
704 // 매칭되는 대기 트랜잭션이 없음
705 std::wstringstream ss;
706 ss << L"응답 매칭 실패: 대기 중인 트랜잭션 없음 (현재 " << m_pendingTxs.size() << L"개 대기 중)"
707 << L" gcd=0x" << std::hex << resp.gcd
708 << L" jcd=0x" << resp.jcd;
709 Logger::warn(ss.str());
710 }
711}
static std::string BytesToHexUpper(const std::vector< byte > &buf, size_t len)
Definition ProtocolAdapter.cpp:86
static std::wstring Utf8ToWide(const std::string &text)
Definition ProtocolAdapter.cpp:101
static constexpr int ResponseJcdInitializationFailure
Definition ED785Worker.h:18
static constexpr int ResponseGcdInitializationFailure
Definition ED785Worker.h:17
static constexpr int ResponseCmdInternal
Definition ED785Worker.h:16
static const char * rcToText(int rcd)
Definition ProtocolAdapter.cpp:138
static std::string bytesToUtf8String(const std::vector< byte > &buf, size_t len)
Definition ProtocolAdapter.cpp:157
int gcd
Definition ED785Worker.h:33
size_t hexLen
Definition ED785Worker.h:39
int cmd
Definition ED785Worker.h:32
int rcd
Definition ED785Worker.h:35
int jcd
Definition ED785Worker.h:34
size_t dataLen
Definition ED785Worker.h:37
std::vector< byte > data
Definition ED785Worker.h:36
std::vector< byte > hex
Definition ED785Worker.h:38
Definition ProtocolAdapter.h:32

다음을 참조함 : BytesToHexUpper(), bytesToUtf8String(), ED785Worker::Response::cmd, ED785Worker::Response::data, ProtocolAdapter::TxResponse::data, ED785Worker::Response::dataLen, ProtocolAdapter::PendingTx::expectGcd, ProtocolAdapter::PendingTx::expectJcd, ED785Worker::Response::gcd, ED785Worker::Response::hex, ED785Worker::Response::hexLen, ED785Worker::Response::jcd, m_deviceReady, m_lastInitializationError, m_pendingMutex, m_pendingTxs, m_stateMutex, ProtocolAdapter::PendingTx::promise, ED785Worker::Response::rcd, ProtocolAdapter::TxResponse::rcd, rcToText(), ED785Worker::ResponseCmdInternal, ED785Worker::ResponseGcdInitializationFailure, ED785Worker::ResponseJcdInitializationFailure, Utf8ToWide().

이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:

◆ ParseAndAppendElectronicMoney()

void ProtocolAdapter::ParseAndAppendElectronicMoney ( std::vector< std::pair< std::string, std::string > > & kv)
staticprivate
166{
167 // R32: 전자화폐 결과전문, R33: 전자화폐 구분
168 if (!HasKv(kv, "R32"))
169 return;
170
171 std::string r32 = GetKv(kv, "R32");
172 std::string r33 = GetKv(kv, "R33");
173
174 if (r32.empty())
175 return;
176
177 bool isTMoney = (r33 == "TM");
178 bool isCashBee = (r33 == "EB" || r33 == "CB");
179 bool isRailPlus = (r33 == "RP");
180
181 // 자동 판단
182 if (!isTMoney && !isCashBee && !isRailPlus)
183 {
184 if (r32.size() >= 239 && r32.size() < 300)
185 isTMoney = true;
186 else if (r32.size() >= 334)
187 isCashBee = true;
188 else if (r32.size() == 4 || r32.size() == 55)
189 isRailPlus = true;
190 }
191
192 if (isTMoney)
193 {
194 auto tmResult = ElectronicMoneyParser::ParseTMoney(r32);
195 if (tmResult.isValid)
196 {
197 kv.emplace_back("EM_TYPE", "TMONEY");
198 auto tmKv = ElectronicMoneyParser::TMoneyToKeyValue(tmResult);
199 for (auto &p : tmKv)
200 kv.push_back(p);
201 Logger::info(L"티머니 전문 파싱 성공");
202 }
203 else
204 {
205 if (r32.size() >= 3)
206 {
207 auto tmError = ElectronicMoneyParser::ParseTMoneyError(r32);
208 if (tmError.isValid)
209 {
210 kv.emplace_back("EM_TYPE", "TMONEY_ERROR");
211 kv.emplace_back("TM_ERROR_MSG", tmError.errorMessage);
212 Logger::warn(L"티머니 에러: " + std::wstring(tmError.errorMessage.begin(), tmError.errorMessage.end()));
213 }
214 }
215 }
216 }
217 else if (isCashBee)
218 {
219 auto cbResult = ElectronicMoneyParser::ParseCashBee(r32);
220 if (cbResult.isValid)
221 {
222 kv.emplace_back("EM_TYPE", "CASHBEE");
223 auto cbKv = ElectronicMoneyParser::CashBeeToKeyValue(cbResult);
224 for (auto &p : cbKv)
225 kv.push_back(p);
226 Logger::info(L"캐시비 전문 파싱 성공");
227 }
228 else
229 {
230 if (r32.size() >= 69)
231 {
233 if (cbError.isValid)
234 {
235 kv.emplace_back("EM_TYPE", "CASHBEE_ERROR");
236 kv.emplace_back("CB_ERROR_MSG", cbError.errorMessage);
237 Logger::warn(L"캐시비 에러: " + std::wstring(cbError.errorMessage.begin(), cbError.errorMessage.end()));
238 }
239 }
240 }
241 }
242 else if (isRailPlus)
243 {
244 auto rpResult = ElectronicMoneyParser::ParseRailPlus(r32);
245 if (rpResult.isValid)
246 {
247 kv.emplace_back("EM_TYPE", "RAILPLUS");
248 auto rpKv = ElectronicMoneyParser::RailPlusToKeyValue(rpResult);
249 for (auto &p : rpKv)
250 kv.push_back(p);
251 if (rpResult.isSuccess)
252 Logger::info(L"레일플러스 전문 파싱 성공");
253 else
254 Logger::warn(L"레일플러스 실패");
255 }
256 }
257}
static std::string GetKv(const std::vector< std::pair< std::string, std::string > > &kvs, const std::string &key)
Definition ProtocolAdapter.cpp:56
TMoneyResult ParseTMoney(const std::string &data)
Definition ElectronicMoneyParser.cpp:17
CashBeeError ParseCashBeeError(const std::string &data)
Definition ElectronicMoneyParser.cpp:153
std::vector< std::pair< std::string, std::string > > TMoneyToKeyValue(const TMoneyResult &result)
Definition ElectronicMoneyParser.cpp:348
std::vector< std::pair< std::string, std::string > > RailPlusToKeyValue(const RailPlusResult &result)
Definition ElectronicMoneyParser.cpp:431
CashBeeResult ParseCashBee(const std::string &data)
Definition ElectronicMoneyParser.cpp:61
RailPlusResult ParseRailPlus(const std::string &data)
Definition ElectronicMoneyParser.cpp:174
TMoneyError ParseTMoneyError(const std::string &data)
Definition ElectronicMoneyParser.cpp:134
std::vector< std::pair< std::string, std::string > > CashBeeToKeyValue(const CashBeeResult &result)
Definition ElectronicMoneyParser.cpp:381

다음을 참조함 : ElectronicMoneyParser::CashBeeToKeyValue(), GetKv(), HasKv(), ElectronicMoneyParser::ParseCashBee(), ElectronicMoneyParser::ParseCashBeeError(), ElectronicMoneyParser::ParseRailPlus(), ElectronicMoneyParser::ParseTMoney(), ElectronicMoneyParser::ParseTMoneyError(), ElectronicMoneyParser::RailPlusToKeyValue(), ElectronicMoneyParser::TMoneyToKeyValue().

다음에 의해서 참조됨 : sendAndWait().

이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:
이 함수를 호출하는 함수들에 대한 그래프입니다.:

◆ rcToText()

const char * ProtocolAdapter::rcToText ( int rcd)
staticprivate
139{
140 switch (rcd)
141 {
142 case 0x00:
143 return "RC_SUCCESS";
144 case 0xFF:
145 return "RC_FAILURE";
146 case 0xFA:
147 return "RC_BUSY";
148 case 0xF9:
149 return "RC_INVALID_COMMAND";
150 case 0xF8:
151 return "RC_INVALID_DATA";
152 default:
153 return "RC_UNKNOWN";
154 }
155}

다음에 의해서 참조됨 : onEd785Response(), sendAndWait().

이 함수를 호출하는 함수들에 대한 그래프입니다.:

◆ sendAndWait()

std::string ProtocolAdapter::sendAndWait ( int gcd,
int jcd,
const std::string & asciiPayload,
uint32_t timeoutMs,
const std::weak_ptr< TcpServer::Session > & session )
private
422{
423 uint64_t tx = ++m_txSeq;
424 Logger::info(std::wstring(L"TX#") + std::to_wstring(tx) + L" 요청");
425
426 // 트랜잭션 대기 상태 등록
427 std::future<TxResponse> future;
428 {
429 std::scoped_lock lock(m_pendingMutex);
430 auto pending = std::make_unique<PendingTx>(gcd, jcd, session);
431 future = pending->promise.get_future();
432 m_pendingTxs[tx] = std::move(pending);
433 }
434
435 // ED785 요청 큐잉
436 ED785Worker::Request req;
437 req.cmd = 0xFB;
438 req.gcd = gcd;
439 req.jcd = jcd;
440 req.payload.assign(asciiPayload.begin(), asciiPayload.end());
441
442 if (!m_worker.enqueue(req))
443 {
444 // 등록한 대기 상태 제거
445 {
446 std::scoped_lock lock(m_pendingMutex);
447 m_pendingTxs.erase(tx);
448 }
449 Logger::error(L"요청 큐 추가 실패");
450 return "ERR=ENQUEUE_FAILED";
451 }
452
453 // 응답 대기 (future wait_for)
454 auto status = future.wait_for(std::chrono::milliseconds(timeoutMs));
455
456 // 대기 상태 제거
457 {
458 std::scoped_lock lock(m_pendingMutex);
459 m_pendingTxs.erase(tx);
460 }
461
462 if (status == std::future_status::timeout)
463 {
464 Logger::warn(L"응답 타임아웃");
465 return "ERR=TIMEOUT";
466 }
467
468 // 응답 수신 완료
469 TxResponse txResp = future.get();
470 std::string result;
471
472 // 응답 구성
473 const bool isApprove = (gcd == 0x14) && (jcd == 0x04);
474 if (isApprove)
475 {
476 auto kv = ParseKvOrdered(txResp.data);
477 if (kv.empty() && !txResp.data.empty())
478 kv.emplace_back("MSG", txResp.data);
479
481
482 std::ostringstream os;
483 os << "RC=" << rcToText(txResp.rcd) << ';' << JoinKvOrdered(kv);
484 result = os.str();
485
486 // 로그 출력 (UTF-8 → UTF-16 변환)
487 int wlen = ::MultiByteToWideChar(CP_UTF8, 0, result.c_str(), (int)result.size(), nullptr, 0);
488 std::wstring wResult;
489 if (wlen > 0)
490 {
491 wResult.resize(wlen);
492 ::MultiByteToWideChar(CP_UTF8, 0, result.c_str(), (int)result.size(), wResult.data(), wlen);
493 }
494 if (wResult.size() > 300)
495 wResult = wResult.substr(0, 300) + L"...";
496 Logger::info(L"승인 응답: " + wResult);
497 }
498 else if (gcd == 0x01 && jcd == 0x06)
499 {
500 std::ostringstream os;
501 os << "RC=" << rcToText(txResp.rcd) << ";SNO=" << txResp.data;
502 result = os.str();
503 }
504 else
505 {
506 std::ostringstream os;
507 os << "RC=" << rcToText(txResp.rcd) << ";DATA=" << txResp.data;
508 result = os.str();
509 }
510
511 Logger::info(std::wstring(L"TX#") + std::to_wstring(tx) + L" 완료");
512 return result;
513}
static void ParseAndAppendElectronicMoney(std::vector< std::pair< std::string, std::string > > &kv)
Definition ProtocolAdapter.cpp:165

다음을 참조함 : ED785Worker::Request::cmd, ProtocolAdapter::TxResponse::data, ED785Worker::Request::gcd, ED785Worker::Request::jcd, JoinKvOrdered(), m_pendingMutex, m_pendingTxs, m_txSeq, m_worker, ParseAndAppendElectronicMoney(), ParseKvOrdered(), ED785Worker::Request::payload, ProtocolAdapter::TxResponse::rcd, rcToText().

다음에 의해서 참조됨 : onCommand().

이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:
이 함수를 호출하는 함수들에 대한 그래프입니다.:

◆ splitOnce()

std::pair< std::string, std::string > ProtocolAdapter::splitOnce ( const std::string & s,
char delim )
staticprivate
122{
123 size_t p = s.find(delim);
124 if (p == std::string::npos)
125 return {s, std::string{}};
126 return {s.substr(0, p), s.substr(p + 1)};
127}

다음에 의해서 참조됨 : onCommand().

이 함수를 호출하는 함수들에 대한 그래프입니다.:

◆ trim()

std::string ProtocolAdapter::trim ( const std::string & s)
staticprivate
130{
131 size_t b = s.find_first_not_of("\r\n\t ");
132 if (b == std::string::npos)
133 return {};
134 size_t e = s.find_last_not_of("\r\n\t ");
135 return s.substr(b, e - b + 1);
136}

다음에 의해서 참조됨 : onCommand().

이 함수를 호출하는 함수들에 대한 그래프입니다.:

멤버 데이터 문서화

◆ m_deviceReady

std::atomic<bool> ProtocolAdapter::m_deviceReady {true}
private
73{true};

다음에 의해서 참조됨 : onCommand(), onEd785Response().

◆ m_lastInitializationError

std::string ProtocolAdapter::m_lastInitializationError
private

다음에 의해서 참조됨 : onCommand(), onEd785Response().

◆ m_pendingMutex

std::mutex ProtocolAdapter::m_pendingMutex
private

다음에 의해서 참조됨 : onCommand(), onEd785Response(), sendAndWait().

◆ m_pendingTxs

std::map<uint64_t, std::unique_ptr<PendingTx> > ProtocolAdapter::m_pendingTxs
private

다음에 의해서 참조됨 : onCommand(), onEd785Response(), sendAndWait().

◆ m_settings

AppSettings ProtocolAdapter::m_settings
private

다음에 의해서 참조됨 : ProtocolAdapter().

◆ m_stateMutex

std::mutex ProtocolAdapter::m_stateMutex
private

다음에 의해서 참조됨 : onCommand(), onEd785Response().

◆ m_txSeq

std::atomic<uint64_t> ProtocolAdapter::m_txSeq {0}
private
71{0};

다음에 의해서 참조됨 : onCommand(), sendAndWait().

◆ m_worker

ED785Worker& ProtocolAdapter::m_worker
private

다음에 의해서 참조됨 : onCommand(), ProtocolAdapter(), sendAndWait().


이 클래스에 대한 문서화 페이지는 다음의 파일들로부터 생성되었습니다.: