362 lines
12 KiB
Markdown
362 lines
12 KiB
Markdown
# ExperionCrawler 프로젝트 분석 보고서 (Qwen 기반)
|
|
|
|
**작성일**: 2026-04-28
|
|
**분석 도구**: Qwen3-Coder-Next
|
|
**프로젝트 경로**: `/home/windpacer/projects/ExperionCrawler`
|
|
|
|
---
|
|
|
|
## 1. 개요
|
|
|
|
### 1.1 프로젝트 목표
|
|
Honeywell Experion OPC UA 서버를 위한 **웹 기반 데이터 수집 및 시계열 분석 도구**입니다.
|
|
OPC UA 프로토콜을 통해 실시간 데이터 수집, CSV 저장, TimescaleDB 이력 관리, 자연어 질의 처리까지 통합적으로 제공합니다.
|
|
|
|
### 1.2 기술 스택
|
|
|
|
| 계층 | 기술 | 용도 |
|
|
|------|------|------|
|
|
| **Backend** | .NET 8 (C#) | 웹 API, 백그라운드 서비스 |
|
|
| **Frontend** | Vanilla JS + Bootstrap | UI 구현 |
|
|
| **Database** | PostgreSQL + TimescaleDB | 시계열 데이터 저장 |
|
|
| **OPC UA** | opcua-sharp (Opc.Ua) | 실시간/히스토리 데이터 수집 |
|
|
| **MCP** | Python + Qwen3-Coder-Next | 자연어 → SQL 변환 (LLM 기반) |
|
|
|
|
---
|
|
|
|
## 2. 아키텍처
|
|
|
|
### 2.1 소스 구조 (Clean Architecture)
|
|
|
|
```
|
|
ExperionCrawler/
|
|
├── src/
|
|
│ ├── Core/ # 핵심 비즈니스 로직 (Domain, Application)
|
|
│ │ ├── Domain/
|
|
│ │ │ └── Entities/ # 엔티티 정의 (ExperionTag, ExperionRecord, RealtimePoint...)
|
|
│ │ └── Application/
|
|
│ │ ├── DTOs/ # 데이터 전송 객체
|
|
│ │ ├── Interfaces/ # 서비스 인터페이스 (DI 대상)
|
|
│ │ └── Services/ # 구현체 (TextToSqlService, KoreanTimeRangeExtractor...)
|
|
│ │
|
|
│ ├── Infrastructure/ # 기술적 구현 (OPC UA, DB, Mcp)
|
|
│ │ ├── Certificates/ # X.509 인증서 관리 (pki/ 디렉토리)
|
|
│ │ ├── Database/ # EF Core + TimescaleDB
|
|
│ │ ├── Csv/ # CSV 읽기/쓰기 (CsvHelper)
|
|
│ │ ├── OpcUa/ # OPC UA 클라이언트/서버 구현
|
|
│ │ └── Mcp/ # MCP 클라이언트 (Python 통신)
|
|
│ │
|
|
│ └── Web/ # ASP.NET Core 웹 프로젝트
|
|
│ ├── Controllers/ # API 컨트롤러
|
|
│ ├── Program.cs # DI, 미들웨어 구성
|
|
│ └── wwwroot/ # 정적 파일 (index.html, js/app.js, css/)
|
|
```
|
|
|
|
### 2.2 DI 컨테이너 등록 (`Program.cs`)
|
|
|
|
| Service | Lifetime | 구현체 |
|
|
|---------|----------|--------|
|
|
| `IExperionCertificateService` | Singleton | `ExperionCertificateService` |
|
|
| `IExperionStatusCodeService` | Singleton | `ExperionStatusCodeService` |
|
|
| `IOpcUaConfigProvider` | Singleton | `OpcUaConfigProvider` |
|
|
| `IExperionOpcClient` | Scoped | `ExperionOpcClient` |
|
|
| `IExperionCsvService` | Scoped | `ExperionCsvService` |
|
|
| `IExperionDbService` | Scoped | `ExperionDbService` |
|
|
| `ITextToSqlService` | Scoped | `TextToSqlService` |
|
|
| `IMcpService` | Singleton | `McpService` |
|
|
| `ExperionRealtimeService` | Singleton | 실시간 구독 (BackgroundService) |
|
|
| `ExperionHistoryService` | Singleton | 히스토리 구독 (BackgroundService) |
|
|
| `ExperionOpcServerService` | Singleton | OPC UA 서버 (BackgroundService) |
|
|
|
|
---
|
|
|
|
## 3. 핵심 기능
|
|
|
|
### 3.1 인증서 관리
|
|
|
|
| 기능 | 설명 |
|
|
|------|------|
|
|
| **생성** | `POST /api/certificate/create` → X.509 클라이언트 인증서 생성 |
|
|
| **상태 확인** | `GET /api/certificate/status?clientHostName=dbsvr` |
|
|
| **PKI 구조** | `pki/{own,trusted,issuers,rejected}/certs/` |
|
|
|
|
### 3.2 OPC UA 클라이언트
|
|
|
|
| 기능 | 설명 |
|
|
|------|------|
|
|
| **서버 접속** | `POST /api/connection/test` → 단일 태그 읽기, 노드 탐색 |
|
|
| **실시간 구독** | `ExperionRealtimeService` → Subscription 기반 콜백 |
|
|
| **히스토리 수집** | `ExperionHistoryService` → 주기적 Snapshot |
|
|
| **CSV 저장** | `ExperionCsvService` → `data/csv/` 디렉토리 |
|
|
| **DB 임포트** | `ExperionDbService` → `history_table` / `realtime_table` |
|
|
|
|
### 3.3 Text-to-SQL (자연어 → SQL)
|
|
|
|
| 기능 | 설명 |
|
|
|------|------|
|
|
| **자연어 파싱** | `POST /api/text-to-sql/parse` → `TextToSqlService.ParseNaturalLanguageAsync()` |
|
|
| **MCP 통합** | `POST /api/text-to-sql/query-nl` → LLM → SQL → 실행 |
|
|
| **도구 목록** | `GET /api/text-to-sql/tools` |
|
|
| **시계열 분석** | `POST /api/text-to-sql/analyze` → avg/max/min/추세 계산 |
|
|
| **간격 쿼리** | `POST /api/text-to-sql/query-history-interval` |
|
|
|
|
**시간 키워드 예시**:
|
|
- `"최근 1시간"`, `"최근 24시간"`, `"최근 7일"`, `"최근 1개월"`
|
|
- `"오늘"`, `"어제"`, `"오늘부터 ~ 까지"`, `"어제부터 ~ 까지"`
|
|
- `"오전 9시부터 오후 5시까지"`
|
|
|
|
### 3.4 MCP (Model Context Protocol)
|
|
|
|
| 기능 | 설명 |
|
|
|------|------|
|
|
| **Ping** | `GET /api/mcp/ping` → Python 서버 연결 확인 |
|
|
| **SQL 실행** | `POST /api/mcp/run-sql` → TimescaleDB 쿼리 |
|
|
| **PV 히스토리** | `POST /api/mcp/query-pv-history` → 태그명 + 시간 범위 |
|
|
| **태그 메타데이터** | `POST /api/mcp/get-tag-metadata` |
|
|
| **도면 목록** | `GET /api/mcp/list-drawings?unitNo=...` |
|
|
| **자연어 질의** | `POST /api/mcp/query-with-nl` |
|
|
|
|
### 3.5 OPC UA 서버
|
|
|
|
| 기능 | 설명 |
|
|
|------|------|
|
|
| **시작** | `POST /api/opcserver/start` → 자동 시작 플래그 저장 |
|
|
| **중지** | `POST /api/opcserver/stop` → 플래그 삭제 |
|
|
| **NodeManager** | `ExperionOpcServerNodeManager` → 커스텀 노드 매니저 |
|
|
|
|
---
|
|
|
|
## 4. 데이터베이스 스키마
|
|
|
|
### 4.1 테이블 정의
|
|
|
|
| 테이블명 | 용도 | 주요 컬럼 |
|
|
|----------|------|-----------|
|
|
| `raw_node_map` | 노드맵 원시 데이터 | `id, level, class, name, node_id, data_type` |
|
|
| `node_map_master` | 마스터 노드맵 | `id, level, class, name, node_id, data_type` |
|
|
| `realtime_table` | 실시간 포인트 | `id, tagname, node_id, livevalue, timestamp` |
|
|
| `history_table` | 시계열 이력 | `id, tagname, node_id, value, recorded_at` |
|
|
|
|
** TimescaleDB 확장 활성화: `CREATE EXTENSION IF NOT EXISTS timescaledb` **
|
|
|
|
---
|
|
|
|
## 5. API 엔드포인트
|
|
|
|
### 5.1 인증서
|
|
|
|
| 메서드 | 엔드포인트 | 기능 |
|
|
|--------|-----------|------|
|
|
| GET | `/api/certificate/status?clientHostName={name}` | 인증서 존재 여부 확인 |
|
|
| POST | `/api/certificate/create` | X.509 클라이언트 인증서 생성 |
|
|
|
|
### 5.2 연결
|
|
|
|
| 메서드 | 엔드포인트 | 기능 |
|
|
|--------|-----------|------|
|
|
| POST | `/api/connection/test` | 서버 접속 테스트, 단일 태그 읽기 |
|
|
| POST | `/api/connection/read` | nodeId 기반 읽기 |
|
|
| POST | `/api/connection/browse` | 노드 탐색 |
|
|
|
|
### 5.3 크롤링
|
|
|
|
| 메서드 | 엔드포인트 | 기능 |
|
|
|--------|-----------|------|
|
|
| POST | `/api/crawl/start` | 복수 노드 주기 수집 시작 |
|
|
| POST | `/api/crawl/stop` | 수집 중지 |
|
|
| POST | `/api/crawl/export` | CSV 다운로드 |
|
|
|
|
### 5.4 DB
|
|
|
|
| 메서드 | 엔드포인트 | 기능 |
|
|
|--------|-----------|------|
|
|
| GET | `/api/db/records?limit={n}&offset={m}` | 레코드 조회 |
|
|
| POST | `/api/db/import` | CSV 임포트 |
|
|
| POST | `/api/db/export` | CSV 다운로드 |
|
|
|
|
### 5.5 Text-to-SQL
|
|
|
|
| 메서드 | 엔드포인트 | 기능 |
|
|
|--------|-----------|------|
|
|
| POST | `/api/text-to-sql/parse` | 자연어 → SQL 변환 |
|
|
| POST | `/api/text-to-sql/execute` | SQL 실행 |
|
|
| POST | `/api/text-to-sql/suggest` | 쿼리 제안 |
|
|
| POST | `/api/text-to-sql/analyze` | 시계열 분석 |
|
|
| POST | `/api/text-to-sql/query-history-interval` | 사용자 지정 간격 조회 |
|
|
| POST | `/api/text-to-sql/query-nl` | MCP 통합 자연어 질의 |
|
|
| GET | `/api/text-to-sql/tools` | MCP 도구 목록 |
|
|
|
|
### 5.6 OPC UA 서버
|
|
|
|
| 메서드 | 엔드포인트 | 기능 |
|
|
|--------|-----------|------|
|
|
| POST | `/api/opcserver/start` | 서버 시작 |
|
|
| POST | `/api/opcserver/stop` | 서버 중지 |
|
|
| GET | `/api/opcserver/status` | 서버 상태 |
|
|
|
|
---
|
|
|
|
## 6. 주요 서비스 클래스
|
|
|
|
### 6.1 TextToSqlService
|
|
|
|
| 기능 | 메서드 |
|
|
|------|--------|
|
|
| 자연어 파싱 | `ParseNaturalLanguageAsync(string input)` |
|
|
| SQL 생성 | `BuildSqlFromNaturalLanguage(string input, out List<string> tagNames)` |
|
|
| 태그 매핑 | `GetMappingNodesAsync(List<string> tagNames)` |
|
|
| 시계열 분석 | `AnalyzeAsync(string sql)` |
|
|
| 시간 범위 추출 | `KoreanTimeRangeExtractor` 협업 |
|
|
|
|
### 6.2 ExperionOpcClient
|
|
|
|
| 기능 | 메서드 |
|
|
|------|--------|
|
|
| 단일 읽기 | `ReadAsync(string nodeId)` |
|
|
| 복수 읽기 | `ReadAsync(List<string> nodeIds)` |
|
|
| 노드 탐색 | `BrowseAsync(string nodeId)` |
|
|
| 연결 테스트 | `TestConnectionAsync(ExperionServerConfig cfg)` |
|
|
|
|
### 6.3 ExperionRealtimeService
|
|
|
|
| 기능 | 메서드 |
|
|
|------|--------|
|
|
| 시작 | `StartAsync(ExperionServerConfig cfg)` |
|
|
| 중지 | `StopAsync()` |
|
|
| 등록 | `SubscribeAsync(List<string> nodeIds)` |
|
|
| 해제 | `UnsubscribeAsync(List<string> nodeIds)` |
|
|
|
|
---
|
|
|
|
## 7. 설정 파일
|
|
|
|
### 7.1 appsettings.json
|
|
|
|
```json
|
|
{
|
|
"ConnectionStrings": {
|
|
"DefaultConnection": "Host=localhost;Port=5432;Database=iiot_platform;Username=postgres;Password=postgres",
|
|
"ExperionDbConnection": "Host=localhost;Port=5432;Database=postgres;Username=postgres;Password=postgres;Trust Server Certificate=true"
|
|
},
|
|
"OpcUaServer": {
|
|
"Port": 4841,
|
|
"EnableSecurity": false,
|
|
"AllowAnonymous": true,
|
|
"AllowedUsernames": ["mngr"],
|
|
"AllowedPasswords": ["mngr"]
|
|
}
|
|
}
|
|
```
|
|
|
|
### 7.2 자동 시작 플래그
|
|
|
|
| 파일 | 용도 |
|
|
|------|------|
|
|
| `realtime_autostart.json` | 실시간 구독 자동 시작 |
|
|
| `opcserver_autostart.json` | OPC UA 서버 자동 시작 |
|
|
|
|
---
|
|
|
|
## 8. 개발/배포
|
|
|
|
### 8.1 로컬 실행
|
|
|
|
```bash
|
|
cd src/Web
|
|
dotnet run
|
|
# → http://localhost:5000
|
|
```
|
|
|
|
### 8.2 Ubuntu 배포
|
|
|
|
```bash
|
|
git clone <repo> ExperionCrawler
|
|
cd ExperionCrawler
|
|
sudo bash deploy.sh
|
|
```
|
|
|
|
### 8.3 systemctl 관리
|
|
|
|
```bash
|
|
sudo systemctl status experioncrawler
|
|
sudo systemctl restart experioncrawler
|
|
sudo systemctl stop experioncrawler
|
|
sudo journalctl -u experioncrawler -f # 실시간 로그
|
|
```
|
|
|
|
---
|
|
|
|
## 9. 테스팅
|
|
|
|
### 9.1 단위 테스트 프로젝트
|
|
|
|
| 테스트 클래스 | 주요 테스트 항목 |
|
|
|---------------|----------------|
|
|
| `TextToSqlServiceTests.cs` | SQL 생성, 태그 매핑, 시간 범위 추출 |
|
|
| `SqlValidatorTests.cs` | SQL 인젝션 방지, 테이블 제한 |
|
|
| `KoreanTimeRangeExtractorTests.cs` | 한국어 시간 표현 파싱 |
|
|
|
|
### 9.2 테스트 명령어
|
|
|
|
```bash
|
|
dotnet test ExperionCrawler.Tests/
|
|
```
|
|
|
|
---
|
|
|
|
## 10. 주의사항
|
|
|
|
### 10.1 JSON 직렬화 정책
|
|
|
|
`Program.cs`에서 **PascalCase 유지** 설정:
|
|
|
|
```csharp
|
|
opt.JsonSerializerOptions.PropertyNamingPolicy = null; // camelCase로 변환하지 않음
|
|
```
|
|
|
|
**프론트엔드 대응**: app.js의 모든 API 응답은 소문자 키로 접근 (`res.id`, `res.tagName`).
|
|
|
|
### 10.2 인증서 경로
|
|
|
|
```bash
|
|
pki/own/certs/{clientHostName}.pfx # 클라이언트 인증서
|
|
pki/trusted/certs/ # 신뢰 피어
|
|
pki/issuers/certs/ # 신뢰 발급자 (필수)
|
|
pki/rejected/certs/ # 거부 인증서
|
|
```
|
|
|
|
### 10.3 TimescaleDB
|
|
|
|
- `history_table`은 TimescaleDB의 **Hypercube**로 자동 관리됨
|
|
- `recorded_at` 컬럼은 `TIMESTAMPTZ` 타입
|
|
|
|
---
|
|
|
|
## 11. 다음 개선 방향
|
|
|
|
| 항목 | 설명 |
|
|
|------|------|
|
|
| **realtime_table indexing** | `node_id` 유니크 인덱스만 있고 `tagname` 인덱스 추가 필요 |
|
|
| **CSV import 성능** | AssetLoader의 binary COPY 대신 EF Core Bulk Insert 고려 |
|
|
| **MCP error handling** | Python 서버 장애 시 fallback 처리 강화 |
|
|
| **UI/UX** | Bootstrap 5 업그레이드, 모바일 반응형 개선 |
|
|
| **OPC UA Security** | 현재 `AutoAcceptUntrustedCertificates=true` → 프로덕션 시 변경 필요 |
|
|
|
|
---
|
|
|
|
## 12. 관련 문서
|
|
|
|
| 문서 | 경로 | 설명 |
|
|
|------|------|------|
|
|
| CLAUDE.md | `CLAUDE.md` | Claude 작업 규칙 |
|
|
| .roo.md | `.roo.md` | Roo 작업 규칙 |
|
|
| task_state.md | `task_state.md` | Text-to-SQL 개발 로그 |
|
|
| issues.md | `issues.md` | 이슈 추적 |
|
|
| REVIEW_REQUEST.md | `REVIEW_REQUEST.md` | 코드 리뷰 요청 |
|
|
|
|
---
|
|
|
|
**분석 완료일**: 2026-04-28 23:11 (KST)
|
|
**분석 도구**: Qwen3-Coder-Next
|
|
**프로젝트 상태**: ✅ 활발한 개발 중 (Text-to-SQL + MCP 통합 완료)
|