Robotics Device API
로봇 장비 프로비저닝, 조회, 명령 실행을 위한 REST API 엔드포인트입니다.
개요
Robotics Device API는 제조 현장의 로봇 및 자동화 장비를 중앙에서 관리하고 제어하는 기능을 제공합니다.
Base URL: /api/v1/devices
네임스페이스: Caffeine.Robotics.Controllers
어셈블리: Caffeine.Robotics.dll
.NET 버전: .NET 10.0
인증
모든 엔드포인트는 JWT Bearer 토큰 인증이 필요합니다.
Authorization: Bearer <token>
엔드포인트 요약
| 메서드 | 경로 | 설명 |
|---|---|---|
GET | /api/v1/devices | 전체 장비 목록 조회 |
GET | /api/v1/devices/{id} | 특정 장비 상세 조회 |
POST | /api/v1/devices | 신규 장비 프로비저닝 |
POST | /api/v1/devices/{id}/commands | 장비 명령 실행 |
상세 엔드포인트
GET /api/v1/devices
등록된 전체 장비 목록을 조회합니다.
요청:
GET /api/v1/devices
Authorization: Bearer <token>
응답 (200 OK):
[
{
"deviceId": "robot-arm-001",
"tenantId": "factory-a",
"name": "로봇 팔 #1",
"currentState": "Idle",
"lastSeen": "2026-02-17T10:30:00Z"
},
{
"deviceId": "agv-002",
"tenantId": "factory-a",
"name": "AGV #2",
"currentState": "Working",
"lastSeen": "2026-02-17T10:35:00Z"
}
]
응답 필드:
| 필드 | 타입 | 설명 |
|---|---|---|
deviceId | string | 장비 고유 식별자 |
tenantId | string? | 테넌트 ID (멀티테넌시 환경) |
name | string | 장비 이름 |
currentState | string | 현재 상태 (DeviceState 열거형) |
lastSeen | string | 마지막 통신 시각 (ISO 8601, UTC) |
DeviceState 값:
Unknown— 알 수 없음Provisioned— 프로비저닝됨Offline— 오프라인Online— 온라인Idle— 유휴Working— 작업 중Error— 오류Maintenance— 유지보수Decommissioned— 폐기됨
GET /api/v1/devices/{id}
특정 장비의 상세 정보를 조회합니다.
요청:
GET /api/v1/devices/robot-arm-001
Authorization: Bearer <token>
응답 (200 OK):
{
"deviceId": "robot-arm-001",
"tenantId": "factory-a",
"name": "로봇 팔 #1",
"currentState": "Idle",
"lastSeen": "2026-02-17T10:30:00Z"
}
응답 (404 Not Found):
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.5",
"title": "Not Found",
"status": 404
}
POST /api/v1/devices
신규 장비를 프로비저닝합니다.
요청:
POST /api/v1/devices
Authorization: Bearer <token>
Content-Type: application/json
{
"deviceId": "agv-003",
"name": "AGV #3",
"description": "Warehouse autonomous vehicle"
}
요청 필드:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
deviceId | string | ✅ | 장비 고유 식별자 |
name | string | ✅ | 장비 이름 |
description | string? | ❌ | 장비 설명 |
응답 (201 Created):
{
"deviceId": "agv-003",
"tenantId": null,
"name": "AGV #3",
"currentState": "Offline",
"lastSeen": "2026-02-17T10:40:00Z"
}
응답 헤더:
Location: /api/v1/devices/agv-003
POST /api/v1/devices/{id}/commands
장비에 명령을 전송하여 실행합니다. 명령 실행 전에 Safety Policy Engine을 통해 안전성 검증이 수행됩니다.
요청:
POST /api/v1/devices/robot-arm-001/commands
Authorization: Bearer <token>
Content-Type: application/json
{
"commandId": "cmd-12345",
"deviceId": "robot-arm-001",
"commandType": "MoveTo",
"parameters": {
"x": 100,
"y": 200,
"z": 50,
"speed": 0.5
},
"priority": 1,
"timestamp": "2026-02-17T10:45:00Z"
}
요청 필드:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
commandId | string | ✅ | 명령 고유 식별자 |
deviceId | string | ✅ | 대상 장비 ID (경로 파라미터와 일치해야 함) |
commandType | string | ✅ | 명령 유형 (예: MoveTo, PickUp, Release) |
parameters | object? | ❌ | 명령 파라미터 (Key-Value 딕셔너리) |
priority | int | ❌ | 우선순위 (기본값: 0) |
timestamp | string | ❌ | 명령 발행 시각 (ISO 8601, 기본값: 현재 시각) |
응답 (200 OK - 성공):
{
"commandId": "cmd-12345",
"success": true,
"message": null,
"data": null,
"completedAt": "2026-02-17T10:45:02Z"
}
응답 (400 Bad Request - ID 불일치):
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"detail": "Device ID mismatch"
}
응답 (403 Forbidden - 안전성 위반):
{
"message": "Safety violation",
"reason": "Geofence boundary exceeded: target position (100, 200, 50) is outside allowed zone"
}
응답 (404 Not Found - 장비 없음):
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.5",
"title": "Not Found",
"status": 404
}
CommandResult 응답 필드:
| 필드 | 타입 | 설명 |
|---|---|---|
commandId | string | 명령 ID |
success | bool | 실행 성공 여부 |
message | string? | 에러 메시지 (실패 시) |
data | object? | 반환 데이터 (선택적) |
completedAt | string | 완료 시각 (ISO 8601, UTC) |
DTO 정의
CreateDeviceRequest
public record CreateDeviceRequest(
string DeviceId,
string Name,
string? Description = null
);
DeviceCommand
public record DeviceCommand(
string CommandId,
string DeviceId,
string CommandType,
Dictionary<string, object>? Parameters = null,
int Priority = 0
)
{
public DateTimeOffset Timestamp { get; init; } = DateTimeOffset.UtcNow;
}
CommandResult
public record CommandResult(
string CommandId,
bool Success,
string? Message = null,
object? Data = null
)
{
public DateTimeOffset CompletedAt { get; init; } = DateTimeOffset.UtcNow;
public static CommandResult Succeeded(string commandId, object? data = null);
public static CommandResult Failed(string commandId, string message);
}
Device
public class Device
{
public string DeviceId { get; }
public string? TenantId { get; }
public string Name { get; }
public DeviceState CurrentState { get; }
public DateTimeOffset LastSeen { get; }
}
DeviceState (열거형)
public enum DeviceState
{
Unknown,
Provisioned,
Offline,
Online,
Idle,
Working,
Error,
Maintenance,
Decommissioned
}
에러 코드
| HTTP 상태 | 설명 | 대응 방법 |
|---|---|---|
200 | 성공 | - |
201 | 리소스 생성 성공 | Location 헤더에서 리소스 URL 확인 |
400 | 잘못된 요청 (필수 필드 누락, ID 불일치) | 요청 본문 및 파라미터 확인 |
401 | 인증 실패 (JWT 토큰 없음/만료) | 토큰 갱신 |
403 | 권한 없음 (안전성 위반) | Safety Policy 위반 사유 확인 |
404 | 장비 없음 | 장비 ID 확인 |
500 | 서버 내부 오류 | 로그 확인 |
사용 예제
C# (HttpClient)
using System.Net.Http.Json;
using Caffeine.Robotics.Abstractions.Models;
var client = new HttpClient
{
BaseAddress = new Uri("http://localhost:5200")
};
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
// 전체 장비 목록 조회
var devices = await client.GetFromJsonAsync<List<Device>>("/api/v1/devices");
foreach (var device in devices)
{
Console.WriteLine($"{device.DeviceId}: {device.Name} ({device.CurrentState})");
}
// 특정 장비 조회
var device = await client.GetFromJsonAsync<Device>("/api/v1/devices/robot-arm-001");
Console.WriteLine($"Last Seen: {device?.LastSeen}");
// 신규 장비 프로비저닝
var createRequest = new CreateDeviceRequest(
DeviceId: "agv-003",
Name: "AGV #3",
Description: "Warehouse autonomous vehicle"
);
var response = await client.PostAsJsonAsync("/api/v1/devices", createRequest);
var newDevice = await response.Content.ReadFromJsonAsync<Device>();
Console.WriteLine($"Created: {newDevice?.DeviceId}");
// 명령 실행
var command = new DeviceCommand(
CommandId: "cmd-12345",
DeviceId: "robot-arm-001",
CommandType: "MoveTo",
Parameters: new Dictionary<string, object>
{
["x"] = 100,
["y"] = 200,
["z"] = 50,
["speed"] = 0.5
},
Priority: 1
);
var cmdResponse = await client.PostAsJsonAsync("/api/v1/devices/robot-arm-001/commands", command);
var result = await cmdResponse.Content.ReadFromJsonAsync<CommandResult>();
Console.WriteLine($"Command Result: {result?.Success}, Message: {result?.Message}");
curl
# 전체 장비 목록 조회
curl -H "Authorization: Bearer <token>" \
http://localhost:5200/api/v1/devices
# 특정 장비 조회
curl -H "Authorization: Bearer <token>" \
http://localhost:5200/api/v1/devices/robot-arm-001
# 신규 장비 프로비저닝
curl -X POST http://localhost:5200/api/v1/devices \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"deviceId": "agv-003",
"name": "AGV #3",
"description": "Warehouse autonomous vehicle"
}'
# 명령 실행
curl -X POST http://localhost:5200/api/v1/devices/robot-arm-001/commands \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"commandId": "cmd-12345",
"deviceId": "robot-arm-001",
"commandType": "MoveTo",
"parameters": {
"x": 100,
"y": 200,
"z": 50,
"speed": 0.5
},
"priority": 1
}'
PowerShell
$headers = @{ Authorization = "Bearer <token>" }
$baseUrl = "http://localhost:5200"
# 전체 장비 목록 조회
$devices = Invoke-RestMethod -Uri "$baseUrl/api/v1/devices" -Headers $headers
$devices | ForEach-Object { Write-Host "$($_.deviceId): $($_.name) ($($_.currentState))" }
# 특정 장비 조회
$device = Invoke-RestMethod -Uri "$baseUrl/api/v1/devices/robot-arm-001" -Headers $headers
Write-Host "Last Seen: $($device.lastSeen)"
# 신규 장비 프로비저닝
$createRequest = @{
deviceId = "agv-003"
name = "AGV #3"
description = "Warehouse autonomous vehicle"
} | ConvertTo-Json
$newDevice = Invoke-RestMethod -Uri "$baseUrl/api/v1/devices" `
-Method Post -Headers $headers -Body $createRequest -ContentType "application/json"
Write-Host "Created: $($newDevice.deviceId)"
# 명령 실행
$command = @{
commandId = "cmd-12345"
deviceId = "robot-arm-001"
commandType = "MoveTo"
parameters = @{
x = 100
y = 200
z = 50
speed = 0.5
}
priority = 1
} | ConvertTo-Json
$result = Invoke-RestMethod -Uri "$baseUrl/api/v1/devices/robot-arm-001/commands" `
-Method Post -Headers $headers -Body $command -ContentType "application/json"
Write-Host "Command Result: $($result.success), Message: $($result.message)"
Safety Policy 검증
명령 실행 시 ISafetyPolicyEngine을 통해 다음 항목이 자동으로 검증됩니다:
- Geofence: 목표 위치가 허용된 영역 내에 있는지 확인
- Battery Level: 배터리 잔량이 안전 기준 이상인지 확인
- Collision: 장애물과의 충돌 위험 여부 확인
- Emergency Stop: 비상 정지 상태가 아닌지 확인
안전성 위반 시 403 Forbidden 응답과 함께 구체적인 위반 사유가 반환됩니다.
Swagger UI
개발 환경에서 Swagger UI를 통해 API를 테스트할 수 있습니다:
http://localhost:5200/swagger
관련 문서
- Robotics OTA API — OTA 업데이트 관리
- Caffeine.Core API — 핵심 모듈 API
- 실시간 통신 API — gRPC/SignalR 통신
- 설정 가이드 — Robotics 모듈 설정
네임스페이스: Caffeine.Robotics.Controllers DTO 네임스페이스: Caffeine.Robotics.Abstractions.Models 어셈블리: Caffeine.Robotics.dll .NET 플랫폼: .NET 10.0