분리후 첫 Crawling 성공 모델

This commit is contained in:
2026-02-22 22:59:21 +09:00
parent 171aaf6115
commit 4e006a5a5f
208 changed files with 613035 additions and 0 deletions

View File

@@ -0,0 +1,152 @@
using Microsoft.AspNetCore.Mvc;
using Npgsql;
using OpcPks.Core.Data;
using OpcPks.Core.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading.Tasks;
namespace OpcPks.Web.Controllers;
[Route("Engineering")] // 🚨 경로를 명시적으로 고정
public class EngineeringController : Controller
{
[HttpGet("TagExplorer")] // Engineering/TagExplorer
public IActionResult TagExplorer() => View();
[HttpGet("Admin")] // Engineering/Admin
public IActionResult Admin() => View();
[HttpPost("SearchByFilter")]
public async Task<IActionResult> SearchByFilter([FromBody] SearchRequest request)
{
var results = new List<object>();
if (request?.Suffixes == null || request.Suffixes.Count == 0) return Json(results);
using var conn = new NpgsqlConnection(DbConfig.ConnectionString);
await conn.OpenAsync();
var suffixConditions = string.Join(" OR ", request.Suffixes.Select((s, i) => $"node_id ILIKE @s{i}"));
var sql = $@"SELECT name, node_id, data_type FROM raw_node_map
WHERE name ILIKE @tagTerm AND ({suffixConditions})
ORDER BY name ASC LIMIT 1500";
using var cmd = new NpgsqlCommand(sql, conn);
cmd.Parameters.AddWithValue("tagTerm", $"%{request.TagTerm}%");
for (int i = 0; i < request.Suffixes.Count; i++)
cmd.Parameters.AddWithValue($"s{i}", $"%{request.Suffixes[i]}");
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
results.Add(new {
name = reader.GetString(0),
nodeId = reader.GetString(1),
dataType = reader.IsDBNull(2) ? "Double" : reader.GetString(2)
});
}
return Json(results);
}
[HttpPost("RegisterTags")]
public async Task<IActionResult> RegisterTags([FromBody] List<TagRegistrationRequest> tags)
{
if (tags == null || tags.Count == 0) return Ok();
using var conn = new NpgsqlConnection(DbConfig.ConnectionString);
await conn.OpenAsync();
using var trans = await conn.BeginTransactionAsync();
try {
var masterSql = @"INSERT INTO tag_master (server_name, area_code, tag_name, parameter, full_node_id, data_type)
VALUES (@server, @area, @tag, @param, @nodeId, @type)
ON CONFLICT (full_node_id) DO UPDATE SET data_type = EXCLUDED.data_type;";
var liveSql = @"INSERT INTO tag_live_data (full_node_id, live_value, quality)
VALUES (@nodeId, '0', 'Initial')
ON CONFLICT (full_node_id) DO NOTHING;";
foreach (var tag in tags) {
string sContent = tag.NodeId.Contains("s=") ? tag.NodeId.Split("s=")[1] : tag.NodeId;
string[] parts = sContent.Split(':');
string server = parts[0];
string area = parts.Length >= 3 ? parts[1] : "unassigned";
string remains = parts.Last();
int lastDot = remains.LastIndexOf('.');
string tagName = (lastDot != -1) ? remains.Substring(0, lastDot) : remains;
string param = (lastDot != -1) ? remains.Substring(lastDot + 1) : "pv";
using (var cmd = new NpgsqlCommand(masterSql, conn, trans)) {
cmd.Parameters.AddWithValue("server", server);
cmd.Parameters.AddWithValue("area", area);
cmd.Parameters.AddWithValue("tag", tagName);
cmd.Parameters.AddWithValue("param", param);
cmd.Parameters.AddWithValue("nodeId", tag.NodeId);
cmd.Parameters.AddWithValue("type", tag.DataType ?? "Double");
await cmd.ExecuteNonQueryAsync();
}
using (var cmd = new NpgsqlCommand(liveSql, conn, trans)) {
cmd.Parameters.AddWithValue("nodeId", tag.NodeId);
await cmd.ExecuteNonQueryAsync();
}
}
await trans.CommitAsync();
return Ok();
} catch (Exception ex) {
await trans.RollbackAsync();
return BadRequest(ex.Message);
}
}
[HttpPost("RunCrawler")]
public async Task<IActionResult> RunCrawler()
{
Console.WriteLine("\n[API] === RunCrawler 요청 수신됨 ===");
try
{
var sessionManager = new OpcSessionManager();
var session = await sessionManager.GetSessionAsync();
if (session == null || !session.Connected)
{
Console.WriteLine("❌ [API] 세션 연결 실패!");
return BadRequest(new { message = "하니웰 서버 연결 실패." });
}
var crawler = new HoneywellCrawler(session);
string csvPath = @"/home/pacer/projects/OpcPksPlatform/OpcPks.Core/Data/Honeywell_FullMap.csv";
string dir = Path.GetDirectoryName(csvPath);
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
await crawler.RunAsync("ns=1;s=$assetmodel", csvPath);
Console.WriteLine("✅ [API] 모든 탐사 공정 완료!");
return Ok(new { message = "탐사 및 CSV 생성 완료!" });
}
catch (Exception ex)
{
Console.WriteLine($"❌ [API] 치명적 오류: {ex.Message}");
return BadRequest(new { message = ex.Message });
}
}
[HttpPost("ImportCsv")]
public async Task<IActionResult> ImportCsv()
{
try {
using var conn = new NpgsqlConnection(DbConfig.ConnectionString);
await conn.OpenAsync();
var sql = @"TRUNCATE raw_node_map;
COPY raw_node_map(level, node_class, name, node_id)
FROM '/home/pacer/projects/OpcPksPlatform/OpcPks.Core/Data/Honeywell_FullMap.csv'
DELIMITER ',' CSV HEADER;";
using var cmd = new NpgsqlCommand(sql, conn);
await cmd.ExecuteNonQueryAsync();
return Ok(new { message = "DB 동기화 완료" });
}
catch (Exception ex) { return BadRequest(ex.Message); }
}
public class SearchRequest { public string TagTerm { get; set; } public List<string> Suffixes { get; set; } }
public class TagRegistrationRequest { public string TagName { get; set; } public string NodeId { get; set; } public string DataType { get; set; } }
}

View File

@@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace OpcPks.Web.Controllers;
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index() => View();
}