Files
dbserver/.Notebook/OpcTest program by claude.md

14 KiB
Raw Permalink Blame History

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<EndpointDescriptionCollection> 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<ISession> 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) 테스트 실시간 데이터 수신 테스트