본문으로 건너뛰기

드라이버 관리 API

개요

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

기본 정보

항목
서비스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 필드

GetDriverStatusextendedInfo 맵에 포함될 수 있는 필드:

설명예시 값
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. GetDriverStatusextendedInfo.lastError 확인
  2. 장비 네트워크 연결 확인 (ping, telnet)
  3. 포트 충돌 확인 (netstat -an | grep {port})
  4. Bridge 완전 재시작 시도

권한 거부 (PERMISSION_DENIED)

원인:

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

해결:

  1. JWT 토큰의 role 클레임 확인
  2. Admin 권한 부여 요청
  3. JWT 토큰 갱신
// JWT 토큰 디코딩 (검증용)
var handler = new JwtSecurityTokenHandler();
var token = handler.ReadJwtToken(jwtToken);
var roleClaim = token.Claims.FirstOrDefault(c => c.Type == "role");
Console.WriteLine($"Role: {roleClaim?.Value}");

드라이버 상태가 "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}");
}

관련 문서


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 패턴 매칭으로 지원 여부를 확인합니다.

위치: src/Caffeine.Core/Abstractions/Drivers/IDriverCapabilities.cs

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);
}

참고