TAG 1 개 읽기 성공!

This commit is contained in:
2026-02-21 06:14:35 +09:00
parent 3181052619
commit 20ee22ae0c
62 changed files with 3685 additions and 170 deletions

View File

@@ -0,0 +1,55 @@
cat > OpcTestClient.Config.xml << 'EOF'
<?xml version="1.0" encoding="utf-8"?>
<ApplicationConfiguration
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ua="http://opcfoundation.org/UA/2008/02/Types.xsd"
xmlns="http://opcfoundation.org/UA/SDK/Configuration.xsd">
<ApplicationName>OPC Test Client</ApplicationName>
<ApplicationUri>urn:localhost:OpcTestClient</ApplicationUri>
<ApplicationType>Client_1</ApplicationType>
<SecurityConfiguration>
<ApplicationCertificate>
<StoreType>Directory</StoreType>
<StorePath>%CommonApplicationData%/OPC Foundation/pki/own</StorePath>
<SubjectName>CN=OPC Test Client</SubjectName>
</ApplicationCertificate>
<TrustedPeerCertificates>
<StoreType>Directory</StoreType>
<StorePath>%CommonApplicationData%/OPC Foundation/pki/trusted</StorePath>
</TrustedPeerCertificates>
<RejectedCertificateStore>
<StoreType>Directory</StoreType>
<StorePath>%CommonApplicationData%/OPC Foundation/pki/rejected</StorePath>
</RejectedCertificateStore>
<AutoAcceptUntrustedCertificates>true</AutoAcceptUntrustedCertificates>
</SecurityConfiguration>
<TransportQuotas>
<OperationTimeout>600000</OperationTimeout>
<MaxStringLength>1048576</MaxStringLength>
<MaxByteStringLength>1048576</MaxByteStringLength>
<MaxArrayLength>65536</MaxArrayLength>
<MaxMessageSize>4194304</MaxMessageSize>
<MaxBufferSize>65536</MaxBufferSize>
<ChannelLifetime>300000</ChannelLifetime>
<SecurityTokenLifetime>3600000</SecurityTokenLifetime>
</TransportQuotas>
<ClientConfiguration>
<DefaultSessionTimeout>60000</DefaultSessionTimeout>
<MinSubscriptionLifetime>10000</MinSubscriptionLifetime>
</ClientConfiguration>
<TraceConfiguration>
<OutputFilePath>OpcTest.log</OutputFilePath>
<DeleteOnLoad>true</DeleteOnLoad>
</TraceConfiguration>
</ApplicationConfiguration>
EOF
echo "✅ Config file created!"

View File

