본문으로 건너뛰기

AAS 심화: Custom Provider + AASX + Protocol Server

이 튜토리얼에서는 Caffeine V3의 AAS(Asset Administration Shell) 모듈을 심층적으로 활용합니다. 커스텀 서브모델 Provider를 직접 구현하고, AASX 패키지로 내보내며, REST API 서버를 구성합니다.

개요​

단계주제소요 시간
1프로젝트 설정5분
2Custom ISubmodelProvider 구현20분
3AASX Import/Export15분
4AAS Protocol Server (REST API)15분
5검증 및 정리5분

사전 요구 사항:

학습 목표:

  • ISubmodelProvider 인터페이스를 직접 구현하여 도메인 특화 서브모델을 생성할 수 있다
  • AASX 파일로 AAS Shell을 내보내고 다시 가져올 수 있다
  • REST API로 AAS Shell CRUD를 수행할 수 있다

1단계: 프로젝트 설정​

프로젝트 생성 및 패키지 설치​

dotnet new web -n AasDeepDive
cd AasDeepDive

# Caffeine AAS 패키지 추가
dotnet add package NEXCODE.Caffeine.AAS
dotnet add package NEXCODE.Caffeine.Core

Program.cs 기본 구성​

using Caffeine.AAS;

var builder = WebApplication.CreateBuilder(args);

// AAS 모듈 등록
builder.Services.AddCaffeineAas(aas =>
{
aas.AddSubmodelProvider<NameplateSubmodelProvider>()
.WithAasxPackageSupport(); // AASX Import/Export 활성화
});

var app = builder.Build();

app.MapGet("/", () => "AAS Deep Dive Tutorial");

app.Run();

2단계: Custom ISubmodelProvider 구현​

Caffeine에는 7개의 기본 Provider가 내장되어 있습니다:

Provider용도
NameplateSubmodelProvider장비 명판 (제조사, 시리얼)
TechnicalDataSubmodelProvider기술 사양 (온도, 압력 한계)
OperationalDataSubmodelProvider실시간 운영 데이터
AlarmConditionSubmodelProvider활성 알람/결함
MaintenanceHistorySubmodelProvider정비 이력
PredictiveMaintenanceSubmodelProviderML 기반 예측 정비
AiModelNameplateSubmodelProviderAI 모델 메타데이터

이제 에너지 모니터링 서브모델을 직접 구현합니다.

EnergyMonitoringSubmodelProvider.cs​

using Caffeine.Core.Abstractions.AAS;

namespace AasDeepDive;

/// <summary>
/// 에너지 모니터링 서브모델 Provider.
/// 전력 소비, 효율, CO2 배출량을 추적합니다.
/// </summary>
[SubmodelVersion("1.0", "에너지 모니터링 v1.0")]
public class EnergyMonitoringSubmodelProvider : ISubmodelProvider
{
public string SubmodelType => "EnergyMonitoring";
public string SemanticId => "urn:nexcode:caffeine:submodel:energy-monitoring:1.0";

public Task<ISubmodel> BuildSubmodelAsync(string assetId, CancellationToken ct = default)
{
// 서브모델 엘리먼트 구성
var elements = new List<ISubmodelElement>
{
new AasSubmodelElement
{
IdShort = "PowerConsumption",
SemanticId = "0173-1#02-AAP917#001", // ECLASS: 전력 소비
ValueType = "double",
Value = "2450.5" // kW
},
new AasSubmodelElement
{
IdShort = "EnergyEfficiency",
SemanticId = "urn:nexcode:energy:efficiency",
ValueType = "double",
Value = "92.3" // %
},
new AasSubmodelElement
{
IdShort = "CO2Emission",
SemanticId = "urn:nexcode:energy:co2",
ValueType = "double",
Value = "1.23" // ton/h
},
new AasSubmodelElement
{
IdShort = "MeasurementTimestamp",
SemanticId = "urn:nexcode:timestamp",
ValueType = "dateTime",
Value = DateTime.UtcNow.ToString("O")
}
};

ISubmodel submodel = new AasSubmodel
{
IdShort = $"EnergyMonitoring_{assetId}",
SemanticId = SemanticId,
Elements = elements
};

return Task.FromResult(submodel);
}

public Task<byte[]> SerializeAsync(ISubmodel submodel, CancellationToken ct = default)
{
// JSON 직렬화
var json = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(submodel);
return Task.FromResult(json);
}

public Task<ISubmodel> DeserializeAsync(byte[] data, CancellationToken ct = default)
{
var submodel = System.Text.Json.JsonSerializer.Deserialize<AasSubmodel>(data)!;
return Task.FromResult<ISubmodel>(submodel);
}
}

DI에 등록​

builder.Services.AddCaffeineAas(aas =>
{
// 기본 Provider
aas.AddSubmodelProvider<NameplateSubmodelProvider>()
.AddSubmodelProvider<TechnicalDataSubmodelProvider>()
// 커스텀 Provider 추가
.AddSubmodelProvider<EnergyMonitoringSubmodelProvider>()
.WithAasxPackageSupport();
});

검증​

등록된 모든 Provider를 확인하는 엔드포인트를 추가합니다:

app.MapGet("/aas/providers", (IEnumerable<ISubmodelProvider> providers) =>
providers.Select(p => new { p.SubmodelType, p.SemanticId }));
curl http://localhost:5000/aas/providers

