MFC 애플리케이션에서의 OPC UA: Windows 기반 산업용 SCADA를 위한 open62541 통합
산업 자동화 시스템은 점점 더 단일 플랫폼에 의존하는 독점적 SCADA 솔루션에서 벗어나, 유연하고 안전하며 크로스플랫폼인 OPC UA 기반 플랫폼으로 전환하고 있습니다. 이러한 맥락에서, Microsoft Foundation Classes(MFC)를 기반으로 하며 open62541을 통합한 경량 SCADA 시스템을 개발하는 것은 중견 기업에게 기술적으로 타당한 선택이 됩니다—특히 레거시 Windows 인프라와 Siemens S7-1200/S7-1500 컨트롤러와의 통신에 대한 엄격한 지연 요구 사항을 충족해야 할 때 더욱 그렇습니다.
아키텍처의 변화: 독점 SCADA에서 OPC UA 기반 통합으로
전통적인 SCADA 플랫폼(WinCC, InTouch, EcoStruxure)은 폐쇄형 프로토콜과 벤더 종속성에 기반해 구축됩니다. 이들 플랫폼의 배포에는 라이선스 비용과 광범위한 구성 작업이 필요하며, 종종 소프트웨어 설치가 과도하게 부풀려지는 결과를 초래합니다—설치 패키지가 실제로 사용되는 기능의 15~20%만 차지함에도 불구하고 20GB에 달하기도 합니다. 이는 중소기업 입장에서는 경제적으로 정당화되기 어려운 상황입니다.
OPC UA는 세 가지 체계적 문제를 해결합니다:
- 상호 운용성: 통합된 주소 공간(NodeId), 타입화된 변수 및 단일 구독 메커니즘 덕분에 서로 다른 제조사의 장비에서 데이터를 중간 게이트웨이 없이 집계할 수 있습니다.
- 즉시 적용 가능한 보안: X.509 인증서 지원, 보안 정책(없음, Basic128Rsa15, Basic256Sha256) 및 프로토콜 수준의 암호화와 인증을 제공합니다.
- 배포 유연성: 클라이언트는 네이티브 Windows 애플리케이션(MFC/Win32), 웹 서비스 또는 Linux에서 실행되는 마이크로서비스일 수 있으며, 서버 측 로직을 수정하지 않아도 됩니다.
산업 현장에서는 이는 고가의 SCADA 시스템을 OPC UA를 통해 컨트롤러에 직접 연결하고, SQL Server와 연동하여 레시피와 히스토리 데이터를 저장하며, 차트와 장비 상태 표시를 통해 원격 모니터링을 가능하게 하는 맞춤형 솔루션으로 대체하는 것을 의미합니다.
MFC에 open62541 통합: 기술적 특징과 한계
open62541은 임베디드 및 데스크톱 시스템용으로 설계된 C99 준수, 종속성이 없는 오픈소스 라이브러리입니다. 이를 MFC 프로젝트에 사용하려면 몇 가지 조건을 충족해야 합니다:
- 프로젝트는
/MD모드( CRT 동적 링크)로 컴파일되어야 합니다. open62541은 표준 C 라이브러리를 기반으로 하기 때문입니다. - 보안 정책을 사용할 경우 TLS/SSL 스택의 명시적 초기화가 필요합니다—Windows에서는
Wincrypt.h와CryptInitialize()를 사용해야 합니다. UA_Client,UA_CreateSubscriptionRequest,UA_MonitoredItemCreateRequest등 모든 객체는 수동으로 관리해야 합니다: 메모리 할당,UA_Client_disconnect()호출, 그리고UA_Client_delete()를 통한 해제.
중요한 한계는 내장된 스레드 풀이 없다는 점입니다. GUI 스레드가 있는 MFC 애플리케이션에서는 UA_Client_* 호출을 모두 별도의 워커 스레드(AfxBeginThread)에서 실행하거나 PostMessage/WM_USER를 통해 처리해야 인터페이스가 멈추지 않습니다. CMainFrame::OnInitialUpdate()에서 바로 UA_Client_connect()를 호출하면 창이 멈춰 버립니다.
C++/MFC에서의 연결 및 구독 구현
CMainFrame::ConnectCPUOnline() 함수는 OPC UA 서버에 연결하는 최소한의 실행 가능한 시나리오를 보여줍니다. 클라이언트를 초기화하고 설정을 구성한 뒤 연결을 성공적으로 완료합니다. 주요 포인트는 다음과 같습니다:
CT2A문자열을UA_String으로 변환하기 위해UA_String_fromChars()를 사용하는 것—이는 open62541 API의 필수 요건입니다.cc->securityMode = UA_MESSAGESECURITYMODE_NONE을 설정하는 것은 격리된 네트워크에서만 허용되며, 실제 프로젝트에서는UA_MESSAGESECURITYMODE_SIGNANDENCRYPT을 선택하고UA_ClientConfig_setDefaultEncryption()를 통해 인증서를 로드해야 합니다.UA_LOGLEVEL_ERROR수준의 로깅은 생산 환경에서 진단과 성능 사이의 최적 균형을 유지합니다.
연결 후에는 구독을 생성하고 모니터링 항목을 설정합니다. 아래는 구독을 생성하고 첫 번째 태그를 추가하는 코드 스니펫입니다:
// 구독 생성
UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
request.requestedPublishingInterval = 500.0; // ms
request.requestedMaxKeepAliveCount = 10;
request.requestedLifetimeCount = 30;
UA_CreateSubscriptionResponse response = UA_Client_Subscriptions_create(
StrCPUADR[iAdr].g_clientOnline, request, NULL, NULL, NULL);
if (response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
return false;
}
StrCPUADR[iAdr].g_subscriptionId = response.subscriptionId;
// 태그 모니터링 설정
UA_MonitoredItemCreateRequest monRequest = UA_MonitoredItemCreateRequest_default();
monRequest.itemToMonitor.nodeId = UA_NODEID_NUMERIC(0, StrDBINI[i].iIDUA);
monRequest.itemToMonitor.attributeId = UA_ATTRIBUTEID_VALUE;
monRequest.monitoringMode = UA_MONITORINGMODE_REPORTING;
UA_MonitoredItemCreateResult monResponse = UA_Client_MonitoredItems_createSingle(
StrCPUADR[iAdr].g_clientOnline,
StrCPUADR[iAdr].g_subscriptionId,
&monRequest);
참고로 iIDUA는 데이터베이스에서 가져온 컨트롤러의 NodeId(예: ns=2;s=Channel1.Device1.Tag1)입니다. 반드시 서버의 주소 공간과 정확히 일치해야 합니다. NodeId에 오류가 있으면 UA_STATUSCODE_BADNODEIDUNKNOWN이 발생하지만, 시스템이 크래시되지 않습니다—monResponse.statusCode를 확인하는 것이 필수적입니다.
데이터 처리: 읽기부터 SQL Server에 로깅까지
시스템은 데이터 관리를 위해 세 가지 핵심 구조를 사용합니다:
StrDBCP—컨트롤러와의 연결 설정: IP 주소, 온라인/오프라인 상태,UA_Client*포인터, 구독 ID 및 값을 받기 위한arrayValue버퍼.StrDBINI—태그 메타데이터:iIDUA(NodeId),iTYPEID(데이터 타입:UA_TYPES_INT32,UA_TYPES_DOUBLE,UA_TYPES_BOOLEAN), 그리고 로깅 여부를 나타내는bSaveDb플래그.StrDBVAL—시간戳,CTime,iNOMEQ,iItem,iVal을 포함한 히스토리 값 테이블.
값을 업데이트하는 로직은 콜백 함수 UA_Client_DataChangeNotification에서 구현됩니다. 새 값이 수신되면:
iTYPEID를 확인하고UA_Variant_getScalarCopy()를 사용해 타입을 안전하게 캐스팅합니다.sizearray를 고려하여arrayValue[i]에 기록합니다.bSaveDb == true인 경우,CDatabase::ExecuteSQL()를 통해 SQL Server에 매개변수화된 INSERT 문을 작성합니다.
중요한 점: arrayValue에 대한 모든 작업은 크리티컬 섹션(CCriticalSection)으로 보호되어야 합니다. 데이터가 백그라운드 스레드에서 들어오는 반면, GUI 스레드는 동시에 차트 렌더링을 위해 해당 데이터를 요청할 수 있기 때문입니다.
무엇이 중요한가
- MFC 애플리케이션에서 OPC UA를 사용하려면
UA_Client의 생명 주기를 수동으로 관리해야 합니다: 초기화 → 연결 → 구독 → 읽기 → 구독 해제 → 연결 해제 → 삭제. - 보안은 선택 사항이 아닙니다:
SECURITYPOLICY_NONE은 테스트 환경에서만 허용되며, 산업 네트워크에서는 X.509 인증서와SIGNANDENCRYPT가 필수입니다. - open62541은 Windows GUI용 비동기 API를 제공하지 않습니다—모든 네트워크 작업은 별도의 스레드에서 수행한 뒤
PostMessage를 통해 동기화해야 합니다. StrDBCP,StrDBINI,StrDBVAL구조는 OPC UA 위에 추상화 계층을 형성하여 데이터 처리 로직을 변경하지 않고도 다양한 컨트롤러 간에 전환할 수 있도록 합니다.- Siemens S7-1200/S7-1500을 지원하려면 컨트롤러 펌웨어에서 OPC UA 서버를 활성화하고 TIA Portal에서 사용자 접근 제어(UAC)를 적절히 구성해야 합니다.
— Editorial Team
아직 댓글이 없습니다.