@@ -0,0 +1,24 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpcConnectionTest", "OpcConnectionTest.csproj", "{C5537E83-BE41-3CD8-A6DF-DAE381FC5D5C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C5537E83-BE41-3CD8-A6DF-DAE381FC5D5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C5537E83-BE41-3CD8-A6DF-DAE381FC5D5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C5537E83-BE41-3CD8-A6DF-DAE381FC5D5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C5537E83-BE41-3CD8-A6DF-DAE381FC5D5C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {382A4C92-79E5-4684-B03A-688BC4DC21D3}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,358 @@
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Configuration;
using System.Security.Cryptography.X509Certificates;
using System.Text.Json;
namespace OpcConnectionTest
{
public class StatusCodeInfo
{
public string Name { get; set; } = "";
public string Hex { get; set; } = "";
public ulong Decimal { get; set; }
public string Description { get; set; } = "";
}
class Program
{
[Obsolete]
static Dictionary<uint, StatusCodeInfo> _statusCodeMap = new();
static void LoadStatusCodes()
{
try
{
string path = Path.Combine(Directory.GetCurrentDirectory(), "statuscode.json");
if (!File.Exists(path))
{
Console.WriteLine("⚠️ statuscode.json 파일을 찾을 수 없습니다.");
return;
}
var json = File.ReadAllText(path);
var list = JsonSerializer.Deserialize<List<StatusCodeInfo>>(json);
if (list != null)
{
foreach (var item in list)
{
_statusCodeMap[(uint)item.Decimal] = item;
}
}
Console.WriteLine($"✅ StatusCode { _statusCodeMap.Count }개 로드 완료\n");
}
catch (Exception ex)
{
Console.WriteLine($"❌ statuscode.json 로드 실패: {ex.Message}");
}
}
static async Task Main(string[] args)
{
Console.WriteLine("=== Experion OPC UA Connection Test ===\n");
LoadStatusCodes();
// ⚠️ Experion 서버 IP 주소 수정
string endpoint = "opc.tcp://192.168.0.20:4840";
// ⚠️ Honeywell CA로 서명된 클라이언트 인증서 경로 및 비밀번호
string pfxPath = Path.Combine(Directory.GetCurrentDirectory(), "pki/own/certs/OpcTestClient.pfx");
string pfxPassword = ""; // ⚠️ PFX 비밀번호 (없으면 빈 문자열)
// ⚠️ Experion Windows 계정 정보
string userName = "mngr"; // 예: "DOMAIN\\user" 또는 "localuser"
string password = "mngr";
Console.WriteLine($"Server: {endpoint}");
Console.WriteLine($"User: {userName}\n");
// PFX 파일 존재 여부 확인
if (!File.Exists(pfxPath))
{
Console.WriteLine($"❌ PFX 파일을 찾을 수 없습니다: {pfxPath}");
Console.WriteLine("\nPress Enter to exit...");
Console.ReadLine();
return;
}
// Honeywell CA 서명 인증서 로드
// Exportable: OPC UA 채널 서명 시 개인키 접근 필요
X509Certificate2 clientCertificate = new X509Certificate2(
pfxPath,
pfxPassword,
X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
Console.WriteLine($"✅ 인증서 로드: {clientCertificate.Subject}");
Console.WriteLine($" 유효기간: {clientCertificate.NotBefore:yyyy-MM-dd} ~ {clientCertificate.NotAfter:yyyy-MM-dd}");
Console.WriteLine($" 개인키 포함: {clientCertificate.HasPrivateKey}\n");
if (!clientCertificate.HasPrivateKey)
{
Console.WriteLine("❌ 개인키가 없습니다. PFX 파일을 확인하세요.");
Console.WriteLine("\nPress Enter to exit...");
Console.ReadLine();
return;
}
ISession? session = null;
try
{
// 1. Configuration 생성
var config = new ApplicationConfiguration()
{
ApplicationName = "OPC Test Client",
ApplicationType = ApplicationType.Client,
ApplicationUri = "urn:OpcTestClient",
SecurityConfiguration = new SecurityConfiguration
{
ApplicationCertificate = new CertificateIdentifier
{
StoreType = "Directory",
StorePath = Path.GetFullPath("pki/own"),
SubjectName = clientCertificate.Subject,
// Honeywell CA 서명 인증서를 직접 주입
Certificate = clientCertificate
},
TrustedPeerCertificates = new CertificateTrustList
{
StoreType = "Directory",
StorePath = Path.GetFullPath("pki/trusted")
},
TrustedIssuerCertificates = new CertificateTrustList
{
StoreType = "Directory",
// ⚠️ Honeywell 내부 CA 인증서를 이 폴더에 배치해야 합니다
// Experion Station에서 CA 인증서를 내보내어 pki/issuers/certs/ 에 복사
StorePath = Path.GetFullPath("pki/issuers")
},
RejectedCertificateStore = new CertificateTrustList
{
StoreType = "Directory",
StorePath = Path.GetFullPath("pki/rejected")
},
// Honeywell CA 서명 인증서이므로 자동 수락 불필요 — false 권장
// 테스트 중 서버 인증서 검증 실패 시 true로 임시 변경 가능
AutoAcceptUntrustedCertificates = false,
RejectSHA1SignedCertificates = false,
AddAppCertToTrustedStore = false
},
TransportConfigurations = new TransportConfigurationCollection(),
TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 }
};
// 서버 인증서 검증 실패 시 로그만 출력하는 핸들러
// Honeywell CA를 pki/issuers/에 제대로 넣으면 이 핸들러까지 오지 않아야 정상
// 불가피한 경우 e.Accept = true; 주석 해제 (운영 환경에서는 사용 금지)
config.CertificateValidator.CertificateValidation += (validator, e) =>
{
Console.WriteLine($" ⚠️ 서버 인증서 검증 이슈: {e.Error.StatusCode} — {e.Certificate.Subject}");
// e.Accept = true;
};
await config.ValidateAsync(ApplicationType.Client);
Console.WriteLine("Step 1: Discovering endpoints...");
// 2. Endpoint 검색
var endpointConfig = EndpointConfiguration.Create(config);
endpointConfig.OperationTimeout = 10000;
var discoveryClient = await DiscoveryClient.CreateAsync(
new Uri(endpoint),
endpointConfig,
config,
DiagnosticsMasks.None,
CancellationToken.None);
var endpoints = await discoveryClient.GetEndpointsAsync(null);
discoveryClient.Dispose();
if (endpoints == null || endpoints.Count == 0)
{
Console.WriteLine("❌ No endpoints found!");
return;
}
Console.WriteLine($"✅ Found {endpoints.Count} endpoint(s)");
foreach (var ep in endpoints)
{
Console.WriteLine($" - {ep.EndpointUrl} | Security: {ep.SecurityMode} | Policy: {ep.SecurityPolicyUri?.Split('#').Last()}");
}
// SignAndEncrypt 엔드포인트 우선 선택, 없으면 Sign, 없으면 첫 번째
var selectedEndpoint =
endpoints.FirstOrDefault(e => e.SecurityMode == MessageSecurityMode.SignAndEncrypt)
?? endpoints.FirstOrDefault(e => e.SecurityMode == MessageSecurityMode.Sign)
?? endpoints[0];
Console.WriteLine($"\n선택된 엔드포인트: {selectedEndpoint.EndpointUrl} ({selectedEndpoint.SecurityMode})");
Console.WriteLine("\nStep 2: Creating session...");
// 3. ConfiguredEndpoint 생성
var configuredEndpoint = new ConfiguredEndpoint(
null,
selectedEndpoint,
EndpointConfiguration.Create(config));
// 4. Session 생성 — UserIdentity에 Windows 계정 사용
// Experion R530은 Windows 계정을 OPC UA UserNameIdentityToken으로 인증
var userIdentity = new UserIdentity(new UserNameIdentityToken
{
UserName = userName,
DecryptedPassword = System.Text.Encoding.UTF8.GetBytes(password)
});
session = await Session.Create(
config,
configuredEndpoint,
false,
"OPC Test Session",
60000,
userIdentity,
null);
Console.WriteLine($"✅ Connected!");
Console.WriteLine($" Session ID: {session.SessionId}");
Console.WriteLine("\nStep 3: Reading server info...");
await ReadServerInfoAsync(session);
Console.WriteLine("\nStep 4: Checking redundancy...");
await CheckRedundancyAsync(session);
Console.WriteLine("\nStep 5: Browsing nodes...");
await BrowseNodesAsync(session);
Console.WriteLine("\n✅ All tests completed!");
}
catch (ServiceResultException sre)
{
uint code = (uint)sre.StatusCode;
// Console.WriteLine($"\n❌ OPC UA 오류: {sre.Message}");
Console.WriteLine($" StatusCode: 0x{code:X8} ({code})");
if (_statusCodeMap.TryGetValue(code, out var info))
{
Console.WriteLine($" Name: {info.Name}");
Console.WriteLine($" Description: {info.Description}");
}
else
{
Console.WriteLine(" ⚠️ statuscode.json에 정의되지 않은 코드입니다.");
}
}
finally
{
if (session != null)
{
try
{
Console.WriteLine("\nClosing session...");
await session.CloseAsync();
session.Dispose();
Console.WriteLine("✅ Session closed");
}
catch { }
}
}
Console.WriteLine("\nPress Enter to exit...");
Console.ReadLine();
}
static async Task ReadServerInfoAsync(ISession session)
{
try
{
var state = await session.ReadValueAsync(
new NodeId(Variables.Server_ServerStatus_State));
Console.WriteLine($" State: {state.Value}");
var time = await session.ReadValueAsync(
new NodeId(Variables.Server_ServerStatus_CurrentTime));
Console.WriteLine($" Time: {time.Value}");
}
catch (Exception ex)
{
Console.WriteLine($" Error: {ex.Message}");
}
}
static async Task CheckRedundancyAsync(ISession session)
{
try
{
var serviceLevel = await session.ReadValueAsync(
new NodeId(Variables.Server_ServiceLevel));
byte level = Convert.ToByte(serviceLevel.Value);
Console.WriteLine($" Service Level: {level}");
if (level >= 200)
Console.WriteLine(" ✅ PRIMARY server");
else if (level > 0)
Console.WriteLine(" ⚠️ SECONDARY server");
else
Console.WriteLine(" STANDALONE");
}
catch (Exception ex)
{
Console.WriteLine($" Error: {ex.Message}");
}
}
static async Task BrowseNodesAsync(ISession session)
{
try
{
var browser = new Browser(session)
{
BrowseDirection = BrowseDirection.Forward,
ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences,
IncludeSubtypes = true,
NodeClassMask = (int)(NodeClass.Object | NodeClass.Variable)
};
var references = await browser.BrowseAsync(ObjectIds.ObjectsFolder);
Console.WriteLine($" Found {references.Count} top-level nodes:");
int count = 0;
foreach (var r in references)
{
Console.WriteLine($" {r.DisplayName} ({r.NodeClass})");
if (++count >= 5)
{
Console.WriteLine($" ... and {references.Count - 5} more");
break;
}
}
}
catch (Exception ex)
{
Console.WriteLine($" Error: {ex.Message}");
}
}
}
}

View File

@@ -0,0 +1,253 @@
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Configuration;
using System.Security.Cryptography.X509Certificates;
namespace OpcConnectionTest
{
class Program
{
[Obsolete]
static async Task Main(string[] args)
{
Console.WriteLine("=== Experion OPC UA Connection Test ===\n");
// ⚠️ Experion 서버 IP 주소 수정
string endpoint = "opc.tcp://192.168.0.20:4840";
string pfxPath = Path.Combine(Directory.GetCurrentDirectory(), "pki/own/certs/OpcTestClient.pfx");
X509Certificate2 clientCertificate = new X509Certificate2(pfxPath, "");
Console.WriteLine($"Server: {endpoint}\n");
ISession? session = null;
try
{
// 1. Configuration 생성
var config = new ApplicationConfiguration()
{
ApplicationName = "OPC Test Client",
ApplicationType = ApplicationType.Client,
ApplicationUri = "urn:OpcTestClient",
SecurityConfiguration = new SecurityConfiguration
{
ApplicationCertificate = new CertificateIdentifier
{
StoreType = "Directory",
StorePath = Path.GetFullPath("pki/own"),
SubjectName = "CN=OpcTestClient"
},
TrustedPeerCertificates = new CertificateTrustList
{
StoreType = "Directory",
StorePath = Path.GetFullPath("pki/trusted")
},
TrustedIssuerCertificates = new CertificateTrustList
{
StoreType = "Directory",
StorePath = Path.GetFullPath("pki/issuers")
},
RejectedCertificateStore = new CertificateTrustList
{
StoreType = "Directory",
StorePath = Path.GetFullPath("pki/rejected")
},
AutoAcceptUntrustedCertificates = true,
RejectSHA1SignedCertificates = false,
AddAppCertToTrustedStore = true
},
TransportConfigurations = new TransportConfigurationCollection(),
TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 }
};
await config.ValidateAsync(ApplicationType.Client);
Console.WriteLine("Step 1: Discovering endpoints...");
// 2. Endpoint 검색
var endpointConfig = EndpointConfiguration.Create(config);
endpointConfig.OperationTimeout = 10000;
var discoveryClient = await DiscoveryClient.CreateAsync(
new Uri(endpoint),
endpointConfig,
config,
DiagnosticsMasks.None,
CancellationToken.None);
var endpoints = await discoveryClient.GetEndpointsAsync(null);
discoveryClient.Dispose();
if (endpoints == null || endpoints.Count == 0)
{
Console.WriteLine("❌ No endpoints found!");
return;
}
Console.WriteLine($"✅ Found {endpoints.Count} endpoint(s)");
foreach (var ep in endpoints)
{
Console.WriteLine($" - {ep.EndpointUrl} ({ep.SecurityMode})");
}
Console.WriteLine("\nStep 2: Creating session...");
// 3. ConfiguredEndpoint 생성
var selectedEndpoint = endpoints[0];
var configuredEndpoint = new ConfiguredEndpoint(
null,
selectedEndpoint,
EndpointConfiguration.Create(config));
// Session.Create 호출 직전에 추가
var cert = await config.SecurityConfiguration.ApplicationCertificate.Find(true);
if (cert == null)
{
Console.WriteLine("❌ [Internal Error] 코드가 인증서 파일을 찾지 못했습니다!");
Console.WriteLine($"확인 경로: {Path.GetFullPath(config.SecurityConfiguration.ApplicationCertificate.StorePath)}");
return;
}
else
{
Console.WriteLine($"✅ 인증서 로드 성공: {cert.Subject}");
}
// 4. Session 생성 (최신 API)
session = await Session.Create(
config,
configuredEndpoint,
false,
"OPC Test Session",
60000,
new UserIdentity(new AnonymousIdentityToken()),
null);
Console.WriteLine($"✅ Connected!");
Console.WriteLine($" Session ID: {session.SessionId}");
Console.WriteLine("\nStep 3: Reading server info...");
await ReadServerInfoAsync(session);
Console.WriteLine("\nStep 4: Checking redundancy...");
await CheckRedundancyAsync(session);
Console.WriteLine("\nStep 5: Browsing nodes...");
await BrowseNodesAsync(session);
Console.WriteLine("\n✅ All tests completed!");
}
catch (Exception ex)
{
Console.WriteLine($"\n❌ Error: {ex.Message}");
Console.WriteLine($"Type: {ex.GetType().Name}");
if (ex.InnerException != null)
{
Console.WriteLine($"Inner: {ex.InnerException.Message}");
}
}
finally
{
if (session != null)
{
try
{
Console.WriteLine("\nClosing session...");
await session.CloseAsync();
session.Dispose();
Console.WriteLine("✅ Session closed");
}
catch { }
}
}
Console.WriteLine("\nPress Enter to exit...");
Console.ReadLine();
}
static async Task ReadServerInfoAsync(ISession session)
{
try
{
// Server State
var state = await session.ReadValueAsync(
new NodeId(Variables.Server_ServerStatus_State));
Console.WriteLine($" State: {state.Value}");
// Server Time
var time = await session.ReadValueAsync(
new NodeId(Variables.Server_ServerStatus_CurrentTime));
Console.WriteLine($" Time: {time.Value}");
}
catch (Exception ex)
{
Console.WriteLine($" Error: {ex.Message}");
}
}
static async Task CheckRedundancyAsync(ISession session)
{
try
{
var serviceLevel = await session.ReadValueAsync(
new NodeId(Variables.Server_ServiceLevel));
byte level = Convert.ToByte(serviceLevel.Value);
Console.WriteLine($" Service Level: {level}");
if (level >= 200)
Console.WriteLine(" ✅ PRIMARY server");
else if (level > 0)
Console.WriteLine(" ⚠️ SECONDARY server");
else
Console.WriteLine(" STANDALONE");
}
catch (Exception ex)
{
Console.WriteLine($" Error: {ex.Message}");
}
}
static async Task BrowseNodesAsync(ISession session)
{
try
{
var browser = new Browser(session)
{
BrowseDirection = BrowseDirection.Forward,
ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences,
IncludeSubtypes = true,
NodeClassMask = (int)(NodeClass.Object | NodeClass.Variable)
};
var references = await browser.BrowseAsync(ObjectIds.ObjectsFolder);
Console.WriteLine($" Found {references.Count} top-level nodes:");
int count = 0;
foreach (var r in references)
{
Console.WriteLine($" {r.DisplayName} ({r.NodeClass})");
if (++count >= 5)
{
Console.WriteLine($" ... and {references.Count - 5} more");
break;
}
}
}
catch (Exception ex)
{
Console.WriteLine($" Error: {ex.Message}");
}
}
}
}

View File