예상 출력:

[
{ "submodelType": "Nameplate", "semanticId": "..." },
{ "submodelType": "TechnicalData", "semanticId": "..." },
{ "submodelType": "EnergyMonitoring", "semanticId": "urn:nexcode:caffeine:submodel:energy-monitoring:1.0" }
]

3단계: AASX Import/Export​

AASX는 IEC 63278-2 표준 패키지 포맷입니다. ZIP 기반으로 AAS Shell과 서브모델을 하나의 파일로 묶습니다.

AASX 파일 구조​

my-equipment.aasx (ZIP)
├── aas/
│ └── aas.json ← AAS Shell + 서브모델 JSON
├── _rels/
│ └── .rels ← 관계 정의
└── [Content_Types].xml ← MIME 타입 정의

AAS Shell 생성 및 AASX 내보내기​

using Caffeine.AAS.Serialization;

app.MapPost("/aas/export", async (
IAasRepository repository,
AasxPackageHandler packageHandler) =>
{
// 1. AAS Shell 생성
var shell = new AasShell
{
IdShort = "CVD-Equipment-001",
GlobalAssetId = "urn:nexcode:asset:cvd:001",
AssetKind = AssetKind.Instance
};

// 2. 서브모델 연결
shell.SubmodelIds.Add("EnergyMonitoring_cvd001");
shell.SubmodelIds.Add("Nameplate_cvd001");

// 3. 저장소에 저장
await repository.SaveAsync(shell);

// 4. AASX 파일로 내보내기
var aasxBytes = await packageHandler.ExportAsync("CVD-Equipment-001");

return Results.File(aasxBytes, "application/asset-administration-shell-package",
"cvd-equipment-001.aasx");
});

AASX 가져오기​

app.MapPost("/aas/import", async (
IFormFile file,
AasxPackageHandler packageHandler) =>
{
using var stream = file.OpenReadStream();
var shells = await packageHandler.ImportAsync(stream);

return Results.Ok(new
{
ImportedShells = shells.Count(),
ShellIds = shells.Select(s => s.IdShort)
});
});

테스트​

# AASX 내보내기
curl -X POST http://localhost:5000/aas/export -o cvd-equipment.aasx

# AASX 가져오기
curl -X POST http://localhost:5000/aas/import \
-F "file=@cvd-equipment.aasx"

4단계: AAS Protocol Server (REST API)​

Caffeine V3는 ISP-6 표준의 6개 Operation을 REST API로 노출합니다.

ISP-6 Operation 매핑​

ISP-6 OperationHTTP Method경로
GetAllAssetAdministrationShellsGET/api/v3/aas/shells
GetAssetAdministrationShellGET/api/v3/aas/shells/{id}
PutAssetAdministrationShellPUT/api/v3/aas/shells/{id}
DeleteAssetAdministrationShellDELETE/api/v3/aas/shells/{id}
GetAllSubmodelsGET/api/v3/aas/shells/{id}/submodels
GetSubmodelGET/api/v3/aas/submodels/{id}

REST API 구성​

// Program.cs에 AAS REST 엔드포인트 매핑
app.MapCaffeineAasEndpoints();

API 호출 실습​

# 전체 AAS Shell 목록 조회
curl http://localhost:5000/api/v3/aas/shells

# 특정 Shell 조회
curl http://localhost:5000/api/v3/aas/shells/CVD-Equipment-001

# Shell의 서브모델 목록
curl http://localhost:5000/api/v3/aas/shells/CVD-Equipment-001/submodels

# Shell 삭제
curl -X DELETE http://localhost:5000/api/v3/aas/shells/CVD-Equipment-001

예상 출력 (Shell 목록):

[
{
"idShort": "CVD-Equipment-001",
"globalAssetId": "urn:nexcode:asset:cvd:001",
"assetKind": "Instance",
"submodelIds": ["EnergyMonitoring_cvd001", "Nameplate_cvd001"]
}
]

5단계: 검증 및 정리​

전체 동작 검증 체크리스트​

  • Custom EnergyMonitoringSubmodelProvider가 DI에 등록되어 /aas/providers에 표시
  • BuildSubmodelAsync()로 서브모델이 올바르게 생성
  • AASX 파일 내보내기 → 다운로드 가능
  • AASX 파일 가져오기 → Shell 복원 확인
  • REST API로 Shell CRUD 정상 동작

배운 내용 정리​

항목설명
ISubmodelProvider도메인 특화 서브모델 빌드/직렬화 인터페이스
AasBuilderFluent API로 AAS 모듈 구성 (Provider, Mapper, Serializer)
AasxPackageHandlerIEC 63278-2 표준 AASX 패키지 Import/Export
MapCaffeineAasEndpoints()ISP-6 6개 Operation을 REST API로 노출

다음 단계​


문제 해결​

Q: Provider가 /aas/providers에 표시되지 않습니다.

AddCaffeineAas() 블록 내에서 .AddSubmodelProvider<T>()를 호출했는지 확인하세요.

Q: AASX 내보내기 시 빈 파일이 생성됩니다.

WithAasxPackageSupport()가 AasBuilder에 등록되어 있는지 확인하세요.

Q: REST API 엔드포인트가 404를 반환합니다.

app.MapCaffeineAasEndpoints()가 app.Run() 전에 호출되어 있는지 확인하세요.