14 KiB
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!
🔧 문제 해결
- "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) 테스트 ✅ 실시간 데이터 수신 테스트