@@ -0,0 +1,212 @@
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Configuration;
using System.Security.Cryptography.X509Certificates;
using System.Text.Json;
using System.Text;
namespace OpcConnectionTest
{
public class StatusCodeInfo
{
public string Name { get; set; } = "";
public string Hex { get; set; } = "";
public ulong Decimal { get; set; }
public string Description { get; set; } = "";
}
class Program
{
static Dictionary<uint, StatusCodeInfo> _statusCodeMap = new();
// statuscode.json 로드 로직
static void LoadStatusCodes()
{
string path = Path.Combine(Directory.GetCurrentDirectory(), "statuscode.json");
if (!File.Exists(path))
{
Console.WriteLine("⚠️ statuscode.json 파일을 찾을 수 없습니다. (에러 분석 기능 제한)");
return;
}
try
{
var json = File.ReadAllText(path);
var list = JsonSerializer.Deserialize<List<StatusCodeInfo>>(json);
if (list != null)
foreach (var item in list)
_statusCodeMap[(uint)item.Decimal] = item;
Console.WriteLine($"✅ StatusCode {_statusCodeMap.Count}개 로드 완료\n");
}
catch (Exception ex)
{
Console.WriteLine($"❌ statuscode.json 로드 실패: {ex.Message}");
}
}
static async Task Main(string[] args)
{
LoadStatusCodes();
// 1. 설정 정보 (UaExpert 정보 기반)
string endpointUrl = "opc.tcp://DESKTOP-8VS6SD2:4840";
string pfxPath = Path.Combine(Directory.GetCurrentDirectory(), "pki/own/certs/OpcTestClient.pfx");
string pfxPassword = ""; // 저장된 정보 기반 공백 유지
string userName = "mngr";
string password = "mngr";
if (!File.Exists(pfxPath))
{
Console.WriteLine($"❌ PFX 파일 없음: {pfxPath}");
return;
}
// 2. 인증서 로드 및 애플리케이션 설정
var clientCert = new X509Certificate2(
pfxPath,
pfxPassword,
X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet
);
var config = new ApplicationConfiguration
{
ApplicationName = "OpcTestClient",
ApplicationType = ApplicationType.Client,
ApplicationUri = "urn:OpcTestClient",
SecurityConfiguration = new SecurityConfiguration
{
ApplicationCertificate = new CertificateIdentifier { Certificate = clientCert },
TrustedPeerCertificates = new CertificateTrustList { StoreType = "Directory", StorePath = Path.GetFullPath("pki/trusted") },
TrustedIssuerCertificates = new CertificateTrustList { StoreType = "Directory", StorePath = Path.GetFullPath("pki/issuers") },
RejectedCertificateStore = new CertificateTrustList { StoreType = "Directory", StorePath = Path.GetFullPath("pki/rejected") },
AutoAcceptUntrustedCertificates = true,
AddAppCertToTrustedStore = true
},
TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 }
};
config.CertificateValidator.CertificateValidation += (validator, e) =>
{
if (e.Error != null && e.Error.StatusCode != StatusCodes.Good)
{
Console.WriteLine($"인증서 보안 경고 무시(Ignore): {e.Error.StatusCode}");
e.Accept = true;
}
};
await config.ValidateAsync(ApplicationType.Client);
ISession? session = null;
try
{
// 3. 엔드포인트 탐색 및 선택 (UaExpert 보안 정책 반영)
Console.WriteLine("Step 1: Discovering endpoints...");
using (var discovery = DiscoveryClient.Create(new Uri(endpointUrl), EndpointConfiguration.Create(config)))
{
var endpoints = discovery.GetEndpoints(null);
Console.WriteLine($"✅ {endpoints.Count} 엔드포인트 발견");
// 가장 높은 보안 수준을 가진 엔드포인트 중 Aes256_Sha256_RsaPss 우선 선택
var selected = endpoints.OrderByDescending(e => e.SecurityLevel)
.FirstOrDefault(e => e.SecurityPolicyUri.Contains("Basic256Sha256"))
?? endpoints.OrderByDescending(e => e.SecurityLevel).First();
Console.WriteLine($"👉 선택된 엔드포인트: {selected.SecurityMode} | {selected.SecurityPolicyUri.Split('#').Last()}");
var configured = new ConfiguredEndpoint(null, selected, EndpointConfiguration.Create(config));
// 4. 세션 생성 (mngr 계정)
Console.WriteLine("\nStep 2: Creating session with user identity...");
// 패스워드를 byte[]로 요구하는 최신 SDK 방식 적용
var identity = new UserIdentity(userName, Encoding.UTF8.GetBytes(password));
session = await Session.Create(
config,
configured,
false, // updateBeforeConnect
"OPC Test Session",
60000,
identity,
null
);
}
Console.WriteLine($"✅ Connected! SessionID: {session.SessionId}");
// 5. 서버 상태 확인
await ReadServerInfoAsync(session);
// 6. UaExpert에서 확인한 하니웰 실제 태그 데이터 읽기
await ReadHoneywellTagAsync(session);
}
catch (ServiceResultException sre)
{
uint code = (uint)sre.StatusCode;
Console.WriteLine($"\n❌ OPC UA 오류: {sre.Message}");
Console.WriteLine($" StatusCode: 0x{code:X8}");
if (_statusCodeMap.TryGetValue(code, out var info))
Console.WriteLine($" Name: {info.Name}\n Description: {info.Description}");
}
catch (Exception ex)
{
Console.WriteLine($"\n❌ 일반 오류 발생: {ex.Message}");
}
finally
{
if (session != null)
{
await session.CloseAsync();
session.Dispose();
Console.WriteLine("\n✅ Session closed");
}
}
Console.WriteLine("\nPress Enter to exit...");
Console.ReadLine();
}
// 서버 기본 정보 읽기
static async Task ReadServerInfoAsync(ISession session)
{
var state = await session.ReadValueAsync(Variables.Server_ServerStatus_State);
var time = await session.ReadValueAsync(Variables.Server_ServerStatus_CurrentTime);
Console.WriteLine($"\n[Server Status]\n - State: {state.Value}\n - Time: {time.Value}");
}
// 🎯 하니웰 특정 태그 읽기 (UaExpert 데이터 기반)
static async Task ReadHoneywellTagAsync(ISession session)
{
string nodeId = "ns=1;s=shinam:p-6102.hzset.fieldvalue";
Console.WriteLine($"\nStep 3: Reading Honeywell Tag [{nodeId}]...");
try
{
var result = await session.ReadValueAsync(nodeId);
if (StatusCode.IsGood(result.StatusCode))
{
Console.WriteLine("========================================");
Console.WriteLine($"🟢 TAG VALUE : {result.Value}");
Console.WriteLine($"🟡 DATATYPE : {result.WrappedValue.TypeInfo.BuiltInType}");
Console.WriteLine($"🔵 TIMESTAMP : {result.SourceTimestamp.ToLocalTime()}");
Console.WriteLine($"⚪️ STATUS : {result.StatusCode}");
Console.WriteLine("========================================");
}
else
{
Console.WriteLine($"❌ 읽기 실패: {result.StatusCode}");
}
}
catch (Exception ex)
{
Console.WriteLine($"❌ 태그 읽기 중 예외 발생: {ex.Message}");
}
}
}
}

View File

