본문으로 건너뛰기

드라이버 관리 API

개요​

드라이버 관리 API는 Caffeine Bridge에 로드된 드라이버(플러그인)의 목록을 조회하고, 상태를 확인하며, 재시작하는 기능을 제공합니다. SecsGem, Mitsubishi, Simulation 등 다양한 프로토콜 드라이버를 관리할 수 있습니다.

Bridge.Host와 Engine API 구분

드라이버 API는 두 서비스에서 각각 다른 경로로 제공됩니다.

  • Engine (/api/v1/drivers, /api/v1.0/drivers): 드라이버 목록/상태 조회, 재시작, 설정 관리
  • Bridge.Host (/api/bridge/*): 브릿지 상태, 매니페스트, 드라이버·태그 설정, 라이선스

아래 gRPC/REST 엔드포인트는 Engine 서비스 기준입니다. Bridge.Host API는 별도 섹션을 참고하세요.

기본 정보​

항목값
서비스CaffeineApi
엔드포인트Engine gRPC 포트 (기본: 5001)
인증JWT 토큰 필요
권한RestartDriver는 Admin 역할 필요
버전v1

엔드포인트 요약​

메서드경로설명권한
GET/api/v1/drivers드라이버 목록 조회User
GET/api/v1/drivers/{driver_name}/status드라이버 상태 조회User
POST/api/v1/drivers/{driver_name}/restart드라이버 재시작Admin

상세 엔드포인트​

GetDrivers​

등록된 드라이버 목록을 조회합니다.

gRPC 메서드

rpc GetDrivers (GetDriversRequest) returns (GetDriversResponse)

요청

파라미터타입필수설명
type_filterstringN드라이버 타입 필터 (예: "SecsGem")

응답

{
"drivers": [
{
"name": "Sim-PLC-01",
"type": "Simulation",
"status": "Running",
"startedAt": "2026-02-17T09:00:00Z",
"tagCount": 523,
"metadata": {
"version": "1.0.0",
"protocol": "Modbus TCP"
}
},
{
"name": "SECS-Equipment-01",
"type": "SecsGem",
"status": "Running",
"startedAt": "2026-02-17T09:00:00Z",
"tagCount": 1247,
"metadata": {
"deviceId": "1",
"port": "5000"
}
}
]
}

응답 코드

코드설명
OK성공
UNAUTHENTICATED인증 필요

REST 경로

GET /api/v1/drivers?typeFilter=SecsGem

GetDriverStatus​

특정 드라이버의 상세 상태를 조회합니다.

gRPC 메서드

rpc GetDriverStatus (GetDriverStatusRequest) returns (GetDriverStatusResponse)

요청

파라미터타입필수설명
driver_namestringY드라이버 이름

응답

{
"driver": {
"name": "Sim-PLC-01",
"type": "Simulation",
"status": "Running",
"startedAt": "2026-02-17T09:00:00Z",
"tagCount": 523,
"metadata": {
"version": "1.0.0"
}
},
"extendedInfo": {
"connectionState": "Connected",
"lastCommunication": "2026-02-17T10:30:00Z",
"errorCount": "0",
"averageLatency": "5ms",
"lastError": null
}
}

응답 코드

코드설명
OK성공
NOT_FOUND드라이버를 찾을 수 없음
UNAUTHENTICATED인증 필요

REST 경로

GET /api/v1/drivers/{driver_name}/status

RestartDriver​

드라이버를 재시작합니다. (Admin 권한 필요)

gRPC 메서드

rpc RestartDriver (RestartDriverRequest) returns (RestartDriverResponse)

요청

파라미터타입필수설명
driver_namestringY드라이버 이름

응답

{
"success": true,
"message": "Driver 'Sim-PLC-01' restarted successfully"
}

응답 코드

코드설명
OK성공
NOT_FOUND드라이버를 찾을 수 없음
PERMISSION_DENIEDAdmin 권한 필요
FAILED_PRECONDITION재시작 실패 (예: 드라이버 오류)

REST 경로

POST /api/v1/drivers/{driver_name}/restart

데이터 타입​

DriverInfo 구조​

필드타입설명
namestring드라이버 이름 (고유 식별자)
typestring드라이버 타입
statusstring상태 ("Running", "Stopped", "Error")
started_atTimestamp시작 시각
tag_countint32등록된 태그 수
metadatamap<string, string>추가 메타데이터

Driver Type 값​

값설명
SecsGemSEMI SECS/GEM 프로토콜
Simulation시뮬레이션 드라이버
Mitsubishi미쓰비시 PLC
Omron오므론 PLC
LseLS Electric (구 LS산전)
ModbusTcp (v2.0.5)Modbus TCP 드라이버 (FluentModbus 기반)
ModbusRtu (v2.0.5)Modbus RTU 드라이버 — RS-485 시리얼 (FluentModbus + System.IO.Ports)

Status 값​

값설명
Running정상 동작 중
Stopped정지 상태
Error오류 상태

Extended Info 필드​

GetDriverStatus의 extendedInfo 맵에 포함될 수 있는 필드:

키설명예시 값
connectionState연결 상태"Connected", "Disconnected"
lastCommunication마지막 통신 시각"2026-02-17T10:30:00Z"
errorCount누적 에러 횟수"0", "5"
averageLatency평균 응답 시간"5ms", "12ms"
lastError마지막 에러 메시지null, "Connection timeout"
ipAddress장비 IP 주소"192.168.1.100"
port통신 포트"5000"

사용 예제​

C# (CaffeineClient)​

using Caffeine.Client;

var client = new CaffeineClient();
await client.ConnectAsync("https://localhost:5001");

// 드라이버 목록 조회
var driversResponse = await client.GetDriversAsync();
foreach (var driver in driversResponse.Drivers)
{
Console.WriteLine($"{driver.Name} ({driver.Type}): {driver.Status} - {driver.TagCount} tags");
}

// 특정 타입 필터
var secsDrivers = await client.GetDriversAsync(typeFilter: "SecsGem");

// 드라이버 상태 조회
var statusResponse = await client.GetDriverStatusAsync("Sim-PLC-01");
Console.WriteLine($"Status: {statusResponse.Driver.Status}");
Console.WriteLine($"Connection: {statusResponse.ExtendedInfo["connectionState"]}");
Console.WriteLine($"Last Communication: {statusResponse.ExtendedInfo["lastCommunication"]}");

// 드라이버 재시작 (Admin 권한 필요)
var restartResponse = await client.RestartDriverAsync("Sim-PLC-01");
if (restartResponse.Success)
{
Console.WriteLine($"Restarted: {restartResponse.Message}");
}

await client.DisposeAsync();

C# (직접 gRPC 호출)​

using Grpc.Net.Client;
using Caffeine.IPC.Grpc;

var channel = GrpcChannel.ForAddress("https://localhost:5001");
var grpcClient = new CaffeineApi.CaffeineApiClient(channel);

// 드라이버 목록 조회
var driversResponse = await grpcClient.GetDriversAsync(new GetDriversRequest
{
TypeFilter = "SecsGem"
});

foreach (var driver in driversResponse.Drivers)
{
Console.WriteLine($"{driver.Name}: {driver.Status} ({driver.TagCount} tags)");
}

// 드라이버 상태 조회
var statusResponse = await grpcClient.GetDriverStatusAsync(new GetDriverStatusRequest
{
DriverName = "Sim-PLC-01"
});

Console.WriteLine($"Driver: {statusResponse.Driver.Name}");
Console.WriteLine($"Status: {statusResponse.Driver.Status}");
Console.WriteLine($"Extended Info:");
foreach (var kvp in statusResponse.ExtendedInfo)
{
Console.WriteLine($" {kvp.Key}: {kvp.Value}");
}

// 드라이버 재시작 (Admin JWT 필요)
var headers = new Metadata
{
{ "Authorization", $"Bearer {adminJwtToken}" }
};

var restartResponse = await grpcClient.RestartDriverAsync(
new RestartDriverRequest { DriverName = "Sim-PLC-01" },
headers
);

Console.WriteLine($"Restart: {restartResponse.Success}, {restartResponse.Message}");

curl (REST API)​

드라이버 목록 조회​

curl -X GET "https://localhost:5001/api/v1/drivers" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"

응답

{
"drivers": [
{
"name": "Sim-PLC-01",
"type": "Simulation",
"status": "Running",
"startedAt": "2026-02-17T09:00:00.000Z",
"tagCount": 523,
"metadata": {
"version": "1.0.0"
}
}
]
}

특정 타입 필터​

curl -X GET "https://localhost:5001/api/v1/drivers?typeFilter=SecsGem" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"

드라이버 상태 조회​

curl -X GET "https://localhost:5001/api/v1/drivers/Sim-PLC-01/status" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"

응답

{
"driver": {
"name": "Sim-PLC-01",
"type": "Simulation",
"status": "Running",
"startedAt": "2026-02-17T09:00:00.000Z",
"tagCount": 523,
"metadata": {
"version": "1.0.0"
}
},
"extendedInfo": {
"connectionState": "Connected",
"lastCommunication": "2026-02-17T10:30:00Z",
"errorCount": "0",
"averageLatency": "5ms"
}
}

드라이버 재시작​

curl -X POST "https://localhost:5001/api/v1/drivers/Sim-PLC-01/restart" \
-H "Authorization: Bearer ADMIN_JWT_TOKEN"

응답

{
"success": true,
"message": "Driver 'Sim-PLC-01' restarted successfully"
}

에러 코드​

코드설명HTTP 상태
DRIVER_NOT_FOUND드라이버를 찾을 수 없음404
PERMISSION_DENIEDAdmin 권한 필요403
RESTART_FAILED드라이버 재시작 실패500
DRIVER_ALREADY_STOPPED이미 정지된 드라이버409

드라이버 상태 모니터링​

상태 확인 주기​

프로덕션 환경에서는 주기적으로 드라이버 상태를 확인하는 것이 권장됩니다.

using System.Timers;

var timer = new Timer(5000); // 5초마다
timer.Elapsed += async (sender, e) =>
{
var statusResponse = await client.GetDriverStatusAsync("Sim-PLC-01");
if (statusResponse.Driver.Status != "Running")
{
Console.WriteLine($"⚠️ Driver {statusResponse.Driver.Name} is {statusResponse.Driver.Status}");
// 알림 발송 또는 자동 재시작 로직
}
};
timer.Start();

Health Check 통합​

ASP.NET Core Health Check에 드라이버 상태를 통합할 수 있습니다.

public class DriverHealthCheck : IHealthCheck
{
private readonly CaffeineClient _client;

public DriverHealthCheck(CaffeineClient client)
{
_client = client;
}

public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
var driversResponse = await _client.GetDriversAsync();
var unhealthyDrivers = driversResponse.Drivers
.Where(d => d.Status != "Running")
.ToList();

if (unhealthyDrivers.Any())
{
var driverNames = string.Join(", ", unhealthyDrivers.Select(d => d.Name));
return HealthCheckResult.Unhealthy($"Drivers unhealthy: {driverNames}");
}

return HealthCheckResult.Healthy($"{driversResponse.Drivers.Count} drivers running");
}
}

문제 해결​

드라이버를 찾을 수 없음 (DRIVER_NOT_FOUND)​

원인:

  • 드라이버 이름 오타
  • 드라이버가 Bridge에 로드되지 않음
  • 드라이버 설정 파일 누락

해결:

  1. GetDrivers로 등록된 드라이버 목록 확인
  2. Bridge의 driver_settings.json 확인
  3. Bridge 로그에서 드라이버 로드 오류 확인
# Bridge 로그 확인
tail -f /var/log/caffeine/bridge.log | grep "Driver"

드라이버 재시작 실패 (RESTART_FAILED)​

원인:

  • 드라이버 내부 오류
  • 장비 연결 실패
  • 포트 충돌

해결:

  1. GetDriverStatus로 extendedInfo.lastError 확인
  2. 장비 네트워크 연결 확인 (ping, telnet)
  3. 포트 충돌 확인 (netstat -an | grep {port})
  4. Bridge 완전 재시작 시도

권한 거부 (PERMISSION_DENIED)​

원인:

  • Admin 역할이 없는 JWT 토큰
  • JWT 토큰 만료

해결:

  1. JWT 토큰의 role 클레임 확인
  2. Admin 권한 부여 요청
  3. JWT 토큰 갱신
# cafe doctor로 현재 인증 상태 확인
cafe doctor

# 또는 Admin 대시보드에서 사용자 권한 확인
# Settings → Users → 해당 사용자 → Role 확인

드라이버 상태가 "Error"​

원인:

  • 장비 통신 오류
  • 설정 파일 오류
  • 드라이버 버그

해결:

  1. extendedInfo.lastError 확인
  2. 장비 전원 및 네트워크 확인
  3. 드라이버 설정 검증
  4. 드라이버 재시작 시도
  5. Bridge 로그 상세 확인

성능 최적화​

드라이버 목록 캐싱​

드라이버 목록은 자주 변경되지 않으므로 캐싱을 사용합니다.

using Microsoft.Extensions.Caching.Memory;

private readonly IMemoryCache _cache;
private const string CacheKey = "drivers_list";

public async Task<GetDriversResponse> GetDriversWithCacheAsync()
{
if (_cache.TryGetValue(CacheKey, out GetDriversResponse cachedResponse))
{
return cachedResponse;
}

var response = await client.GetDriversAsync();
_cache.Set(CacheKey, response, TimeSpan.FromMinutes(5));
return response;
}

병렬 상태 조회​

여러 드라이버 상태를 병렬로 조회합니다.

var driverNames = new[] { "Sim-PLC-01", "SECS-Equipment-01", "Modbus-Device-01" };

var statusTasks = driverNames.Select(name => client.GetDriverStatusAsync(name));
var statuses = await Task.WhenAll(statusTasks);

foreach (var status in statuses)
{
Console.WriteLine($"{status.Driver.Name}: {status.Driver.Status}");
}

Bridge.Host API​

Bridge.Host는 Minimal API로 아래 엔드포인트를 제공합니다. Engine의 gRPC/REST API와는 별개입니다.

메서드경로설명인증
GET/api/bridge/status브릿지 상태 조회불필요
GET/api/bridge/manifest브릿지 매니페스트 조회불필요
GET/api/bridge/config/driver드라이버 설정 조회필요
GET/api/bridge/config/tags태그 설정 조회필요
GET/api/bridge/license/라이선스 정보 조회불필요

관련 문서​


Engine REST API​

Caffeine Engine의 REST 엔드포인트(DriverController)를 통해 HTTP로 드라이버 설정을 관리할 수 있습니다.

기본 정보​

항목값
기본 경로/api/v1.0/drivers
인증불필요

엔드포인트 목록​

메서드경로설명
GET/api/v1.0/drivers드라이버 설정 목록 조회
POST/api/v1.0/drivers드라이버 설정 업데이트

GET /api/v1.0/drivers​

전체 드라이버 설정을 조회합니다.

응답

[
{
"id": "Equipment-01",
"driverType": "Modbus",
"ipAddress": "192.168.1.10",
"port": 502,
"pollingInterval": 1000
}
]

POST /api/v1.0/drivers​

드라이버 설정을 업데이트합니다. 설정 변경 후 Engine 재시작이 필요합니다.

요청 본문: DriverConfig[] 배열 (JSON)

응답: { "message": "Configuration saved. Restart Engine to apply changes." }

응답 코드: 200 저장 성공, 400 빈 목록, 500 파일 쓰기 실패

REST DTO​

DriverConfig

속성타입설명
idstring드라이버 고유 식별자
driverTypestring드라이버 타입 (Modbus, SecsGem, Simulation 등)
ipAddressstring장비 IP 주소
portint통신 포트 번호
pollingIntervalint폴링 주기 (밀리초)

지원 드라이버 타입​

드라이버 타입설명기본 포트패키지
ModbusTcp (v2.0.5)Modbus TCP 프로토콜502Caffeine.Drivers.ModbusTcp
ModbusRtu (v2.0.5)Modbus RTU (RS-485 시리얼)COM1Caffeine.Drivers.ModbusRtu
SecsGemSECS/GEM (SEMI E5/E30)5000Caffeine.Drivers.SecsGem
LseLS Electric PLC2004Caffeine.Drivers.Lse
MitsubishiMitsubishi PLC (MC Protocol)5000Caffeine.Drivers.Mitsubishi
OmronOmron FINS 프로토콜9600Caffeine.Drivers.Omron
Simulation시뮬레이션 드라이버 (테스트용)0Caffeine.Drivers.Simulation

라이선스별 드라이버 제한​

등급최대 드라이버
Community2개
Professional10개
Enterprise무제한

설정 파일 위치: {ContentRootPath}/config/equipment_model.json (또는 CAFFEINE_MODEL_PATH 환경변수)


IDriverCapabilities​

드라이버의 선택적 고급 기능을 런타임에 조회하기 위한 인터페이스입니다. 모든 드라이버가 구현할 필요는 없으며, is 패턴 매칭으로 지원 여부를 확인합니다.

네임스페이스: Caffeine.Core.Abstractions.Drivers.IDriverCapabilities

public interface IDriverCapabilities
{
/// <summary>드라이버가 지원하는 통신 프로토콜 목록 (예: "ModbusTCP", "MQTT", "OPC-UA")</summary>
IReadOnlyList<string> SupportedProtocols { get; }

/// <summary>최대 초당 처리 메시지 수 (0 = 무제한)</summary>
int MaxMessagesPerSecond { get; }

/// <summary>오프라인 버퍼링 지원 여부</summary>
bool SupportsOfflineBuffering { get; }

/// <summary>쌍방향 명령/응답 통신 지원 여부</summary>
bool SupportsBidirectional { get; }

/// <summary>드라이버가 읽을 수 있는 센서 카테고리 목록</summary>
IReadOnlyList<string> SupportedSensorCategories { get; }
}

속성​

속성타입설명
SupportedProtocolsIReadOnlyList<string>지원 프로토콜 목록 (예: ["ModbusTCP"])
MaxMessagesPerSecondint초당 최대 처리 메시지 수 (0 = 무제한)
SupportsOfflineBufferingbool오프라인 버퍼링 지원 여부
SupportsBidirectionalbool읽기/쓰기 양방향 통신 지원 여부
SupportedSensorCategoriesIReadOnlyList<string>읽을 수 있는 SensorCategory 이름 목록

드라이버별 지원 현황​

드라이버프로토콜MaxMsg/sOfflineBufferBidirectional
SimulationSimulation0(무제한)❌✅
SecsGemSECS/GEM0(무제한)❌✅
MitsubishiMC Protocol0(무제한)❌✅
OmronFINS0(무제한)❌✅
LseXGT0(무제한)❌✅
ModbusTcp (v2.0.5)ModbusTCP100❌✅
ModbusRtu (v2.0.5)ModbusRTU20❌✅

ModbusRtu MaxMessagesPerSecond = 20: RS-485 시리얼 대역폭 제한으로 TCP(100)보다 낮게 설정됩니다.

사용 예시​

// IDriverCapabilities 지원 여부 확인
if (driver is IDriverCapabilities caps)
{
Console.WriteLine($"Protocols: {string.Join(", ", caps.SupportedProtocols)}");
Console.WriteLine($"Max msg/s: {caps.MaxMessagesPerSecond}");
Console.WriteLine($"Bidirectional: {caps.SupportsBidirectional}");
Console.WriteLine($"Sensor categories: {string.Join(", ", caps.SupportedSensorCategories)}");
}

// 양방향 지원 드라이버에만 쓰기 수행
if (driver is IDriverCapabilities { SupportsBidirectional: true })
{
await driver.WriteTagAsync(tagId, value, ct);
}

참고​