본문으로 건너뛰기

Bridge Discovery API

mDNS/DNS-SD 기반으로 네트워크의 Bridge.Host를 자동 검색하고 Engine에 바인딩하는 REST API입니다.

기본 정보​

항목값
Base URL (Engine)/api/discovery
Base URL (Bridge)/api/bridge
인증JWT 토큰 ([Authorize] 필수, Engine 측)
Content-Typeapplication/json

Engine 측 엔드포인트​

Engine/Admin에서 호출하여 Bridge를 검색·관리합니다.

엔드포인트 요약​

메서드경로설명
GET/api/discovery/scanmDNS 네트워크 스캔
POST/api/discovery/bindBridge 바인딩
DELETE/api/discovery/unbind/{bridgeId}Bridge 언바인딩
GET/api/discovery/bindings바인딩 목록 조회

GET /api/discovery/scan​

네트워크의 Bridge를 mDNS로 스캔합니다.

쿼리 매개변수:

매개변수타입기본값설명
timeoutSecondsint5스캔 시간 (1~30초)
excludeBoundbooltrue바인딩된 Bridge 제외

응답 (200 OK):

[
{
"bridgeId": "bridge-001",
"hostName": "caffeine-bridge.local",
"ipAddress": "192.168.1.100",
"port": 5100,
"version": "3.0.0",
"status": "Free",
"driverType": "ModbusTcp",
"tenantId": null,
"boundEngineId": null,
"tagCount": 24,
"discoveredAt": "2026-03-22T10:30:00Z",
"baseUrl": "http://192.168.1.100:5100"
}
]

POST /api/discovery/bind​

Bridge를 Engine에 바인딩합니다.

요청 바디:

{
"bridgeId": "bridge-001",
"bridgeUrl": "http://192.168.1.100:5100"
}

응답 (200 OK):

{
"binding": {
"bridgeId": "bridge-001",
"ipAddress": "192.168.1.100",
"port": 5100,
"driverType": "ModbusTcp",
"bindingToken": "abc123...",
"boundAt": "2026-03-22T10:31:00Z",
"isOnline": true
},
"manifest": {
"bridgeId": "bridge-001",
"driverType": "ModbusTcp",
"driverId": "modbus-line1",
"tags": [],
"availableSubmodels": ["OperationalData", "Nameplate", "TechnicalData"],
"bindingToken": "abc123..."
}
}

오류 응답:

코드설명
400bridgeId 또는 bridgeUrl 누락
409이미 바인딩된 Bridge
502Bridge 연결 실패

DELETE /api/discovery/unbind/{bridgeId}​

Bridge 바인딩을 해제합니다.

경로 매개변수: bridgeId — 언바인딩할 Bridge 식별자

응답: 204 No Content

오류 응답: 404 — 바인딩되지 않은 Bridge

GET /api/discovery/bindings​

현재 바인딩된 모든 Bridge 목록을 조회합니다.

응답 (200 OK):

[
{
"bridgeId": "bridge-001",
"ipAddress": "192.168.1.100",
"port": 5100,
"driverType": "ModbusTcp",
"bindingToken": "abc123...",
"boundAt": "2026-03-22T10:31:00Z",
"lastHeartbeatAt": "2026-03-22T10:35:00Z",
"isOnline": true,
"activeSubmodels": "[\"OperationalData\",\"Nameplate\"]",
"baseUrl": "http://192.168.1.100:5100"
}
]

Bridge 측 엔드포인트​

Bridge.Host에서 제공하는 바인딩/상태 API입니다. Engine이 프록시 호출합니다.

엔드포인트 요약​

메서드경로설명
POST/api/bridge/bind바인딩 수락 (매니페스트 반환)
DELETE/api/bridge/bind바인딩 해제
GET/api/bridge/statusBridge 상태 조회
GET/api/bridge/manifestBridge 매니페스트 조회

GET /api/bridge/status​

Bridge의 현재 상태를 조회합니다. Heartbeat 모니터링에 사용됩니다.

응답 (200 OK):

{
"bridgeId": "bridge-001",
"status": "bound",
"boundEngineId": "engine-001",
"driverType": "ModbusTcp",
"driverId": "modbus-line1",
"version": "3.0.0"
}

GET /api/bridge/manifest​

Bridge가 제공하는 태그/서브모델 매니페스트를 조회합니다.

응답 (200 OK):

{
"bridgeId": "bridge-001",
"driverType": "ModbusTcp",
"driverId": "modbus-line1",
"tags": [
{ "name": "Temperature", "address": "D100", "dataType": "Int16", "length": 2, "scanInterval": 1000 }
],
"availableSubmodels": ["OperationalData", "Nameplate", "TechnicalData"]
}

핵심 인터페이스​

IBridgeDiscovery​

mDNS/DNS-SD 기반 Bridge 검색 인터페이스입니다.