@@ -1,191 +1,145 @@
// cd ~/projects/OpcConnectionTest using System;
using System.Collections.Generic;
// 기존 파일 삭제 using System.IO;
// rm Program.cs using System.Linq;
using System.Text;
// # 새 파일 생성 using System.Threading;
//cat > Program.cs << 'EOF' using System.Threading.Tasks;
using System.Security.Cryptography.X509Certificates;
using System.Text.Json;
using Opc.Ua; using Opc.Ua;
using Opc.Ua.Client; using Opc.Ua.Client;
using Opc.Ua.Configuration;
namespace OpcConnectionTest namespace OpcConnectionTest
{ {
public class StatusCodeInfo
{
public string Name { get; set; } = "";
public string Hex { get; set; } = "";
public ulong Decimal { get; set; }
public string Description { get; set; } = "";
}
class Program class Program
{ {
static Dictionary<string, StatusCodeInfo> _statusCodeMap = new(StringComparer.OrdinalIgnoreCase);
static void LoadStatusCodes()
{
string path = Path.Combine(Directory.GetCurrentDirectory(), "statuscode.json");
if (File.Exists(path))
{
try {
var json = File.ReadAllText(path);
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var list = JsonSerializer.Deserialize<List<StatusCodeInfo>>(json, options);
if (list != null) {
foreach (var item in list) _statusCodeMap[item.Hex] = item;
Console.WriteLine($"✅ {_statusCodeMap.Count}개의 에러 코드 정의 로드 완료.");
}
} catch { }
}
}
static async Task Main(string[] args) static async Task Main(string[] args)
{ {
Console.WriteLine("=== Experion OPC UA Connection Test ===\n"); LoadStatusCodes();
// ⚠️ Experion 서버 IP 주소 수정 // 1. 핵심 변수 (서버 이름 사칭 방지)
string primaryEndpoint = "opc.tcp://192.168.0.20:4840"; string serverHostName = "192.168.0.20"; //DESKTOP-8VS6SD2
string secondaryEndpoint = "opc.tcp://192.168.1.20:4840"; string clientHostName = "dbsvr";
string endpointUrl = $"opc.tcp://{serverHostName}:4840";
string applicationUri = $"urn:{clientHostName}:OpcTestClient";
Console.WriteLine($"Primary: {primaryEndpoint}"); string pfxPath = Path.Combine(Directory.GetCurrentDirectory(), "pki/own/certs/OpcTestClient.pfx");
Console.WriteLine($"Secondary: {secondaryEndpoint}\n"); string pfxPassword = "";
string userName = "mngr";
string password = "mngr";
ISession? session = null; // 필수 폴더들 생성 (TrustedIssuer 포함)
Directory.CreateDirectory(Path.GetDirectoryName(pfxPath)!);
Directory.CreateDirectory("pki/trusted/certs");
Directory.CreateDirectory("pki/issuers/certs"); // ← 이 경로가 필요함
Directory.CreateDirectory("pki/rejected/certs");
try X509Certificate2? clientCert = null;
if (!File.Exists(pfxPath))
{ {
var config = new ApplicationConfiguration() var builder = CertificateFactory.CreateCertificate(
applicationUri, "OpcTestClient", $"CN=OpcTestClient, O=MyCompany",
new List<string> { "localhost", clientHostName, "192.168.0.5", "192.168.0.102" }
);
clientCert = builder.CreateForRSA();
File.WriteAllBytes(pfxPath, clientCert.Export(X509ContentType.Pfx, pfxPassword));
Console.WriteLine($"✨ 새 인증서 생성됨: {applicationUri}");
}
else
{ {
ApplicationName = "OPC Test", clientCert = new X509Certificate2(pfxPath, pfxPassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
}
var config = new ApplicationConfiguration
{
ApplicationName = "OpcTestClient",
ApplicationType = ApplicationType.Client, ApplicationType = ApplicationType.Client,
ApplicationUri = "urn:OpcTest", ApplicationUri = applicationUri,
SecurityConfiguration = new SecurityConfiguration SecurityConfiguration = new SecurityConfiguration
{ {
ApplicationCertificate = new CertificateIdentifier ApplicationCertificate = new CertificateIdentifier { Certificate = clientCert },
{ // ⚠️ 에러 발생했던 지점: 아래 3개 경로가 모두 명시되어야 함
StoreType = "Directory", TrustedPeerCertificates = new CertificateTrustList { StoreType = "Directory", StorePath = Path.GetFullPath("pki/trusted") },
StorePath = "OPC Foundation/CertificateStores/MachineDefault" TrustedIssuerCertificates = new CertificateTrustList { StoreType = "Directory", StorePath = Path.GetFullPath("pki/issuers") },
}, RejectedCertificateStore = new CertificateTrustList { StoreType = "Directory", StorePath = Path.GetFullPath("pki/rejected") },
AutoAcceptUntrustedCertificates = true, AutoAcceptUntrustedCertificates = true,
RejectSHA1SignedCertificates = false AddAppCertToTrustedStore = true
}, },
TransportConfigurations = new TransportConfigurationCollection(),
TransportQuotas = new TransportQuotas { OperationTimeout = 15000 }, TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 } ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 }
}; };
await config.ValidateAsync(ApplicationType.Client); await config.ValidateAsync(ApplicationType.Client);
config.CertificateValidator.CertificateValidation += (v, e) => { if (e.Error.StatusCode != StatusCodes.Good) e.Accept = true; };
Console.WriteLine("Step 1: Discovering endpoints..."); ISession? session = null;
var endpoints = await DiscoverAsync(config, primaryEndpoint); try
if (endpoints.Count == 0)
{ {
Console.WriteLine("❌ No endpoints found!"); Console.WriteLine($"Step 1: Connecting to {endpointUrl}...");
return; var endpointConfig = EndpointConfiguration.Create(config);
} using (var discovery = await DiscoveryClient.CreateAsync(config, new Uri(endpointUrl), DiagnosticsMasks.All, CancellationToken.None))
Console.WriteLine($"✅ Found {endpoints.Count} endpoint(s)");
Console.WriteLine("\nStep 2: Connecting...");
session = await ConnectAsync(config, endpoints[0]);
if (session == null)
{ {
Console.WriteLine("❌ Connection failed!"); var endpoints = await discovery.GetEndpointsAsync(null);
return; var selected = endpoints.OrderByDescending(e => e.SecurityLevel)
.FirstOrDefault(e => e.SecurityPolicyUri.Contains("Basic256Sha256")) ?? endpoints[0];
Console.WriteLine($"🔍 정책 선택됨: {selected.SecurityPolicyUri}");
var endpoint = new ConfiguredEndpoint(null, selected, endpointConfig);
Console.WriteLine("Step 2: Creating session...");
var identity = new UserIdentity(userName, Encoding.UTF8.GetBytes(password));
#pragma warning disable CS0618
session = await Session.Create(config, endpoint, false, "OpcTestSession", 60000, identity, null);
#pragma warning restore CS0618
} }
Console.WriteLine($"✅ Connected! SessionID: {session.SessionId}");
Console.WriteLine($"✅ Connected! ID: {session.SessionId}"); var result = await session.ReadValueAsync("ns=1;s=shinam:p-6102.hzset.fieldvalue");
Console.WriteLine($"🟢 TAG VALUE: {result.Value}");
Console.WriteLine("\nStep 3: Reading server info...");
await ReadInfoAsync(session);
Console.WriteLine("\nStep 4: Checking redundancy...");
await CheckRedundancyAsync(session);
Console.WriteLine("\n✅ Test completed!");
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"\n❌ Error: {ex.Message}"); if (ex is ServiceResultException sre)
}
finally
{ {
if (session != null) string hexCode = $"0x{((uint)sre.StatusCode):X8}";
{ Console.WriteLine($"\n❌ OPC UA 서비스 오류: {hexCode}");
await session.CloseAsync(); if (_statusCodeMap.TryGetValue(hexCode, out var info)) {
session.Dispose(); Console.WriteLine($"👉 에러명: {info.Name}");
Console.WriteLine($"👉 설 명: {info.Description}");
} }
} }
else Console.WriteLine($"❌ 오류: {ex.Message}");
Console.WriteLine("\nPress Enter to exit..."); }
finally { if (session != null) { await session.CloseAsync(); session.Dispose(); } }
Console.WriteLine("\n엔터를 누르면 종료됩니다...");
Console.ReadLine(); Console.ReadLine();
} }
static async Task<EndpointDescriptionCollection> DiscoverAsync(
ApplicationConfiguration config, string url)
{
try
{
var epConfig = EndpointConfiguration.Create(config);
epConfig.OperationTimeout = 10000;
var client = await DiscoveryClient.CreateAsync(
new Uri(url), epConfig, config,
DiagnosticsMasks.None, CancellationToken.None);
var eps = await client.GetEndpointsAsync(null);
client.Dispose();
return eps;
}
catch
{
return new EndpointDescriptionCollection();
} }
} }
static async Task<ISession?> ConnectAsync(
ApplicationConfiguration config, EndpointDescription ep)
{
try
{
var epConfig = EndpointConfiguration.Create(config);
var configEp = new ConfiguredEndpoint(null, ep, epConfig);
var channel = SessionChannel.Create(
config,
configEp.Description,
configEp.Configuration,
X509CertificateValidator.TolerateAll,
null);
var sess = new Session(channel, config, configEp, null);
await sess.OpenAsync(
config.ApplicationName, 60000,
new UserIdentity(new AnonymousIdentityToken()),
null, CancellationToken.None);
return sess;
}
catch
{
return null;
}
}
static async Task ReadInfoAsync(ISession s)
{
try
{
var v1 = await s.ReadValueAsync(new NodeId(Variables.Server_ServerStatus_State));
Console.WriteLine($" State: {v1.Value}");
var v2 = await s.ReadValueAsync(new NodeId(Variables.Server_ServerStatus_CurrentTime));
Console.WriteLine($" Time: {v2.Value}");
}
catch (Exception ex)
{
Console.WriteLine($" Failed: {ex.Message}");
}
}
static async Task CheckRedundancyAsync(ISession s)
{
try
{
var v = await s.ReadValueAsync(new NodeId(Variables.Server_ServiceLevel));
byte level = Convert.ToByte(v.Value);
Console.WriteLine($" Service Level: {level}");
if (level >= 200)
Console.WriteLine(" ✅ PRIMARY server");
else if (level > 0)
Console.WriteLine(" ⚠️ SECONDARY server");
else
Console.WriteLine(" STANDALONE");
}
catch (Exception ex)
{
Console.WriteLine($" Failed: {ex.Message}");
}
}
}
}

View File

@@ -0,0 +1,187 @@
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Configuration;
using System.Security.Cryptography.X509Certificates;
using System.Text.Json;
using System.Text;
namespace OpcConnectionTest
{
public class StatusCodeInfo
{
public string Name { get; set; } = "";
public string Hex { get; set; } = "";
public ulong Decimal { get; set; }
public string Description { get; set; } = "";
}
class Program
{
static Dictionary<uint, StatusCodeInfo> _statusCodeMap = new();
static void LoadStatusCodes()
{
string path = Path.Combine(Directory.GetCurrentDirectory(), "statuscode.json");
if (!File.Exists(path))
{
Console.WriteLine("⚠️ statuscode.json 파일을 찾을 수 없습니다.");
return;
}
try
{
var json = File.ReadAllText(path);
var list = JsonSerializer.Deserialize<List<StatusCodeInfo>>(json);
if (list != null)
foreach (var item in list)
_statusCodeMap[(uint)item.Decimal] = item;
Console.WriteLine($"✅ StatusCode {_statusCodeMap.Count}개 로드 완료\n");
}
catch (Exception ex)
{
Console.WriteLine($"❌ statuscode.json 로드 실패: {ex.Message}");
}
}
static async Task Main(string[] args)
{
LoadStatusCodes();
string endpointUrl = "opc.tcp://192.168.0.20:4840";
string pfxPath = Path.Combine(Directory.GetCurrentDirectory(), "pki/own/certs/OpcTestClient.pfx");
string pfxPassword = "";
string userName = "mngr";
string password = "mngr";
if (!File.Exists(pfxPath))
{
Console.WriteLine($"❌ PFX 파일 없음: {pfxPath}");
return;
}
var clientCert = new X509Certificate2(
pfxPath,
pfxPassword,
X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet
);
if (!clientCert.HasPrivateKey)
{
Console.WriteLine("❌ 인증서에 개인키 없음");
return;
}
var config = new ApplicationConfiguration
{
ApplicationName = "OPC Test Client",
ApplicationType = ApplicationType.Client,
SecurityConfiguration = new SecurityConfiguration
{
ApplicationCertificate = new CertificateIdentifier
{
StoreType = "Directory",
StorePath = Path.GetFullPath("pki/own"),
Certificate = clientCert
},
TrustedPeerCertificates = new CertificateTrustList
{
StoreType = "Directory",
StorePath = Path.GetFullPath("pki/trusted")
},
TrustedIssuerCertificates = new CertificateTrustList
{
StoreType = "Directory",
StorePath = Path.GetFullPath("pki/issuers")
},
RejectedCertificateStore = new CertificateTrustList
{
StoreType = "Directory",
StorePath = Path.GetFullPath("pki/rejected")
},
AutoAcceptUntrustedCertificates = true
},
TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 }
};
config.CertificateValidator.CertificateValidation += (s, e) =>
{
Console.WriteLine($"⚠️ 서버 인증서 검증 실패: {e.Error.StatusCode} - {e.Certificate.Subject}");
};
await config.ValidateAsync(ApplicationType.Client);
ISession? session = null;
try
{
Console.WriteLine("Step 1: Discovering endpoints...");
var discovery = DiscoveryClient.Create(new Uri(endpointUrl));
var endpoints = discovery.GetEndpoints(null);
Console.WriteLine($"✅ {endpoints.Count} 엔드포인트 발견");
foreach (var ep in endpoints)
Console.WriteLine($" - {ep.EndpointUrl} | {ep.SecurityMode} | {ep.SecurityPolicyUri?.Split('#').Last()}");
// AES256/SHA256 우선 선택
var selected = endpoints.FirstOrDefault(ep =>
ep.SecurityMode == MessageSecurityMode.SignAndEncrypt &&
ep.SecurityPolicyUri.EndsWith("Basic256Sha256"))
?? endpoints.First();
Console.WriteLine($"\n선택된 엔드포인트: {selected.EndpointUrl} ({selected.SecurityMode})");
var configured = new ConfiguredEndpoint(null, selected, EndpointConfiguration.Create(config));
// ✅ Session.CreateAsync 사용
session = await Session.Create(
config,
configured,
false,
"OPC Test Session",
60000,
new UserIdentity(userName, Encoding.UTF8.GetBytes(password)),
null
);
Console.WriteLine($"✅ Connected! SessionID: {session.SessionId}");
await ReadServerInfoAsync(session);
}
catch (ServiceResultException sre)
{
uint code = (uint)sre.StatusCode;
Console.WriteLine($"❌ OPC UA 오류: {sre.Message}");
Console.WriteLine($" StatusCode: 0x{code:X8}");
if (_statusCodeMap.TryGetValue(code, out var info))
Console.WriteLine($" Name: {info.Name}\n Description: {info.Description}");
else
Console.WriteLine(" ⚠️ statuscode.json에 정의되지 않은 코드입니다.");
}
finally
{
if (session != null)
{
await session.CloseAsync();
session.Dispose();
Console.WriteLine("✅ Session closed");
}
}
Console.WriteLine("\nPress Enter to exit...");
Console.ReadLine();
}
static async Task ReadServerInfoAsync(ISession session)
{
var state = await session.ReadValueAsync(Variables.Server_ServerStatus_State);
var time = await session.ReadValueAsync(Variables.Server_ServerStatus_CurrentTime);
Console.WriteLine($"Server State: {state.Value}");
Console.WriteLine($"Current Time: {time.Value}");
}
}
}

