AAS API
IEC 63278 Asset Administration Shell REST 및 gRPC API 레퍼런스입니다.
기본 정보
| 항목 | 값 |
|---|---|
| Base URL | /api/v1 |
| 인증 | JWT 토큰 (모든 엔드포인트 [Authorize] 필수) |
| Content-Type | application/json |
| 버전 | v1 |
엔드포인트 요약
AAS Repository API
| 메서드 | 경로 | 설명 |
|---|---|---|
| GET | /api/v1/aas | AAS 목록 조회 |
| GET | /api/v1/aas/{id} | AAS 상세 조회 |
| POST | /api/v1/aas | AAS 생성 |
| PUT | /api/v1/aas/{id} | AAS 수정 |
| DELETE | /api/v1/aas/{id} | AAS 삭제 |
| GET | /api/v1/aas/{id}/submodels | 서브모델 조회 |
AAS Registry API
| 메서드 | 경로 | 설명 |
|---|---|---|
| GET | /api/v1/aas-registry/by-asset/{globalAssetId} | Asset ID로 AAS 검색 |
| GET | /api/v1/aas-registry/search | 필터 기반 AAS 검색 |
ConceptDescription API
| 메서드 | 경로 | 설명 |
|---|---|---|
| GET | /api/v1/concept-descriptions | ConceptDescription 목록 조회 |
| GET | /api/v1/concept-descriptions/{id} | ConceptDescription 상세 조회 |
| POST | /api/v1/concept-descriptions | ConceptDescription 생성 |
| PUT | /api/v1/concept-descriptions/{id} | ConceptDescription 수정 |
| DELETE | /api/v1/concept-descriptions/{id} | ConceptDescription 삭제 |
AAS Repository API
AAS 목록 조회
전체 AAS Shell 목록을 페이지네이션으로 조회합니다.
요청
GET /api/v1/aas
| 파라미터 | 타입 | 필수 | 기본값 | 설명 |
|---|---|---|---|---|
page | int | N | 1 | 페이지 번호 |
pageSize | int | N | 20 | 페이지 크기 (최대 100) |
assetKind | string | N | - | 필터: Instance 또는 Type |
curl 예시
curl -X GET "http://localhost:5000/api/v1/aas?page=1&pageSize=10" \
-H "Authorization: Bearer {token}"
응답 (200 OK)
{
"items": [
{
"id": "urn:caffeine:aas:CVD-001",
"idShort": "CVD_Equipment_001",
"assetInformation": {
"assetKind": "Instance",
"globalAssetId": "urn:caffeine:asset:CVD-001",
"specificAssetIds": [
{ "name": "serialNumber", "value": "CVD-2024-001" }
]
},
"submodels": [
{
"type": "ModelReference",
"keys": [{ "type": "Submodel", "value": "urn:caffeine:sm:nameplate:CVD-001" }]
}
]
}
],
"totalCount": 42,
"page": 1,
"pageSize": 10
}
AAS 상세 조회
특정 AAS Shell의 상세 정보를 조회합니다.
요청
GET /api/v1/aas/{id}
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
id | string (path) | Y | AAS 식별자 (URL 인코딩 필요) |
curl 예시
curl -X GET "http://localhost:5000/api/v1/aas/urn%3Acaffeine%3Aaas%3ACVD-001" \
-H "Authorization: Bearer {token}"
응답 (200 OK)
{
"modelType": "AssetAdministrationShell",
"id": "urn:caffeine:aas:CVD-001",
"idShort": "CVD_Equipment_001",
"assetInformation": {
"assetKind": "Instance",
"globalAssetId": "urn:caffeine:asset:CVD-001",
"specificAssetIds": [
{ "name": "serialNumber", "value": "CVD-2024-001" },
{ "name": "manufacturer", "value": "NEXCODE" }
]
},
"submodels": [
{
"type": "ModelReference",
"keys": [{ "type": "Submodel", "value": "urn:caffeine:sm:nameplate:CVD-001" }]
},
{
"type": "ModelReference",
"keys": [{ "type": "Submodel", "value": "urn:caffeine:sm:technical-data:CVD-001" }]
},
{
"type": "ModelReference",
"keys": [{ "type": "Submodel", "value": "urn:caffeine:sm:operational-data:CVD-001" }]
}
]
}
응답 (404 Not Found)
{
"errorCode": "AAS_NOT_FOUND",
"message": "AAS not found: urn:caffeine:aas:CVD-001"
}
AAS 생성
새 AAS Shell을 생성합니다.
요청
POST /api/v1/aas
요청 본문
{
"idShort": "CVD_Equipment_001",
"id": "urn:caffeine:aas:CVD-001",
"assetInformation": {
"assetKind": "Instance",
"globalAssetId": "urn:caffeine:asset:CVD-001",
"specificAssetIds": [
{ "name": "serialNumber", "value": "CVD-2024-001" }
]
},
"submodels": []
}
curl 예시
curl -X POST http://localhost:5000/api/v1/aas \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"idShort": "CVD_Equipment_001",
"id": "urn:caffeine:aas:CVD-001",
"assetInformation": {
"assetKind": "Instance",
"globalAssetId": "urn:caffeine:asset:CVD-001"
}
}'
응답 (201 Created)
{
"id": "urn:caffeine:aas:CVD-001",
"idShort": "CVD_Equipment_001",
"assetInformation": {
"assetKind": "Instance",
"globalAssetId": "urn:caffeine:asset:CVD-001"
},
"submodels": []
}
응답 (409 Conflict)
동일한 id를 가진 AAS가 이미 존재할 때 반환됩니다.
{
"errorCode": "AAS_ALREADY_EXISTS",
"message": "AAS already exists: urn:caffeine:aas:CVD-001"
}
AAS 수정
기존 AAS Shell을 수정합니다.
요청
PUT /api/v1/aas/{id}
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
id | string (path) | Y | AAS 식별자 (URL 인코딩 필요) |
curl 예시
curl -X PUT "http://localhost:5000/api/v1/aas/urn%3Acaffeine%3Aaas%3ACVD-001" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"idShort": "CVD_Equipment_001_v2",
"assetInformation": {
"assetKind": "Instance",
"globalAssetId": "urn:caffeine:asset:CVD-001"
}
}'
응답 (200 OK) -- 수정된 AAS Shell 반환
응답 (404 Not Found) -- 해당 ID의 AAS가 없을 때
AAS 삭제
AAS Shell을 삭제합니다. 연결된 서브모델은 삭제되지 않습니다.
요청
DELETE /api/v1/aas/{id}
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
id | string (path) | Y | AAS 식별자 (URL 인코딩 필요) |
curl 예시
curl -X DELETE "http://localhost:5000/api/v1/aas/urn%3Acaffeine%3Aaas%3ACVD-001" \
-H "Authorization: Bearer {token}"
응답 (204 No Content) -- 성공
응답 (404 Not Found) -- 해당 ID의 AAS가 없을 때
서브모델 조회
특정 AAS Shell에 연결된 모든 서브모델을 조회합니다.
요청
GET /api/v1/aas/{id}/submodels
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
id | string (path) | Y | AAS 식별자 (URL 인코딩 필요) |
curl 예시
curl -X GET "http://localhost:5000/api/v1/aas/urn%3Acaffeine%3Aaas%3ACVD-001/submodels" \
-H "Authorization: Bearer {token}"
응답 (200 OK)
{
"items": [
{
"modelType": "Submodel",
"id": "urn:caffeine:sm:nameplate:CVD-001",
"idShort": "Nameplate",
"semanticId": {
"type": "ExternalReference",
"keys": [{ "type": "GlobalReference", "value": "urn:idta:submodel:nameplate:2.0" }]
},
"submodelElements": [
{
"modelType": "Property",
"idShort": "ManufacturerName",
"valueType": "xs:string",
"value": "NEXCODE"
},
{
"modelType": "Property",
"idShort": "SerialNumber",
"valueType": "xs:string",
"value": "CVD-2024-001"
},
{
"modelType": "Property",
"idShort": "ManufacturerProductDesignation",
"valueType": "xs:string",
"value": "CVD-PRO-3000"
}
]
},
{
"modelType": "Submodel",
"id": "urn:caffeine:sm:technical-data:CVD-001",
"idShort": "TechnicalData",
"semanticId": {
"type": "ExternalReference",
"keys": [{ "type": "GlobalReference", "value": "urn:idta:submodel:technical-data:1.2" }]
},
"submodelElements": [
{
"modelType": "SubmodelElementCollection",
"idShort": "ChamberTemp",
"value": [
{ "modelType": "Property", "idShort": "MaxValue", "valueType": "xs:float", "value": "800" },
{ "modelType": "Property", "idShort": "MinValue", "valueType": "xs:float", "value": "0" },
{ "modelType": "Property", "idShort": "Unit", "valueType": "xs:string", "value": "degC" }
]
}
]
}
]
}
응답 (404 Not Found) -- 해당 ID의 AAS가 없을 때
AAS Registry API
Asset ID로 AAS 검색
Global Asset ID로 연결된 AAS를 검색합니다.
요청
GET /api/v1/aas-registry/by-asset/{globalAssetId}
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
globalAssetId | string (path) | Y | Global Asset ID (URL 인코딩 필요) |
curl 예시
curl -X GET "http://localhost:5000/api/v1/aas-registry/by-asset/urn%3Acaffeine%3Aasset%3ACVD-001" \
-H "Authorization: Bearer {token}"
응답 (200 OK)
{
"items": [
{
"id": "urn:caffeine:aas:CVD-001",
"idShort": "CVD_Equipment_001",
"assetInformation": {
"assetKind": "Instance",
"globalAssetId": "urn:caffeine:asset:CVD-001"
}
}
]
}
필터 기반 AAS 검색
다양한 조건으로 AAS를 검색합니다.
요청
GET /api/v1/aas-registry/search
| 파라미터 | 타입 | 필수 | 기본값 | 설명 |
|---|---|---|---|---|
idShort | string | N | - | IdShort 부분 일치 검색 |
assetKind | string | N | - | Instance 또는 Type |
submodelType | string | N | - | 특정 서브모델을 포함하는 AAS 검색 (예: Nameplate) |
maxResults | int | N | 20 | 최대 결과 수 (최대 100) |
curl 예시
# IdShort에 "CVD"가 포함된 AAS 검색
curl -X GET "http://localhost:5000/api/v1/aas-registry/search?idShort=CVD&assetKind=Instance&maxResults=10" \
-H "Authorization: Bearer {token}"
응답 (200 OK)
{
"items": [
{
"id": "urn:caffeine:aas:CVD-001",
"idShort": "CVD_Equipment_001",
"assetInformation": {
"assetKind": "Instance",
"globalAssetId": "urn:caffeine:asset:CVD-001"
}
},
{
"id": "urn:caffeine:aas:CVD-002",
"idShort": "CVD_Equipment_002",
"assetInformation": {
"assetKind": "Instance",
"globalAssetId": "urn:caffeine:asset:CVD-002"
}
}
],
"totalCount": 2
}
ConceptDescription API
ConceptDescription은 AAS 데이터 항목의 의미를 정의하는 메타데이터입니다.
ConceptDescription 목록 조회
요청
GET /api/v1/concept-descriptions
| 파라미터 | 타입 | 필수 | 기본값 | 설명 |
|---|---|---|---|---|
page | int | N | 1 | 페이지 번호 |
pageSize | int | N | 20 | 페이지 크기 |
idShort | string | N | - | IdShort 부분 일치 검색 |
curl 예시
curl -X GET "http://localhost:5000/api/v1/concept-descriptions?idShort=Temperature" \
-H "Authorization: Bearer {token}"
응답 (200 OK)
{
"items": [
{
"modelType": "ConceptDescription",
"id": "0173-1#02-AAI835#001",
"idShort": "Temperature",
"embeddedDataSpecifications": [
{
"dataSpecificationContent": {
"preferredName": [
{ "language": "en", "text": "Temperature" },
{ "language": "ko", "text": "온도" }
],
"unit": "degC",
"valueFormat": "NR1..5"
}
}
]
}
],
"totalCount": 1
}
ConceptDescription 상세 조회
요청
GET /api/v1/concept-descriptions/{id}
curl 예시
curl -X GET "http://localhost:5000/api/v1/concept-descriptions/0173-1%2302-AAI835%23001" \
-H "Authorization: Bearer {token}"
응답 (200 OK) -- ConceptDescription 상세 JSON
응답 (404 Not Found) -- 해당 ID의 ConceptDescription이 없을 때
ConceptDescription 생성
요청
POST /api/v1/concept-descriptions
curl 예시
curl -X POST http://localhost:5000/api/v1/concept-descriptions \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"id": "0173-1#02-CUSTOM001#001",
"idShort": "RFPower",
"embeddedDataSpecifications": [
{
"dataSpecificationContent": {
"preferredName": [
{ "language": "en", "text": "RF Power" },
{ "language": "ko", "text": "RF 출력" }
],
"unit": "W",
"valueFormat": "NR1..5"
}
}
]
}'
응답 (201 Created) -- 생성된 ConceptDescription
응답 (409 Conflict) -- 동일 ID가 이미 존재할 때
ConceptDescription 수정
요청
PUT /api/v1/concept-descriptions/{id}
응답 (200 OK) -- 수정된 ConceptDescription
응답 (404 Not Found) -- 해당 ID가 없을 때
ConceptDescription 삭제
요청
DELETE /api/v1/concept-descriptions/{id}
응답 (204 No Content) -- 성공
응답 (404 Not Found) -- 해당 ID가 없을 때
gRPC API
Caffeine Engine은 AAS 관련 gRPC 서비스도 제공합니다.
AasRepositoryApi
service AasRepositoryApi {
// AAS Shell CRUD
rpc GetAllShells (GetAllShellsRequest) returns (GetAllShellsResponse);
rpc GetShell (GetShellRequest) returns (GetShellResponse);
rpc CreateShell (CreateShellRequest) returns (CreateShellResponse);
rpc UpdateShell (UpdateShellRequest) returns (UpdateShellResponse);
rpc DeleteShell (DeleteShellRequest) returns (DeleteShellResponse);
// Submodel 조회
rpc GetSubmodels (GetSubmodelsRequest) returns (GetSubmodelsResponse);
rpc GetSubmodel (GetSubmodelRequest) returns (GetSubmodelResponse);
}
AasRegistryApi
service AasRegistryApi {
// Asset ID로 AAS 검색
rpc GetByAssetId (GetByAssetIdRequest) returns (GetByAssetIdResponse);
// 필터 기반 AAS 검색
rpc Search (AasSearchRequest) returns (AasSearchResponse);
}
gRPC 사용 예시 (C#)
using Grpc.Net.Client;
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new AasRepositoryApi.AasRepositoryApiClient(channel);
// AAS 목록 조회
var response = await client.GetAllShellsAsync(new GetAllShellsRequest
{
Page = 1,
PageSize = 10
});
foreach (var shell in response.Shells)
{
Console.WriteLine($"{shell.IdShort}: {shell.Id}");
}
// 특정 AAS 조회
var shellResponse = await client.GetShellAsync(new GetShellRequest
{
Id = "urn:caffeine:aas:CVD-001"
});
에러 응답
모든 API 에러는 일관된 형식으로 반환됩니다.
{
"errorCode": "AAS_NOT_FOUND",
"message": "AAS not found: urn:caffeine:aas:CVD-999"
}
에러 코드
| HTTP 상태 | 에러 코드 | 설명 |
|---|---|---|
| 400 | INVALID_REQUEST | 요청 본문 유효성 검증 실패 |
| 401 | UNAUTHORIZED | 인증 토큰 없음 또는 만료 |
| 403 | FORBIDDEN | 권한 부족 |
| 404 | AAS_NOT_FOUND | AAS Shell을 찾을 수 없음 |
| 404 | SUBMODEL_NOT_FOUND | Submodel을 찾을 수 없음 |
| 404 | CONCEPT_DESCRIPTION_NOT_FOUND | ConceptDescription을 찾을 수 없음 |
| 409 | AAS_ALREADY_EXISTS | 동일 ID의 AAS가 이미 존재 |
| 409 | CONCEPT_DESCRIPTION_ALREADY_EXISTS | 동일 ID의 ConceptDescription이 이미 존재 |
| 500 | INTERNAL_ERROR | 서버 내부 오류 |
인증
모든 AAS API 엔드포인트는 JWT 인증이 필수입니다.
토큰 획득
curl -X POST http://localhost:5000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "your-password"}'
토큰 사용
curl -X GET http://localhost:5000/api/v1/aas \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
v2.3.0 신규 기능
FIR-039: NetworkComplianceSubmodelProvider
네트워크 보안 컴플라이언스 진단 결과를 IEC 63278 서브모델로 제공하는 Provider입니다.
SemanticId: https://admin-shell.io/idta/SM/NetworkCompliance/1/0
DI 등록 후 AAS 서브모델 조회 시 자동으로 포함됩니다.
// DI 등록
services.AddScoped<INetworkComplianceChecker, DefaultNetworkComplianceChecker>();
services.AddScoped<ISubmodelProvider, NetworkComplianceSubmodelProvider>();
등록 후 기존 서브모델 조회 엔드포인트로 확인합니다.
GET /api/v1/aas/{id}/submodels
응답에 submodelType: "NetworkCompliance" 항목이 포함됩니다.
{
"submodels": [
{
"submodelType": "NetworkCompliance",
"semanticId": "https://admin-shell.io/idta/SM/NetworkCompliance/1/0",
"elements": [
{ "idShort": "OverallLevel", "value": "High" }
]
}
]
}
커스텀 컴플라이언스 검사 로직이 필요하면 INetworkComplianceChecker를 구현해 등록하세요.
FIR-040: ITagToSubmodelMapper — equipmentCategory 파라미터
네임스페이스: Caffeine.AAS.Mapping
v2.3.0에서 Map 메서드에 선택적 equipmentCategory 파라미터가 추가되었습니다.
object? Map(
object tagValue,
string equipmentId,
string semanticId,
string? equipmentCategory = null); // v2.3.0 추가
동일한 semanticId(예: Temperature)라도 장비 카테고리에 따라 다른 매핑 규칙을 적용할 수 있습니다.
// 압축기의 Temperature → 다른 단위/범위 규칙
mapper.Map(tagValue, "eqp-001", "0173-1#02-AAV232#002", "Compressor");
// 포장기의 Temperature → 다른 단위/범위 규칙
mapper.Map(tagValue, "eqp-002", "0173-1#02-AAV232#002", "PackagingMachine");
equipmentCategory를 생략하면 카테고리 비구분 기본 규칙이 적용됩니다 (하위 호환).