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

400 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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) 테스트
✅ 실시간 데이터 수신 테스트