View File

@@ -0,0 +1,274 @@
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Configuration;
using System.Security.Cryptography.X509Certificates;
namespace OpcConnectionTest
{
class Program
{
[Obsolete]
static async Task Main(string[] args)
{
Console.WriteLine("=== Experion OPC UA Connection Test ===\n");
// ⚠️ Experion 서버 IP 주소 수정
string endpoint = "opc.tcp://192.168.0.20:4840";
string pfxPath = Path.Combine(Directory.GetCurrentDirectory(), "pki/own/certs/OpcTestClient.pfx");
// PFX 파일 존재 여부 먼저 확인
if (!File.Exists(pfxPath))
{
Console.WriteLine($"❌ PFX 파일을 찾을 수 없습니다: {pfxPath}");
Console.WriteLine("\nPress Enter to exit...");
Console.ReadLine();
return;
}
// PFX를 직접 로드 (Exportable 플래그 필수 — OPC UA 서명에 개인키 접근 필요)
X509Certificate2 clientCertificate = new X509Certificate2(
pfxPath,
"",
X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
Console.WriteLine($"Server: {endpoint}");
Console.WriteLine($"Certificate: {clientCertificate.Subject}\n");
ISession? session = null;
try
{
// 1. Configuration 생성
var config = new ApplicationConfiguration()
{
ApplicationName = "OPC Test Client",
ApplicationType = ApplicationType.Client,
ApplicationUri = "urn:OpcTestClient",
SecurityConfiguration = new SecurityConfiguration
{
ApplicationCertificate = new CertificateIdentifier
{
StoreType = "Directory",
StorePath = Path.GetFullPath("pki/own"),
SubjectName = "CN=OpcTestClient",
// PFX를 직접 주입 — Directory Store 탐색 없이 이 인증서를 사용
Certificate = clientCertificate
},
TrustedPeerCertificates = new CertificateTrustList
{
StoreType = "Directory",
StorePath = Path.GetFullPath("pki/trusted")
},
TrustedIssuerCertificates = new CertificateTrustList
{
StoreType = "Directory",
StorePath = Path.GetFullPath("pki/issuers")
},
RejectedCertificateStore = new CertificateTrustList
{
StoreType = "Directory",
StorePath = Path.GetFullPath("pki/rejected")
},
AutoAcceptUntrustedCertificates = true,
RejectSHA1SignedCertificates = false,
AddAppCertToTrustedStore = true
},
TransportConfigurations = new TransportConfigurationCollection(),
TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 }
};
await config.ValidateAsync(ApplicationType.Client);
// 인증서 로드 확인 (Find 대신 직접 참조)
var cert = config.SecurityConfiguration.ApplicationCertificate.Certificate;
if (cert == null)
{
Console.WriteLine("❌ [Internal Error] 인증서 주입에 실패했습니다!");
return;
}
Console.WriteLine($"✅ 인증서 로드 성공: {cert.Subject}");
Console.WriteLine($" 유효기간: {cert.NotBefore:yyyy-MM-dd} ~ {cert.NotAfter:yyyy-MM-dd}");
Console.WriteLine($" 개인키 포함: {cert.HasPrivateKey}");
if (!cert.HasPrivateKey)
{
Console.WriteLine("❌ 개인키가 없습니다. PFX 파일 또는 KeyStorageFlags를 확인하세요.");
return;
}
Console.WriteLine("\nStep 1: Discovering endpoints...");
// 2. Endpoint 검색
var endpointConfig = EndpointConfiguration.Create(config);
endpointConfig.OperationTimeout = 10000;
var discoveryClient = await DiscoveryClient.CreateAsync(
new Uri(endpoint),
endpointConfig,
config,
DiagnosticsMasks.None,
CancellationToken.None);
var endpoints = await discoveryClient.GetEndpointsAsync(null);
discoveryClient.Dispose();
if (endpoints == null || endpoints.Count == 0)
{
Console.WriteLine("❌ No endpoints found!");
return;
}
Console.WriteLine($"✅ Found {endpoints.Count} endpoint(s)");
foreach (var ep in endpoints)
{
Console.WriteLine($" - {ep.EndpointUrl} ({ep.SecurityMode})");
}
Console.WriteLine("\nStep 2: Creating session...");
// 3. ConfiguredEndpoint 생성
var selectedEndpoint = endpoints[0];
var configuredEndpoint = new ConfiguredEndpoint(
null,
selectedEndpoint,
EndpointConfiguration.Create(config));
// 4. Session 생성
session = await Session.Create(
config,
configuredEndpoint,
false,
"OPC Test Session",
60000,
new UserIdentity(new AnonymousIdentityToken()),
null);
Console.WriteLine($"✅ Connected!");
Console.WriteLine($" Session ID: {session.SessionId}");
Console.WriteLine("\nStep 3: Reading server info...");
await ReadServerInfoAsync(session);
Console.WriteLine("\nStep 4: Checking redundancy...");
await CheckRedundancyAsync(session);
Console.WriteLine("\nStep 5: Browsing nodes...");
await BrowseNodesAsync(session);
Console.WriteLine("\n✅ All tests completed!");
}
catch (Exception ex)
{
Console.WriteLine($"\n❌ Error: {ex.Message}");
Console.WriteLine($"Type: {ex.GetType().Name}");
if (ex.InnerException != null)
{
Console.WriteLine($"Inner: {ex.InnerException.Message}");
}
}
finally
{
if (session != null)
{
try
{
Console.WriteLine("\nClosing session...");
await session.CloseAsync();
session.Dispose();
Console.WriteLine("✅ Session closed");
}
catch { }
}
}
Console.WriteLine("\nPress Enter to exit...");
Console.ReadLine();
}
static async Task ReadServerInfoAsync(ISession session)
{
try
{
// Server State
var state = await session.ReadValueAsync(
new NodeId(Variables.Server_ServerStatus_State));
Console.WriteLine($" State: {state.Value}");
// Server Time
var time = await session.ReadValueAsync(
new NodeId(Variables.Server_ServerStatus_CurrentTime));
Console.WriteLine($" Time: {time.Value}");
}
catch (Exception ex)
{
Console.WriteLine($" Error: {ex.Message}");
}
}
static async Task CheckRedundancyAsync(ISession session)
{
try
{
var serviceLevel = await session.ReadValueAsync(
new NodeId(Variables.Server_ServiceLevel));
byte level = Convert.ToByte(serviceLevel.Value);
Console.WriteLine($" Service Level: {level}");
if (level >= 200)
Console.WriteLine(" ✅ PRIMARY server");
else if (level > 0)
Console.WriteLine(" ⚠️ SECONDARY server");
else
Console.WriteLine(" STANDALONE");
}
catch (Exception ex)
{
Console.WriteLine($" Error: {ex.Message}");
}
}
static async Task BrowseNodesAsync(ISession session)
{
try
{
var browser = new Browser(session)
{
BrowseDirection = BrowseDirection.Forward,
ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences,
IncludeSubtypes = true,
NodeClassMask = (int)(NodeClass.Object | NodeClass.Variable)
};
var references = await browser.BrowseAsync(ObjectIds.ObjectsFolder);
Console.WriteLine($" Found {references.Count} top-level nodes:");
int count = 0;
foreach (var r in references)
{
Console.WriteLine($" {r.DisplayName} ({r.NodeClass})");
if (++count >= 5)
{
Console.WriteLine($" ... and {references.Count - 5} more");
break;
}
}
}
catch (Exception ex)
{
Console.WriteLine($" Error: {ex.Message}");
}
}
}
}

View File

