Experion 서버 연결 테스트 코드를 작성해드리겠습니다! 🚀 빠른 테스트 프로젝트 생성 bashcd ~/projects mkdir OpcConnectionTest cd OpcConnectionTest # 콘솔 프로젝트 생성 dotnet new console # .NET 8.0 고정 dotnet new globaljson --sdk-version 8.0.123 --force # OPC UA 패키지 설치 dotnet add package OPCFoundation.NetStandard.Opc.Ua dotnet add package OPCFoundation.NetStandard.Opc.Ua.Client 📝 Program.cs csharpusing Opc.Ua; using Opc.Ua.Client; using Opc.Ua.Configuration; namespace OpcConnectionTest { class Program { static async Task Main(string[] args) { Console.WriteLine("=== Experion OPC UA Connection Test ===\n"); // Experion 서버 정보 (VMware 환경) string primaryEndpoint = "opc.tcp://192.168.xxx.xxx:4840"; // Primary 서버 IP string secondaryEndpoint = "opc.tcp://192.168.xxx.xxx:4840"; // Secondary 서버 IP (있다면) Console.WriteLine($"Primary Server: {primaryEndpoint}"); Console.WriteLine($"Secondary Server: {secondaryEndpoint}\n"); try { // 1. Application Configuration 생성 var config = new ApplicationConfiguration() { ApplicationName = "OPC UA Test Client", ApplicationType = ApplicationType.Client, ApplicationUri = "urn:localhost:OpcUaTestClient", SecurityConfiguration = new SecurityConfiguration { ApplicationCertificate = new CertificateIdentifier { StoreType = "Directory", StorePath = "OPC Foundation/CertificateStores/MachineDefault" }, AutoAcceptUntrustedCertificates = true, // 테스트용 RejectSHA1SignedCertificates = false }, TransportConfigurations = new TransportConfigurationCollection(), TransportQuotas = new TransportQuotas { OperationTimeout = 15000 }, ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 }, TraceConfiguration = new TraceConfiguration { OutputFilePath = "OpcUaTest.log", TraceMasks = 0 // 로그 레벨 (0: 없음, 1: Error, 511: 전부) } }; await config.Validate(ApplicationType.Client); // 2. Primary 서버 검색 Console.WriteLine("Step 1: Discovering Primary Server..."); var endpoints = await DiscoverEndpoints(primaryEndpoint); if (endpoints == null || endpoints.Count == 0) { Console.WriteLine("❌ No endpoints found on Primary server!"); return; } Console.WriteLine($"✅ Found {endpoints.Count} endpoint(s)"); foreach (var ep in endpoints) { Console.WriteLine($" - {ep.EndpointUrl} ({ep.SecurityMode})"); } // 3. Primary 서버 연결 Console.WriteLine("\nStep 2: Connecting to Primary Server..."); var session = await CreateSession(config, endpoints[0]); if (session == null) { Console.WriteLine("❌ Failed to create session!"); return; } Console.WriteLine($"✅ Connected! Session ID: {session.SessionId}"); // 4. 서버 정보 읽기 Console.WriteLine("\nStep 3: Reading Server Information..."); await ReadServerInfo(session); // 5. Redundancy 정보 읽기 (Experion Redundant 확인) Console.WriteLine("\nStep 4: Checking Redundancy Support..."); await CheckRedundancy(session); // 6. 샘플 태그 읽기 (있다면) Console.WriteLine("\nStep 5: Reading Sample Tags..."); await ReadSampleTags(session); // 7. 연결 종료 Console.WriteLine("\nStep 6: Closing connection..."); session.Close(); Console.WriteLine("✅ Connection closed successfully!"); // Secondary 서버도 테스트 (선택) if (!string.IsNullOrEmpty(secondaryEndpoint) && secondaryEndpoint != primaryEndpoint) { Console.WriteLine("\n\n=== Testing Secondary Server ==="); await TestSecondaryServer(config, secondaryEndpoint); } Console.WriteLine("\n✅ All tests completed!"); } catch (Exception ex) { Console.WriteLine($"\n❌ Error: {ex.Message}"); Console.WriteLine($"Stack: {ex.StackTrace}"); } Console.WriteLine("\nPress any key to exit..."); Console.ReadKey(); } // Endpoint 검색 static async Task DiscoverEndpoints(string serverUrl) { try { var endpointConfiguration = EndpointConfiguration.Create(); endpointConfiguration.OperationTimeout = 10000; using (var client = DiscoveryClient.Create(new Uri(serverUrl), endpointConfiguration)) { var endpoints = await client.GetEndpointsAsync(null); return endpoints; } } catch (Exception ex) { Console.WriteLine($"Discovery failed: {ex.Message}"); return null; } } // 세션 생성 static async Task CreateSession(ApplicationConfiguration config, EndpointDescription endpoint) { try { var endpointConfiguration = EndpointConfiguration.Create(config); var configuredEndpoint = new ConfiguredEndpoint(null, endpoint, endpointConfiguration); var session = await Session.Create( config, configuredEndpoint, false, config.ApplicationName, 60000, new UserIdentity(new AnonymousIdentityToken()), // 익명 인증 null ); return session; } catch (Exception ex) { Console.WriteLine($"Session creation failed: {ex.Message}"); return null; } } // 서버 정보 읽기 static async Task ReadServerInfo(ISession session) { try { // Server Status 노드 읽기 var nodeId = new NodeId(Objects.Server_ServerStatus); var value = await Task.Run(() => session.ReadValue(nodeId)); Console.WriteLine($"Server Status: {value.Value}"); // Server State 읽기 var stateNodeId = new NodeId(Variables.Server_ServerStatus_State); var stateValue = await Task.Run(() => session.ReadValue(stateNodeId)); Console.WriteLine($"Server State: {stateValue.Value}"); // Current Time 읽기 var timeNodeId = new NodeId(Variables.Server_ServerStatus_CurrentTime); var timeValue = await Task.Run(() => session.ReadValue(timeNodeId)); Console.WriteLine($"Server Current Time: {timeValue.Value}"); } catch (Exception ex) { Console.WriteLine($"Failed to read server info: {ex.Message}"); } } // Redundancy 지원 확인 static async Task CheckRedundancy(ISession session) { try { // ServiceLevel 읽기 (Primary: 200+, Secondary: <200) var serviceLevelNode = new NodeId(Variables.Server_ServiceLevel); var serviceLevel = await Task.Run(() => session.ReadValue(serviceLevelNode)); byte level = Convert.ToByte(serviceLevel.Value); Console.WriteLine($"Service Level: {level}"); if (level >= 200) { Console.WriteLine("✅ This is PRIMARY server"); } else if (level > 0) { Console.WriteLine("⚠️ This is SECONDARY server"); } else { Console.WriteLine("ℹ️ Standalone server (no redundancy)"); } // ServerRedundancy 노드 확인 var redundancyNode = new NodeId(Objects.Server_ServerRedundancy); var redundancyValue = await Task.Run(() => session.ReadValue(redundancyNode)); Console.WriteLine($"Redundancy Support: {redundancyValue.StatusCode}"); } catch (Exception ex) { Console.WriteLine($"Redundancy check failed: {ex.Message}"); Console.WriteLine("ℹ️ Server may not support redundancy"); } } // 샘플 태그 읽기 static async Task ReadSampleTags(ISession session) { try { // Browse root folder var browser = new Browser(session) { BrowseDirection = BrowseDirection.Forward, ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences, IncludeSubtypes = true, NodeClassMask = (int)NodeClass.Variable | (int)NodeClass.Object, ResultMask = (uint)BrowseResultMask.All }; // Objects 폴더 탐색 var references = browser.Browse(ObjectIds.ObjectsFolder); Console.WriteLine($"Found {references.Count} nodes in Objects folder:"); int count = 0; foreach (var reference in references) { Console.WriteLine($" - {reference.DisplayName} ({reference.NodeClass})"); if (++count >= 10) // 처음 10개만 표시 { Console.WriteLine(" ... (more)"); break; } } // 특정 태그가 있다면 읽기 (NodeId를 알고 있을 때) // var tagNodeId = new NodeId("ns=2;s=YourTagName"); // var tagValue = await Task.Run(() => session.ReadValue(tagNodeId)); // Console.WriteLine($"Tag Value: {tagValue.Value}"); } catch (Exception ex) { Console.WriteLine($"Tag reading failed: {ex.Message}"); } } // Secondary 서버 테스트 static async Task TestSecondaryServer(ApplicationConfiguration config, string serverUrl) { try { var endpoints = await DiscoverEndpoints(serverUrl); if (endpoints == null || endpoints.Count == 0) { Console.WriteLine("❌ Secondary server not reachable"); return; } Console.WriteLine($"✅ Secondary server found with {endpoints.Count} endpoint(s)"); var session = await CreateSession(config, endpoints[0]); if (session != null) { Console.WriteLine("✅ Secondary server connection successful!"); // ServiceLevel 확인 var serviceLevelNode = new NodeId(Variables.Server_ServiceLevel); var serviceLevel = await Task.Run(() => session.ReadValue(serviceLevelNode)); Console.WriteLine($"Secondary Service Level: {serviceLevel.Value}"); session.Close(); } } catch (Exception ex) { Console.WriteLine($"Secondary test failed: {ex.Message}"); } } } } ⚙️ 설정 수정 포인트 Line 15-16: Experion 서버 IP 주소 변경 csharpstring primaryEndpoint = "opc.tcp://192.168.1.10:4840"; // ← 실제 IP string secondaryEndpoint = "opc.tcp://192.168.1.11:4840"; // ← 실제 IP 🚀 실행 bashcd ~/projects/OpcConnectionTest # 빌드 dotnet build # 실행 dotnet run ``` --- ## 📊 예상 출력 ``` === Experion OPC UA Connection Test === Primary Server: opc.tcp://192.168.1.10:4840 Secondary Server: opc.tcp://192.168.1.11:4840 Step 1: Discovering Primary Server... ✅ Found 2 endpoint(s) - opc.tcp://192.168.1.10:4840 (SignAndEncrypt) - opc.tcp://192.168.1.10:4840 (None) Step 2: Connecting to Primary Server... ✅ Connected! Session ID: ns=1;i=123456 Step 3: Reading Server Information... Server State: Running Server Current Time: 2026-02-08 15:30:45 Step 4: Checking Redundancy Support... Service Level: 255 ✅ This is PRIMARY server Redundancy Support: Good Step 5: Reading Sample Tags... Found 15 nodes in Objects folder: - Server (Object) - DeviceSet (Object) - ... Step 6: Closing connection... ✅ Connection closed successfully! ✅ All tests completed! 🔧 문제 해결 1. "Connection refused" 에러 bash# 방화벽 확인 sudo ufw status sudo ufw allow 4840/tcp # Experion 서버 ping 확인 ping 192.168.1.10 2. "Certificate validation failed" csharp// Line 25에서 이미 설정됨 AutoAcceptUntrustedCertificates = true 3. IP 주소 확인 bash# VMware에서 Experion 서버 IP 확인 # Windows 개발 PC에서 ping experion-server-name 📝 다음 단계 연결 성공하면: ✅ Tag 목록 가져오기 ✅ Tag 구독 (Subscription) 테스트 ✅ 실시간 데이터 수신 테스트