본문으로 건너뛰기

Caffeine.Robotics.Abstractions API

개요

Caffeine.Robotics.Abstractions는 Caffeine 로보틱스 모듈의 핵심 추상화 레이어입니다. IIoT 엣지 환경의 이동형·고정형 디바이스를 통합 관리하기 위한 인터페이스, CQRS 커맨드, Safety 계약, 디바이스 관리 인터페이스를 정의합니다.

Robotics 모듈 관계도

패키지역할
Caffeine.Robotics.Abstractions인터페이스·CQRS·Safety 계약 정의
Caffeine.Robotics구체 구현 (AGV, 드론, 로봇 등)
Caffeine.Robotics.OTAOTA(Over-The-Air) 업데이트 관리

기본 정보

항목
패키지NEXCODE.Caffeine.Robotics.Abstractions
타겟 프레임워크net10.0
루트 네임스페이스Caffeine.Robotics.Abstractions
멀티테넌시IMultiTenantEntity 상속 (테넌트 격리 지원)

핵심 인터페이스

IDevice

모든 관리 디바이스의 기반 인터페이스입니다.

public interface IDevice : IMultiTenantEntity
{
string DeviceId { get; }
string Name { get; }
DeviceState CurrentState { get; }
DateTimeOffset LastSeen { get; }

// Capability 패턴
bool HasCapability<T>() where T : IDeviceCapability;
T? GetCapability<T>() where T : IDeviceCapability;
IEnumerable<IDeviceCapability> GetAllCapabilities();

// 디바이스 제어
Task<CommandResult> ExecuteCommandAsync(DeviceCommand command, CancellationToken ct = default);
Task EmergencyStopAsync(string reason, CancellationToken ct = default);
}

DeviceState 열거형:

설명
Idle대기 상태
Active작업 수행 중
Error오류 상태
Offline오프라인
Charging충전 중 (이동형 디바이스)
EmergencyStopped비상 정지

IRoboticDevice

이동형 자율 디바이스(AGV, 드론, 로봇)를 위한 인터페이스입니다.

public interface IRoboticDevice : IDevice
{
Task NavigateToAsync(string locationId, CancellationToken ct = default);
Task DockAsync(CancellationToken ct = default);
}

사용 예시:

// AGV 이동 제어
IRoboticDevice agv = deviceRegistry.GetDevice("AGV-001");
await agv.NavigateToAsync("STATION-A-03", cancellationToken);
await agv.DockAsync(cancellationToken);

IStationaryDevice

고정형 디바이스(스마트 피더, 센서 스테이션)를 위한 인터페이스입니다.

public interface IStationaryDevice : IDevice
{
string LocationId { get; }
}

Capability 패턴

디바이스의 기능을 분리된 인터페이스로 조합하는 패턴입니다. IDevice.HasCapability<T>()로 런타임에 지원 여부를 확인합니다.

지원 Capability (6종)

인터페이스설명주요 멤버
INavigationCapability위치 이동 및 경로 추적NavigateToAsync(), GetCurrentPositionAsync(), IsNavigating
ISensingCapability센서 텔레메트리 스트림TelemetryStream (IObservable<DeviceTelemetry>)
IActuationCapability액추에이터 제어(작동 인터페이스)
IDispensingCapability자재 분배 제어(분배 인터페이스)
IDockingCapability도킹/언도킹 제어(도킹 인터페이스)
IDeviceCapability모든 Capability의 기반 인터페이스(마커 인터페이스)

INavigationCapability 상세

public interface INavigationCapability : IDeviceCapability
{
Task<NavigationResult> NavigateToAsync(
Position targetPosition,
CancellationToken cancellationToken = default);

Task<Position> GetCurrentPositionAsync(
CancellationToken cancellationToken = default);

bool IsNavigating { get; }
}

// 위치 정보
public record Position(double Latitude, double Longitude, double? Altitude = null);

// 이동 결과
public record NavigationResult(bool Success, string? ErrorMessage = null, TimeSpan? Duration = null);

ISensingCapability 상세

public interface ISensingCapability : IDeviceCapability
{
IObservable<DeviceTelemetry> TelemetryStream { get; }
}

System.Reactive 기반의 실시간 텔레메트리 스트림을 제공합니다.

Capability 사용 예시

IDevice device = await deviceRegistry.GetDeviceAsync("ROBOT-001");

// Capability 존재 확인 후 사용
if (device.HasCapability<INavigationCapability>())
{
var nav = device.GetCapability<INavigationCapability>()!;
var currentPos = await nav.GetCurrentPositionAsync();
Console.WriteLine($"현재 위치: ({currentPos.Latitude}, {currentPos.Longitude})");

var result = await nav.NavigateToAsync(new Position(37.5665, 126.9780));
if (!result.Success)
Console.WriteLine($"이동 실패: {result.ErrorMessage}");
}

// 센서 구독
if (device.HasCapability<ISensingCapability>())
{
var sensing = device.GetCapability<ISensingCapability>()!;
sensing.TelemetryStream.Subscribe(telemetry =>
{
Console.WriteLine($"[{telemetry.DeviceId}] {telemetry.Key}: {telemetry.Value}");
});
}