@@ -0,0 +1,196 @@
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Configuration;
using System.Security.Cryptography.X509Certificates;
namespace OpcConnectionTest
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("=== Experion OPC UA Connection Test ===\n");
// 1. 기본 경로 및 설정 정보
string serverUrl = "opc.tcp://192.168.0.20:4840";
string basePkiPath = Path.GetFullPath("pki");
string pfxPath = Path.Combine(basePkiPath, "own/certs/OpcTestClient.pfx");
// 필수 폴더 자동 생성 (Issuer/Rejected 저장소 에러 방지)
Directory.CreateDirectory(Path.Combine(basePkiPath, "issuers/certs"));
Directory.CreateDirectory(Path.Combine(basePkiPath, "rejected/certs"));
Directory.CreateDirectory(Path.Combine(basePkiPath, "trusted/certs"));
Console.WriteLine($"Server: {serverUrl}");
Console.WriteLine($"PKI Root: {basePkiPath}\n");
ISession? session = null;
try
{
// 2. Application Configuration 설정
var config = new ApplicationConfiguration()
{
ApplicationName = "OpcTestClient",
ApplicationType = ApplicationType.Client,
ApplicationUri = "urn:OpcTestClient",
SecurityConfiguration = new SecurityConfiguration
{
ApplicationCertificate = new CertificateIdentifier
{
StoreType = "Directory",
StorePath = Path.Combine(basePkiPath, "own"),
SubjectName = "CN=OpcTestClient"
},
TrustedPeerCertificates = new CertificateTrustList
{
StoreType = "Directory",
StorePath = Path.Combine(basePkiPath, "trusted")
},
// 💡 중요: Issuer와 Rejected 경로를 명시해야 에러가 나지 않습니다.
TrustedIssuerCertificates = new CertificateTrustList
{
StoreType = "Directory",
StorePath = Path.Combine(basePkiPath, "issuers")
},
RejectedCertificateStore = new CertificateTrustList
{
StoreType = "Directory",
StorePath = Path.Combine(basePkiPath, "rejected")
},
AutoAcceptUntrustedCertificates = true,
RejectSHA1SignedCertificates = false,
AddAppCertToTrustedStore = true
},
TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 }
};
// 설정 검증
await config.ValidateAsync(ApplicationType.Client);
// 3. 인증서 수동 주입 (리눅스 PFX 인식 해결)
if (File.Exists(pfxPath))
{
X509Certificate2 clientCertificate = new X509Certificate2(pfxPath, "");
config.SecurityConfiguration.ApplicationCertificate.Certificate = clientCertificate;
Console.WriteLine($"✅ 인증서 수동 로드 성공: {clientCertificate.Subject}");
}
else
{
Console.WriteLine($"❌ PFX 파일을 찾을 수 없습니다! 경로 확인: {pfxPath}");
return;
}
// 4. Endpoint 검색
Console.WriteLine("\nStep 1: Discovering endpoints...");
var endpointConfig = EndpointConfiguration.Create(config);
using (var discoveryClient = DiscoveryClient.Create(new Uri(serverUrl), endpointConfig))
{
var endpoints = discoveryClient.GetEndpoints(null);
if (endpoints == null || endpoints.Count == 0)
{
Console.WriteLine("❌ No endpoints found!");
return;
}
Console.WriteLine($"✅ Found {endpoints.Count} endpoint(s)");
// 가장 높은 보안 수준의 엔드포인트 선택
var selectedEndpoint = endpoints.OrderByDescending(e => e.SecurityLevel).First();
Console.WriteLine($"👉 Selected: {selectedEndpoint.SecurityMode} | {selectedEndpoint.SecurityPolicyUri}");
// 5. Session 생성 (mngr 계정)
Console.WriteLine("\nStep 2: Creating session with mngr account...");
var token = new UserNameIdentityToken
{
UserName = "mngr",
DecryptedPassword = System.Text.Encoding.UTF8.GetBytes("mngr")
};
var userIdentity = new UserIdentity(token);
var configuredEndpoint = new ConfiguredEndpoint(null, selectedEndpoint, endpointConfig);
session = await Session.Create(
config,
configuredEndpoint,
updateBeforeConnect: true,
"OPC Test Session",
60000,
userIdentity,
null);
}
Console.WriteLine($"✅ Connected! Session ID: {session.SessionId}");
// 6. 데이터 읽기 및 브라우징 테스트
Console.WriteLine("\nStep 3: Reading server info...");
await ReadServerInfoAsync(session);
Console.WriteLine("\nStep 4: Checking redundancy...");
await CheckRedundancyAsync(session);
Console.WriteLine("\nStep 5: Browsing nodes...");
await BrowseNodesAsync(session);
Console.WriteLine("\n✅ All tests completed successfully!");
}
catch (Exception ex)
{
Console.WriteLine($"\n❌ Error: {ex.Message}");
if (ex.InnerException != null)
Console.WriteLine($" Inner: {ex.InnerException.Message}");
}
finally
{
if (session != null)
{
await session.CloseAsync();
Console.WriteLine("\nSession closed.");
}
}
Console.WriteLine("\nPress Enter to exit...");
Console.ReadLine();
}
// --- 헬퍼 메서드들 ---
static async Task ReadServerInfoAsync(ISession session)
{
var state = await session.ReadValueAsync(new NodeId(Variables.Server_ServerStatus_State));
var time = await session.ReadValueAsync(new NodeId(Variables.Server_ServerStatus_CurrentTime));
Console.WriteLine($" Server State: {state.Value}");
Console.WriteLine($" Server Time: {time.Value}");
}
static async Task CheckRedundancyAsync(ISession session)
{
var serviceLevel = await session.ReadValueAsync(new NodeId(Variables.Server_ServiceLevel));
byte level = Convert.ToByte(serviceLevel.Value);
string status = level >= 200 ? "PRIMARY" : (level > 0 ? "SECONDARY" : "STANDALONE");
Console.WriteLine($" Service Level: {level} ({status})");
}
static async Task BrowseNodesAsync(ISession session)
{
var browser = new Browser(session)
{
BrowseDirection = BrowseDirection.Forward,
ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences,
IncludeSubtypes = true,
NodeClassMask = (int)(NodeClass.Object | NodeClass.Variable)
};
var references = await browser.BrowseAsync(ObjectIds.ObjectsFolder);
Console.WriteLine($" Found {references.Count} nodes in Objects folder. Top 5:");
foreach (var r in references.Take(5))
{
Console.WriteLine($" - {r.DisplayName} ({r.NodeClass})");
}
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,442 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v8.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v8.0": {
"OpcConnectionTest/1.0.0": {
"dependencies": {
"OPCFoundation.NetStandard.Opc.Ua": "1.5.378.106",
"OPCFoundation.NetStandard.Opc.Ua.Client": "1.5.378.106"
},
"runtime": {
"OpcConnectionTest.dll": {}
}
},
"BitFaster.Caching/2.5.4": {
"runtime": {
"lib/net6.0/BitFaster.Caching.dll": {
"assemblyVersion": "2.5.4.0",
"fileVersion": "2.5.4.0"
}
}
},
"Microsoft.Extensions.DependencyInjection/10.0.2": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2"
},
"runtime": {
"lib/net8.0/Microsoft.Extensions.DependencyInjection.dll": {
"assemblyVersion": "10.0.0.0",
"fileVersion": "10.0.225.61305"
}
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.2": {
"runtime": {
"lib/net8.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
"assemblyVersion": "10.0.0.0",
"fileVersion": "10.0.225.61305"
}
}
},
"Microsoft.Extensions.Logging/10.0.2": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "10.0.2",
"Microsoft.Extensions.Logging.Abstractions": "10.0.2",
"Microsoft.Extensions.Options": "10.0.2"
},
"runtime": {
"lib/net8.0/Microsoft.Extensions.Logging.dll": {
"assemblyVersion": "10.0.0.0",
"fileVersion": "10.0.225.61305"
}
}
},
"Microsoft.Extensions.Logging.Abstractions/10.0.2": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2",
"System.Diagnostics.DiagnosticSource": "10.0.2"
},
"runtime": {
"lib/net8.0/Microsoft.Extensions.Logging.Abstractions.dll": {
"assemblyVersion": "10.0.0.0",
"fileVersion": "10.0.225.61305"
}
}
},
"Microsoft.Extensions.Options/10.0.2": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2",
"Microsoft.Extensions.Primitives": "10.0.2"
},
"runtime": {
"lib/net8.0/Microsoft.Extensions.Options.dll": {
"assemblyVersion": "10.0.0.0",
"fileVersion": "10.0.225.61305"
}
}
},
"Microsoft.Extensions.Primitives/10.0.2": {
"runtime": {
"lib/net8.0/Microsoft.Extensions.Primitives.dll": {
"assemblyVersion": "10.0.0.0",
"fileVersion": "10.0.225.61305"
}
}
},
"Newtonsoft.Json/13.0.4": {
"runtime": {
"lib/net6.0/Newtonsoft.Json.dll": {
"assemblyVersion": "13.0.0.0",
"fileVersion": "13.0.4.30916"
}
}
},
"OPCFoundation.NetStandard.Opc.Ua/1.5.378.106": {
"dependencies": {
"OPCFoundation.NetStandard.Opc.Ua.Client": "1.5.378.106",
"OPCFoundation.NetStandard.Opc.Ua.Configuration": "1.5.378.106",
"OPCFoundation.NetStandard.Opc.Ua.Core": "1.5.378.106",
"OPCFoundation.NetStandard.Opc.Ua.Gds.Client.Common": "1.5.378.106",
"OPCFoundation.NetStandard.Opc.Ua.Gds.Server.Common": "1.5.378.106",
"OPCFoundation.NetStandard.Opc.Ua.Security.Certificates": "1.5.378.106",
"OPCFoundation.NetStandard.Opc.Ua.Server": "1.5.378.106",
"OPCFoundation.NetStandard.Opc.Ua.Types": "1.5.378.106"
}
},
"OPCFoundation.NetStandard.Opc.Ua.Client/1.5.378.106": {
"dependencies": {
"BitFaster.Caching": "2.5.4",
"OPCFoundation.NetStandard.Opc.Ua.Configuration": "1.5.378.106",
"OPCFoundation.NetStandard.Opc.Ua.Core": "1.5.378.106"
},
"runtime": {
"lib/net8.0/Opc.Ua.Client.dll": {
"assemblyVersion": "1.5.378.0",
"fileVersion": "1.5.378.106"
}
}
},
"OPCFoundation.NetStandard.Opc.Ua.Configuration/1.5.378.106": {
"dependencies": {
"OPCFoundation.NetStandard.Opc.Ua.Core": "1.5.378.106"
},
"runtime": {
"lib/net8.0/Opc.Ua.Configuration.dll": {
"assemblyVersion": "1.5.378.0",
"fileVersion": "1.5.378.106"
}
}
},
"OPCFoundation.NetStandard.Opc.Ua.Core/1.5.378.106": {
"dependencies": {
"Microsoft.Extensions.Logging": "10.0.2",
"Newtonsoft.Json": "13.0.4",
"OPCFoundation.NetStandard.Opc.Ua.Security.Certificates": "1.5.378.106",
"OPCFoundation.NetStandard.Opc.Ua.Types": "1.5.378.106"
},
"runtime": {
"lib/net8.0/Opc.Ua.Core.dll": {
"assemblyVersion": "1.5.378.0",
"fileVersion": "1.5.378.106"
}
}
},
"OPCFoundation.NetStandard.Opc.Ua.Gds.Client.Common/1.5.378.106": {
"dependencies": {
"OPCFoundation.NetStandard.Opc.Ua.Client": "1.5.378.106",
"OPCFoundation.NetStandard.Opc.Ua.Core": "1.5.378.106"
},
"runtime": {
"lib/net8.0/Opc.Ua.Gds.Client.Common.dll": {
"assemblyVersion": "1.5.378.0",
"fileVersion": "1.5.378.106"
}
}
},
"OPCFoundation.NetStandard.Opc.Ua.Gds.Server.Common/1.5.378.106": {
"dependencies": {
"OPCFoundation.NetStandard.Opc.Ua.Core": "1.5.378.106",
"OPCFoundation.NetStandard.Opc.Ua.Security.Certificates": "1.5.378.106",
"OPCFoundation.NetStandard.Opc.Ua.Server": "1.5.378.106"
},
"runtime": {
"lib/net8.0/Opc.Ua.Gds.Server.Common.dll": {
"assemblyVersion": "1.5.378.0",
"fileVersion": "1.5.378.106"
}
}
},
"OPCFoundation.NetStandard.Opc.Ua.Security.Certificates/1.5.378.106": {
"dependencies": {
"OPCFoundation.NetStandard.Opc.Ua.Types": "1.5.378.106",
"System.Collections.Immutable": "10.0.2",
"System.Diagnostics.DiagnosticSource": "10.0.2",
"System.Formats.Asn1": "10.0.2",
"System.Text.Json": "10.0.2"
},
"runtime": {
"lib/net8.0/Opc.Ua.Security.Certificates.dll": {
"assemblyVersion": "1.5.378.0",
"fileVersion": "1.5.378.106"
}
}
},
"OPCFoundation.NetStandard.Opc.Ua.Server/1.5.378.106": {
"dependencies": {
"OPCFoundation.NetStandard.Opc.Ua.Configuration": "1.5.378.106",
"OPCFoundation.NetStandard.Opc.Ua.Core": "1.5.378.106"
},
"runtime": {
"lib/net8.0/Opc.Ua.Server.dll": {
"assemblyVersion": "1.5.378.0",
"fileVersion": "1.5.378.106"
}
}
},
"OPCFoundation.NetStandard.Opc.Ua.Types/1.5.378.106": {
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "10.0.2",
"System.Collections.Immutable": "10.0.2"
},
"runtime": {
"lib/net8.0/Opc.Ua.Types.dll": {
"assemblyVersion": "1.5.378.0",
"fileVersion": "1.5.378.106"
}
}
},
"System.Collections.Immutable/10.0.2": {
"runtime": {
"lib/net8.0/System.Collections.Immutable.dll": {
"assemblyVersion": "10.0.0.0",
"fileVersion": "10.0.225.61305"
}
}
},
"System.Diagnostics.DiagnosticSource/10.0.2": {
"runtime": {
"lib/net8.0/System.Diagnostics.DiagnosticSource.dll": {
"assemblyVersion": "10.0.0.0",
"fileVersion": "10.0.225.61305"
}
}
},
"System.Formats.Asn1/10.0.2": {
"runtime": {
"lib/net8.0/System.Formats.Asn1.dll": {
"assemblyVersion": "10.0.0.0",
"fileVersion": "10.0.225.61305"
}
}
},
"System.IO.Pipelines/10.0.2": {
"runtime": {
"lib/net8.0/System.IO.Pipelines.dll": {
"assemblyVersion": "10.0.0.0",
"fileVersion": "10.0.225.61305"
}
}
},
"System.Text.Encodings.Web/10.0.2": {
"runtime": {
"lib/net8.0/System.Text.Encodings.Web.dll": {
"assemblyVersion": "10.0.0.0",
"fileVersion": "10.0.225.61305"
}
},
"runtimeTargets": {
"runtimes/browser/lib/net8.0/System.Text.Encodings.Web.dll": {
"rid": "browser",
"assetType": "runtime",
"assemblyVersion": "10.0.0.0",
"fileVersion": "10.0.225.61305"
}
}
},
"System.Text.Json/10.0.2": {
"dependencies": {
"System.IO.Pipelines": "10.0.2",
"System.Text.Encodings.Web": "10.0.2"
},
"runtime": {
"lib/net8.0/System.Text.Json.dll": {
"assemblyVersion": "10.0.0.0",
"fileVersion": "10.0.225.61305"
}
}
}
}
},
"libraries": {
"OpcConnectionTest/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"BitFaster.Caching/2.5.4": {
"type": "package",
"serviceable": true,
"sha512": "sha512-1QroTY1PVCZOSG9FnkkCrmCKk/+bZCgI/YXq376HnYwUDJ4Ho0EaV4YaA/5v5WYLnwIwIO7RZkdWbg9pxIpueQ==",
"path": "bitfaster.caching/2.5.4",
"hashPath": "bitfaster.caching.2.5.4.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection/10.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-J/Zmp6fY93JbaiZ11ckWvcyxMPjD6XVwIHQXBjryTBgn7O6O20HYg9uVLFcZlNfgH78MnreE/7EH+hjfzn7VyA==",
"path": "microsoft.extensions.dependencyinjection/10.0.2",
"hashPath": "microsoft.extensions.dependencyinjection.10.0.2.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-zOIurr59+kUf9vNcsUkCvKWZv+fPosUZXURZesYkJCvl0EzTc9F7maAO4Cd2WEV7ZJJ0AZrFQvuH6Npph9wdBw==",
"path": "microsoft.extensions.dependencyinjection.abstractions/10.0.2",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.10.0.2.nupkg.sha512"
},
"Microsoft.Extensions.Logging/10.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-a0EWuBs6D3d7XMGroDXm+WsAi5CVVfjOJvyxurzWnuhBN9CO+1qHKcrKV1JK7H/T4ZtHIoVCOX/YyWM8K87qtw==",
"path": "microsoft.extensions.logging/10.0.2",
"hashPath": "microsoft.extensions.logging.10.0.2.nupkg.sha512"
},
"Microsoft.Extensions.Logging.Abstractions/10.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-RZkez/JjpnO+MZ6efKkSynN6ZztLpw3WbxNzjLCPBd97wWj1S9ZYPWi0nmT4kWBRa6atHsdM1ydGkUr8GudyDQ==",
"path": "microsoft.extensions.logging.abstractions/10.0.2",
"hashPath": "microsoft.extensions.logging.abstractions.10.0.2.nupkg.sha512"
},
"Microsoft.Extensions.Options/10.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-1De2LJjmxdqopI5AYC5dIhoZQ79AR5ayywxNF1rXrXFtKQfbQOV9+n/IsZBa7qWlr0MqoGpW8+OY2v/57udZOA==",
"path": "microsoft.extensions.options/10.0.2",
"hashPath": "microsoft.extensions.options.10.0.2.nupkg.sha512"
},
"Microsoft.Extensions.Primitives/10.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-QmSiO+oLBEooGgB3i0GRXyeYRDHjllqt3k365jwfZlYWhvSHA3UL2NEVV5m8aZa041eIlblo6KMI5txvTMpTwA==",
"path": "microsoft.extensions.primitives/10.0.2",
"hashPath": "microsoft.extensions.primitives.10.0.2.nupkg.sha512"
},
"Newtonsoft.Json/13.0.4": {
"type": "package",
"serviceable": true,
"sha512": "sha512-pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==",
"path": "newtonsoft.json/13.0.4",
"hashPath": "newtonsoft.json.13.0.4.nupkg.sha512"
},
"OPCFoundation.NetStandard.Opc.Ua/1.5.378.106": {
"type": "package",
"serviceable": true,
"sha512": "sha512-9s7VfRmWRp2W/mrBpCxUe8SCDQCWBPgMsdz9gy3Z19ZCfigukBQj5DoluPz1758lzZpnp/ZJ2eN4mhTNfprwUA==",
"path": "opcfoundation.netstandard.opc.ua/1.5.378.106",
"hashPath": "opcfoundation.netstandard.opc.ua.1.5.378.106.nupkg.sha512"
},
"OPCFoundation.NetStandard.Opc.Ua.Client/1.5.378.106": {
"type": "package",
"serviceable": true,
"sha512": "sha512-jawIdE5meywuxkwy1pQvyxnVhnjYKmZNYH9vWvRo9FTAUIrvtOMMnpsnS9IVBKJ+h+0U7S5R93uWqAOqhHOjrQ==",
"path": "opcfoundation.netstandard.opc.ua.client/1.5.378.106",
"hashPath": "opcfoundation.netstandard.opc.ua.client.1.5.378.106.nupkg.sha512"
},
"OPCFoundation.NetStandard.Opc.Ua.Configuration/1.5.378.106": {
"type": "package",
"serviceable": true,
"sha512": "sha512-XLduk2Y05TC1tND6mJON9g1IcX0B9MsxNPbTHM11jyoOgFFoz+GMqh1EoN0uYFHNGRA/hbSFf87nBB6sqm/6nw==",
"path": "opcfoundation.netstandard.opc.ua.configuration/1.5.378.106",
"hashPath": "opcfoundation.netstandard.opc.ua.configuration.1.5.378.106.nupkg.sha512"
},
"OPCFoundation.NetStandard.Opc.Ua.Core/1.5.378.106": {
"type": "package",
"serviceable": true,
"sha512": "sha512-bHcrfu60PGJWiU4YI2pn0rOpeZCtThHMRGMC9Wv5bP1Fk2zlxTz/QYOxR2Kj17bXmGlu0lUQA1iZDtDUxA1uXg==",
"path": "opcfoundation.netstandard.opc.ua.core/1.5.378.106",
"hashPath": "opcfoundation.netstandard.opc.ua.core.1.5.378.106.nupkg.sha512"
},
"OPCFoundation.NetStandard.Opc.Ua.Gds.Client.Common/1.5.378.106": {
"type": "package",
"serviceable": true,
"sha512": "sha512-O5QD1seSIh6G7wdVbS/FDpVG7T5Ih6zyBDNNFYzA0epfvKeaCOSicVosT+mQXDEqpg4vS6HDCQuJ0q3cB6KC/A==",
"path": "opcfoundation.netstandard.opc.ua.gds.client.common/1.5.378.106",
"hashPath": "opcfoundation.netstandard.opc.ua.gds.client.common.1.5.378.106.nupkg.sha512"
},
"OPCFoundation.NetStandard.Opc.Ua.Gds.Server.Common/1.5.378.106": {
"type": "package",
"serviceable": true,
"sha512": "sha512-CWPtco6KEGL/Av3tzgdcTv9QSkmeWwnvwuDG1PgEtP6Qpb9hHk2GteC2NA6AVvE4fgiCpZZl7s+ikeH2koeCLw==",
"path": "opcfoundation.netstandard.opc.ua.gds.server.common/1.5.378.106",
"hashPath": "opcfoundation.netstandard.opc.ua.gds.server.common.1.5.378.106.nupkg.sha512"
},
"OPCFoundation.NetStandard.Opc.Ua.Security.Certificates/1.5.378.106": {
"type": "package",
"serviceable": true,
"sha512": "sha512-vS6x1lRJtBgElpPyBZ0/rPfaw3r4gMK4TJwoGAGw4IhQg1rVzv370tIvgNlnfnbReBLs9W7N8FzDstq117Jagw==",
"path": "opcfoundation.netstandard.opc.ua.security.certificates/1.5.378.106",
"hashPath": "opcfoundation.netstandard.opc.ua.security.certificates.1.5.378.106.nupkg.sha512"
},
"OPCFoundation.NetStandard.Opc.Ua.Server/1.5.378.106": {
"type": "package",
"serviceable": true,
"sha512": "sha512-EprIRVCEcUBoxpUiTG26bImbo9meHEp8Cak4VzkPN3xX9UFZwbZh/rnwu1nmBngsH/QhuBS9QSrsjn0udpJLpw==",
"path": "opcfoundation.netstandard.opc.ua.server/1.5.378.106",
"hashPath": "opcfoundation.netstandard.opc.ua.server.1.5.378.106.nupkg.sha512"
},
"OPCFoundation.NetStandard.Opc.Ua.Types/1.5.378.106": {
"type": "package",
"serviceable": true,
"sha512": "sha512-9WPEMvW7zJSGvfN7vwjXqC9bCoF4WVm+SCk6IAvDz5fLZmnbb37o8mTrHfwsSkDNFWhal6Aszzre8AmNQnW9/g==",
"path": "opcfoundation.netstandard.opc.ua.types/1.5.378.106",
"hashPath": "opcfoundation.netstandard.opc.ua.types.1.5.378.106.nupkg.sha512"
},
"System.Collections.Immutable/10.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ty6kCaqyoh/M/jKV5yojgta+nf7T8zkkjpRI1beLQkrTC+9R6LS1eCEc+3ED2YUlRyA0g0PXExESnxnW3HcVVQ==",
"path": "system.collections.immutable/10.0.2",
"hashPath": "system.collections.immutable.10.0.2.nupkg.sha512"
},
"System.Diagnostics.DiagnosticSource/10.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-lYWBy8fKkJHaRcOuw30d67PrtVjR5754sz5Wl76s8P+vJ9FSThh9b7LIcTSODx1LY7NB3Srvg+JMnzd67qNZOw==",
"path": "system.diagnostics.diagnosticsource/10.0.2",
"hashPath": "system.diagnostics.diagnosticsource.10.0.2.nupkg.sha512"
},
"System.Formats.Asn1/10.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-9Hee6iQ74pDgdRNIL6lO8Vlx2onVWd5aDnqwk4Pvo0h5FklBd7QwcQDKgxeLuhiy0o3ibzUVO0KFQe0/hPXU+Q==",
"path": "system.formats.asn1/10.0.2",
"hashPath": "system.formats.asn1.10.0.2.nupkg.sha512"
},
"System.IO.Pipelines/10.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-EqMsn9r18ABvTDxrDce4OWDhBE3y+rR23ilG7Y3BudDKrDKrLG/hkD/JmeFZbctAPxSkCjyJ/Ddwbn/g7ufRJA==",
"path": "system.io.pipelines/10.0.2",
"hashPath": "system.io.pipelines.10.0.2.nupkg.sha512"
},
"System.Text.Encodings.Web/10.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Ro4cLT4qpRy64crfLAy3ekihtXckeXrD5eI6qb6NDSEVyHcHsmH7KgN4dbnIuiBmXIoaCslx4SynLYxag1SLSQ==",
"path": "system.text.encodings.web/10.0.2",
"hashPath": "system.text.encodings.web.10.0.2.nupkg.sha512"
},
"System.Text.Json/10.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-zy8ey7I16G9neZ6uzxrnYwS7pidElzN8XarsBjGu7lE2m7afTKMEe18KbY3ZSmh/z/bR40oxjd6hlUcmOEaMHw==",
"path": "system.text.json/10.0.2",
"hashPath": "system.text.json.10.0.2.nupkg.sha512"
}
}
}