public interface IBridgeDiscovery
{
IObservable<BridgeInfo> DiscoveredBridges { get; }
Task<IReadOnlyList<BridgeInfo>> ScanAsync(TimeSpan timeout, CancellationToken ct = default);
}
멤버타입설명
DiscoveredBridgesIObservable<BridgeInfo>실시간 Bridge 발견 스트림
ScanAsyncTask<IReadOnlyList<BridgeInfo>>지정 시간 동안 네트워크 스캔

IBridgeAdvertiser​

Bridge.Host가 자신을 mDNS로 광고하는 인터페이스입니다.

public interface IBridgeAdvertiser : IAsyncDisposable
{
bool IsAdvertising { get; }
Task StartAdvertisingAsync(BridgeAdvertisement advertisement, CancellationToken ct = default);
Task StopAdvertisingAsync(CancellationToken ct = default);
Task UpdateStatusAsync(BridgeBindingStatus status, string? boundEngineId = null, CancellationToken ct = default);

// v2.3.0 (FIR-038): 순수 추상 메서드 — 구현체는 diff 비교로 최소 단절 보장
Task UpdateAdvertisementAsync(BridgeAdvertisement advertisement, CancellationToken ct = default);
}
메서드용도
StartAdvertisingAsyncmDNS 광고 시작
StopAdvertisingAsyncmDNS 광고 중지
UpdateStatusAsync바인딩 상태(Free/Bound) 변경 시 TXT 갱신
UpdateAdvertisementAsync광고 메타데이터 갱신. Identity 필드 불변 시 TXT diff만 적용, 변경 시 재등록
v2.3.0 변경 사항 (FIR-038)

UpdateAdvertisementAsync가 Default Interface Method에서 순수 추상 메서드로 변경되었습니다. 커스텀 IBridgeAdvertiser 구현체가 있다면 이 메서드를 반드시 구현해야 합니다. 광고가 불필요한 환경(테스트, 단독 실행)에서는 NullBridgeAdvertiser를 DI에 등록하세요.

BridgeAdvertisement 갱신 예시 (record with 표현식)

// Updatable 필드만 변경 — 재등록 없이 TXT 레코드만 갱신
var updated = current with { FriendlyName = "Conveyor Line B", Location = "Zone-3" };
await _advertiser.UpdateAdvertisementAsync(updated, ct);

IBridgeBindingRepository​

Bridge 바인딩 상태를 영속화하는 리포지토리 인터페이스입니다.

public interface IBridgeBindingRepository
{
Task<BridgeBindingState?> GetByBridgeIdAsync(string bridgeId, CancellationToken ct = default);
Task SaveAsync(BridgeBindingState binding, CancellationToken ct = default);
Task DeleteAsync(string bridgeId, CancellationToken ct = default);
Task<IReadOnlyList<BridgeBindingState>> ListAsync(CancellationToken ct = default);
Task UpdateHeartbeatAsync(string bridgeId, DateTimeOffset timestamp, CancellationToken ct = default);
}

값 객체​

BridgeInfo​

속성타입설명
BridgeIdstringBridge UUID
HostNamestring호스트명
IpAddressIPAddressIP 주소
PortintREST API 포트
Versionstring소프트웨어 버전
StatusBridgeBindingStatus바인딩 상태 (Free/Binding/Bound)
DriverTypestring드라이버 유형
TagCountint등록된 태그 수
BaseUrlstringREST API 기본 URL

BridgeManifest​

속성타입설명
BridgeIdstringBridge UUID
DriverTypestring드라이버 유형
DriverIdstring드라이버 식별자
TagsIReadOnlyList<ManifestTag>사용 가능한 태그 목록
AvailableSubmodelsIReadOnlyList<string>사용 가능한 서브모델 유형
BindingTokenstring?1회용 바인딩 토큰

BridgeBindingStatus (열거형)​

값설명
Free바인딩되지 않음 (검색 가능)
Binding바인딩 진행 중
BoundEngine에 바인딩됨

mDNS 서비스 타입​

서비스 타입: _caffeine-bridge._tcp

Bridge.Host가 시작되면 이 서비스 타입으로 mDNS 광고를 시작합니다. Engine의 IBridgeDiscovery가 이 서비스 타입을 스캔하여 Bridge를 발견합니다.


바인딩 워크플로우​

1. Engine → ScanAsync() → mDNS 네트워크 스캔
2. Engine → 사용자가 Bridge 선택
3. Engine → POST /api/bridge/bind → Bridge.Host
4. Bridge.Host → 바인딩 토큰 생성 + 매니페스트 반환
5. Engine → IBridgeBindingRepository.SaveAsync() → 바인딩 상태 영속화
6. Engine → BridgeHeartbeatWorker → 30초 간격 헬스 체크 시작

최종 업데이트: 2026-03-22