145 lines
5.6 KiB
C#
145 lines
5.6 KiB
C#
using Opc.Ua;
|
|
using Opc.Ua.Client;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
using OpcPks.Core.Models;
|
|
|
|
namespace OpcPks.Core.Services
|
|
{
|
|
public class HoneywellCrawler
|
|
{
|
|
// ✅ Session 대신 ISession 인터페이스를 사용하여 호환성 확보
|
|
private readonly ISession _session;
|
|
private readonly List<TagMaster> _discoveredTags = new();
|
|
|
|
public HoneywellCrawler(ISession session)
|
|
{
|
|
_session = session ?? throw new ArgumentNullException(nameof(session));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 탐사 시작점
|
|
/// </summary>
|
|
public async Task RunAsync(string startNodeId, string csvPath)
|
|
{
|
|
Console.WriteLine($"\n🚀 하니웰 자산모델 탐사 시작: {startNodeId}");
|
|
|
|
try
|
|
{
|
|
_discoveredTags.Clear();
|
|
NodeId rootNode = NodeId.Parse(startNodeId);
|
|
await BrowseRecursiveAsync(rootNode, 0);
|
|
|
|
Console.WriteLine("\n===============================================");
|
|
Console.WriteLine($"✅ 탐사 완료! 총 {_discoveredTags.Count}개의 항목 발견.");
|
|
Console.WriteLine("===============================================");
|
|
|
|
SaveToCsv(csvPath);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"❌ 탐사 중 오류: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private async Task BrowseRecursiveAsync(NodeId nodeId, int level)
|
|
{
|
|
try
|
|
{
|
|
BrowseDescription description = new() {
|
|
NodeId = nodeId,
|
|
BrowseDirection = BrowseDirection.Forward,
|
|
ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences,
|
|
IncludeSubtypes = true,
|
|
NodeClassMask = (uint)(NodeClass.Variable | NodeClass.Object),
|
|
ResultMask = (uint)BrowseResultMask.All
|
|
};
|
|
|
|
// BrowseAsync 호출
|
|
BrowseResponse response = await _session.BrowseAsync(null, null, 0, new BrowseDescriptionCollection { description }, default);
|
|
|
|
if (response?.Results == null || response.Results.Count == 0) return;
|
|
|
|
foreach (var result in response.Results)
|
|
{
|
|
await ProcessReferencesAsync(result.References, level);
|
|
|
|
byte[] cp = result.ContinuationPoint;
|
|
while (cp != null && cp.Length > 0)
|
|
{
|
|
// ✅ ByteStringCollection을 사용하여 라이브러리 표준 인자 타입 일치화
|
|
var cpCollection = new ByteStringCollection { cp };
|
|
BrowseNextResponse nextResponse = await _session.BrowseNextAsync(null, false, cpCollection, default);
|
|
|
|
if (nextResponse?.Results != null && nextResponse.Results.Count > 0)
|
|
{
|
|
await ProcessReferencesAsync(nextResponse.Results[0].References, level);
|
|
cp = nextResponse.Results[0].ContinuationPoint;
|
|
}
|
|
else
|
|
{
|
|
cp = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception) { /* 접근 권한 없는 노드는 무시 */ }
|
|
}
|
|
|
|
private async Task ProcessReferencesAsync(ReferenceDescriptionCollection references, int level)
|
|
{
|
|
if (references == null || references.Count == 0) return;
|
|
|
|
foreach (var rd in references)
|
|
{
|
|
// ExpandedNodeId -> NodeId 변환
|
|
NodeId childId = ExpandedNodeId.ToNodeId(rd.NodeId, _session.NamespaceUris);
|
|
|
|
// TagMaster 모델 규격에 맞춰 데이터 추가
|
|
_discoveredTags.Add(new TagMaster
|
|
{
|
|
TagName = rd.BrowseName.Name ?? "Unknown",
|
|
FullNodeId = childId.ToString(),
|
|
NodeClass = rd.NodeClass.ToString(),
|
|
Level = level
|
|
});
|
|
|
|
string indent = new string(' ', level * 2);
|
|
Console.WriteLine($"{indent} [{rd.NodeClass}] {rd.BrowseName.Name} (ID: {childId})");
|
|
|
|
// 하위 폴더(Object)가 있고 탐사 깊이가 5단계 미만이면 재귀 탐색
|
|
if (rd.NodeClass == NodeClass.Object && level < 5)
|
|
{
|
|
await BrowseRecursiveAsync(childId, level + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SaveToCsv(string filePath)
|
|
{
|
|
try
|
|
{
|
|
// 디렉토리가 없으면 생성
|
|
string dir = Path.GetDirectoryName(filePath);
|
|
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
|
|
{
|
|
Directory.CreateDirectory(dir);
|
|
}
|
|
|
|
using StreamWriter sw = new(filePath);
|
|
sw.WriteLine("Level,Class,Name,NodeId");
|
|
foreach (var tag in _discoveredTags)
|
|
{
|
|
sw.WriteLine($"{tag.Level},{tag.NodeClass},{tag.TagName},{tag.FullNodeId}");
|
|
}
|
|
Console.WriteLine($"💾 CSV 저장 완료: {filePath}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"❌ CSV 저장 실패: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
} |