View File

@@ -0,0 +1,12 @@
{
"runtimeOptions": {
"tfm": "net8.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "8.0.0"
},
"configProperties": {
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("OpcConnectionTest")] [assembly: System.Reflection.AssemblyCompanyAttribute("OpcConnectionTest")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+3181052619700dceeeef81a9a0851130498f177e")]
[assembly: System.Reflection.AssemblyProductAttribute("OpcConnectionTest")] [assembly: System.Reflection.AssemblyProductAttribute("OpcConnectionTest")]
[assembly: System.Reflection.AssemblyTitleAttribute("OpcConnectionTest")] [assembly: System.Reflection.AssemblyTitleAttribute("OpcConnectionTest")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -1 +1 @@
934b875e72b64b90c5572e45c51a754c2d824022123ba85dea23cfc05669d35b c2e941992237f396c84fee88bc09bdecf79f725598c8300136293f3ac50e6b79

View File

@@ -1,5 +1,39 @@
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/OpcConnectionTest
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/OpcConnectionTest.deps.json
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/OpcConnectionTest.runtimeconfig.json
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/OpcConnectionTest.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/OpcConnectionTest.pdb
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/BitFaster.Caching.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/Microsoft.Extensions.DependencyInjection.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/Microsoft.Extensions.Logging.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/Microsoft.Extensions.Logging.Abstractions.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/Microsoft.Extensions.Options.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/Microsoft.Extensions.Primitives.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/Newtonsoft.Json.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/Opc.Ua.Client.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/Opc.Ua.Configuration.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/Opc.Ua.Core.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/Opc.Ua.Gds.Client.Common.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/Opc.Ua.Gds.Server.Common.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/Opc.Ua.Security.Certificates.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/Opc.Ua.Server.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/Opc.Ua.Types.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/System.Collections.Immutable.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/System.Diagnostics.DiagnosticSource.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/System.Formats.Asn1.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/System.IO.Pipelines.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/System.Text.Encodings.Web.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/System.Text.Json.dll
/home/pacer/projects/OpcConnectionTest/bin/Debug/net8.0/runtimes/browser/lib/net8.0/System.Text.Encodings.Web.dll
/home/pacer/projects/OpcConnectionTest/obj/Debug/net8.0/OpcConnectionTest.csproj.AssemblyReference.cache /home/pacer/projects/OpcConnectionTest/obj/Debug/net8.0/OpcConnectionTest.csproj.AssemblyReference.cache
/home/pacer/projects/OpcConnectionTest/obj/Debug/net8.0/OpcConnectionTest.GeneratedMSBuildEditorConfig.editorconfig /home/pacer/projects/OpcConnectionTest/obj/Debug/net8.0/OpcConnectionTest.GeneratedMSBuildEditorConfig.editorconfig
/home/pacer/projects/OpcConnectionTest/obj/Debug/net8.0/OpcConnectionTest.AssemblyInfoInputs.cache /home/pacer/projects/OpcConnectionTest/obj/Debug/net8.0/OpcConnectionTest.AssemblyInfoInputs.cache
/home/pacer/projects/OpcConnectionTest/obj/Debug/net8.0/OpcConnectionTest.AssemblyInfo.cs /home/pacer/projects/OpcConnectionTest/obj/Debug/net8.0/OpcConnectionTest.AssemblyInfo.cs
/home/pacer/projects/OpcConnectionTest/obj/Debug/net8.0/OpcConnectionTest.csproj.CoreCompileInputs.cache /home/pacer/projects/OpcConnectionTest/obj/Debug/net8.0/OpcConnectionTest.csproj.CoreCompileInputs.cache
/home/pacer/projects/OpcConnectionTest/obj/Debug/net8.0/OpcConnectionTest.csproj.CopyComplete
/home/pacer/projects/OpcConnectionTest/obj/Debug/net8.0/OpcConnectionTest.dll
/home/pacer/projects/OpcConnectionTest/obj/Debug/net8.0/refint/OpcConnectionTest.dll
/home/pacer/projects/OpcConnectionTest/obj/Debug/net8.0/OpcConnectionTest.pdb
/home/pacer/projects/OpcConnectionTest/obj/Debug/net8.0/OpcConnectionTest.genruntimeconfig.cache
/home/pacer/projects/OpcConnectionTest/obj/Debug/net8.0/ref/OpcConnectionTest.dll

View File

@@ -0,0 +1 @@
985ad1f23ff5bf15b24f32d665951935d354064a61d300c10a97a85bb842ecaf

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpcUaMinimal", "OpcUaMinimal.csproj", "{0C38AAFC-1916-357A-13AE-47F9174B60F1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestService", "TestService\TestService.csproj", "{17DA166F-4680-60B2-477D-380118444B5C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0C38AAFC-1916-357A-13AE-47F9174B60F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0C38AAFC-1916-357A-13AE-47F9174B60F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0C38AAFC-1916-357A-13AE-47F9174B60F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0C38AAFC-1916-357A-13AE-47F9174B60F1}.Release|Any CPU.Build.0 = Release|Any CPU
{17DA166F-4680-60B2-477D-380118444B5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{17DA166F-4680-60B2-477D-380118444B5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17DA166F-4680-60B2-477D-380118444B5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{17DA166F-4680-60B2-477D-380118444B5C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {996D0811-5A91-4171-B40A-0692BA9BFEFB}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]

View File

@@ -0,0 +1,23 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: Microsoft.Extensions.Configuration.UserSecrets.UserSecretsIdAttribute("dotnet-TestService-32e4174a-e341-449d-b4e8-ed95b86df201")]
[assembly: System.Reflection.AssemblyCompanyAttribute("TestService")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+3181052619700dceeeef81a9a0851130498f177e")]
[assembly: System.Reflection.AssemblyProductAttribute("TestService")]
[assembly: System.Reflection.AssemblyTitleAttribute("TestService")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// MSBuild WriteCodeFragment 클래스에서 생성되었습니다.

View File

@@ -0,0 +1 @@
2a508951ac49d144820195cb52e9872d0988d5c3e9bce16356203f7d2df9d3e9

View File

@@ -0,0 +1,13 @@
is_global = true
build_property.TargetFramework = net8.0
build_property.TargetPlatformMinVersion =
build_property.UsingMicrosoftNETSdkWeb =
build_property.ProjectTypeGuids =
build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = TestService
build_property.ProjectDir = /home/pacer/projects/OpcUaMinimal/TestService/
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =

View File

@@ -0,0 +1,12 @@
// <auto-generated/>
global using global::Microsoft.Extensions.Configuration;
global using global::Microsoft.Extensions.DependencyInjection;
global using global::Microsoft.Extensions.Hosting;
global using global::Microsoft.Extensions.Logging;
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;

View File

@@ -13,10 +13,10 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("OpcUaMinimal")] [assembly: System.Reflection.AssemblyCompanyAttribute("OpcUaMinimal")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+3181052619700dceeeef81a9a0851130498f177e")]
[assembly: System.Reflection.AssemblyProductAttribute("OpcUaMinimal")] [assembly: System.Reflection.AssemblyProductAttribute("OpcUaMinimal")]
[assembly: System.Reflection.AssemblyTitleAttribute("OpcUaMinimal")] [assembly: System.Reflection.AssemblyTitleAttribute("OpcUaMinimal")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Generated by the MSBuild WriteCodeFragment class. // MSBuild WriteCodeFragment 클래스에서 생성되었습니다.

View File

@@ -1 +1 @@
a745e6c33a6ca21dfa6cf7051fa0dbadc3b703f8cbec423b33c723d2901630c9 a4b50e92c292747ea871d774c25954cdd987abe055be5b9e494c7acb67fa4145