// 모든 Capability 열거
foreach (var capability in device.GetAllCapabilities())
{
Console.WriteLine($"지원 Capability: {capability.GetType().Name}");
}

CQRS Commands

RegisterDeviceCommand

새 디바이스를 시스템에 등록합니다.

public record RegisterDeviceCommand(
string DeviceId,
string TenantId,
string Name,
string DeviceType) : ICommand;

사용 예시:

var command = new RegisterDeviceCommand(
DeviceId: "AGV-NEW-001",
TenantId: "tenant-abc",
Name: "신규 AGV 1호",
DeviceType: "AGV"
);

await mediator.Send(command, cancellationToken);

TriggerEmergencyStopCommand

특정 디바이스에 비상 정지를 명령합니다.

public record TriggerEmergencyStopCommand(
string DeviceId,
string Reason) : ICommand;

사용 예시:

// 비상 상황 감지 시
var stopCommand = new TriggerEmergencyStopCommand(
DeviceId: "AGV-001",
Reason: "인체 감지 — 구역 진입"
);

await mediator.Send(stopCommand, cancellationToken);

Safety 계약

ISafetyPolicyEngine

디바이스 커맨드 실행 전 안전 정책을 검증합니다.

public interface ISafetyPolicyEngine
{
Task<SafetyEvaluationResult> ValidateCommandAsync(
DeviceCommand command,
CancellationToken ct = default);
}

모든 커맨드는 실행 전 ISafetyPolicyEngine의 검증을 통과해야 합니다. 위험 지역 진입, 최대 속도 초과, 비인가 작업 등을 차단합니다.

SafetyEvaluationResult:

속성타입설명
IsAllowedbool커맨드 실행 허용 여부
DenialReasonstring?거부 시 사유
RiskLevelRiskLevel위험도 (Low/Medium/High/Critical)

디바이스 관리

IDeviceRegistry

디바이스 등록 및 조회를 담당합니다.

public interface IDeviceRegistry
{
Task<Device?> GetDeviceAsync(string deviceId, CancellationToken ct = default);
Task RegisterDeviceAsync(Device device, CancellationToken ct = default);
Task UpdateDeviceAsync(Device device, CancellationToken ct = default);
Task<IEnumerable<Device>> GetAllDevicesAsync(CancellationToken ct = default);
}

IDeviceHealthMonitor

디바이스 헬스비트 및 상태 스트리밍을 관리합니다.

public interface IDeviceHealthMonitor
{
IObservable<DeviceHealthEvent> HealthStream { get; }

Task ReportHeartbeatAsync(string deviceId, CancellationToken ct = default);
Task RegisterDeviceAsync(string deviceId, TimeSpan heartbeatTimeout, CancellationToken ct = default);
Task UnregisterDeviceAsync(string deviceId, CancellationToken ct = default);
}

DeviceHealthEvent:

public record DeviceHealthEvent(
string DeviceId,
DeviceHealthStatus Status,
string Reason,
DateTimeOffset Timestamp
);

public enum DeviceHealthStatus { Healthy, Unhealthy, Offline, Unknown }

사용 예시:

// 헬스 스트림 구독
healthMonitor.HealthStream
.Where(e => e.Status == DeviceHealthStatus.Offline)
.Subscribe(e =>
{
_logger.LogWarning("디바이스 오프라인: {DeviceId} — {Reason}", e.DeviceId, e.Reason);
// 알람 발생 또는 자동 복구 로직
});

// 디바이스 등록 (5분 타임아웃)
await healthMonitor.RegisterDeviceAsync("AGV-001", TimeSpan.FromMinutes(5), ct);

// 하트비트 전송 (드라이버 루프에서 주기적 호출)
await healthMonitor.ReportHeartbeatAsync("AGV-001", ct);

연결 계층

IDeviceTransportBridge

드라이버 레이어와 로보틱스 모듈 간의 통신 브릿지입니다.

// Caffeine.Robotics.Abstractions/Connectivity/IDeviceTransportBridge.cs

통합 시나리오

AGV 디바이스 관리 전체 흐름

// 1. DI 등록 (Program.cs)
services.AddSingleton<IDeviceRegistry, InMemoryDeviceRegistry>();
services.AddSingleton<IDeviceHealthMonitor, DeviceHealthMonitor>();
services.AddScoped<ISafetyPolicyEngine, RuleBasedSafetyEngine>();

// 2. 디바이스 등록
var device = new Device { DeviceId = "AGV-001", Name = "물류 AGV 1호" };
await deviceRegistry.RegisterDeviceAsync(device, ct);
await healthMonitor.RegisterDeviceAsync("AGV-001", TimeSpan.FromMinutes(5), ct);

// 3. 커맨드 전송 (Safety 검증 포함)
var moveCommand = new DeviceCommand("NAVIGATE", new { LocationId = "STATION-B" });
var safetyResult = await safetyEngine.ValidateCommandAsync(moveCommand, ct);
if (safetyResult.IsAllowed)
{
var agv = await deviceRegistry.GetDeviceAsync("AGV-001", ct);
await agv!.ExecuteCommandAsync(moveCommand, ct);
}
else
{
_logger.LogWarning("안전 정책 위반: {Reason}", safetyResult.DenialReason);
}

관련 문서