using Microsoft.AspNetCore.Mvc; using OpcUaManager.Models; using OpcUaManager.Services; namespace OpcUaManager.Controllers; [ApiController] [Route("api/crawler")] [Produces("application/json")] public class CrawlerController : ControllerBase { private readonly OpcCrawlerService _crawlerSvc; private readonly OpcSessionService _sessionSvc; private readonly ILogger _logger; public CrawlerController( OpcCrawlerService crawlerSvc, OpcSessionService sessionSvc, ILogger logger) { _crawlerSvc = crawlerSvc; _sessionSvc = sessionSvc; _logger = logger; } /// /// 지정한 시작 노드부터 OPC UA 노드 트리를 재귀 탐색합니다. /// /// /// Sample request: /// /// POST /api/crawler/start /// { /// "startNodeId": "ns=1;s=$assetmodel", /// "maxDepth": 5 /// } /// /// 탐색 결과는 응답 JSON 과 함께 서버 로컬에 Honeywell_FullMap.csv 로 저장됩니다. /// [HttpPost("start")] [ProducesResponseType(typeof(CrawlResult), 200)] [ProducesResponseType(typeof(ApiError), 400)] public async Task Start([FromBody] CrawlRequest req) { if (!_sessionSvc.IsConnected) return BadRequest(new ApiError { Error = "세션 없음", Detail = "먼저 /api/session/connect 로 OPC 서버에 연결하세요." }); _logger.LogInformation("Crawler 시작: {NodeId} (depth={Depth})", req.StartNodeId, req.MaxDepth); // 대규모 탐사는 시간이 오래 걸릴 수 있으므로 타임아웃을 늘려줍니다 HttpContext.RequestAborted.ThrowIfCancellationRequested(); var result = await _crawlerSvc.CrawlAsync(req); return result.Success ? Ok(result) : BadRequest(new ApiError { Error = "탐사 실패", Detail = result.Message }); } /// 마지막 탐사로 생성된 CSV 파일을 다운로드합니다. [HttpGet("csv")] public IActionResult DownloadCsv() { string path = Path.GetFullPath("Honeywell_FullMap.csv"); if (!System.IO.File.Exists(path)) return NotFound(new ApiError { Error = "CSV 없음", Detail = "탐사를 먼저 실행하세요." }); byte[] bytes = System.IO.File.ReadAllBytes(path); return File(bytes, "text/csv", "Honeywell_FullMap.csv"); } }