본문으로 건너뛰기

AAS 핸즈온 랩

반도체 CVD(Chemical Vapor Deposition) 장비를 IEC 63278 AAS로 모델링하는 실전 핸즈온 랩입니다.

학습 시간: 약 2시간
난이도: 중급 ~ 고급
사전 지식: AAS 시작하기 완료


Lab 개요​

시나리오​

반도체 CVD 장비는 기판 위에 화학 기상 증착으로 박막을 형성하는 장비입니다. 이 랩에서는 CVD 장비의 핵심 공정 파라미터를 AAS로 모델링합니다.

모델링 대상​

파라미터태그명단위설명
챔버 온도CVD01.ChamberTempdegC공정 챔버 내부 온도
챔버 압력CVD01.ChamberPressurePa공정 챔버 진공 압력
N2 가스 유량CVD01.N2Flowsccm질소 가스 유량
SiH4 가스 유량CVD01.SiH4Flowsccm실란 가스 유량
RF PowerCVD01.RFPowerW플라즈마 RF 출력

완성 목표​


Lab 1: 장비 매니페스트 정의​

목표​

CVD 장비의 Caffeine 매니페스트와 태그 정의를 작성합니다.

코드​

using Caffeine.Core.Models;

// 장비 매니페스트 정의
var manifest = new EquipmentManifest
{
EquipmentId = "CVD-001",
Name = "CVD_Equipment_001",
Manufacturer = "NEXCODE",
Model = "CVD-PRO-3000",
SerialNumber = "CVD-2024-001",
InstallationDate = new DateTimeOffset(2024, 6, 15, 0, 0, 0, TimeSpan.FromHours(9)),
Location = "FAB-A, Bay-03, Slot-12"
};

// 태그 정의 추가
manifest.Tags.Add(new TagDefinition
{
Name = "CVD01.ChamberTemp",
DataType = "Float",
PhysicalQuantity = "Temperature",
Unit = "degC",
Description = "CVD 챔버 내부 온도",
MinValue = 0,
MaxValue = 800
});

manifest.Tags.Add(new TagDefinition
{
Name = "CVD01.ChamberPressure",
DataType = "Float",
PhysicalQuantity = "Pressure",
Unit = "Pa",
Description = "CVD 챔버 진공 압력",
MinValue = 0,
MaxValue = 101325
});

manifest.Tags.Add(new TagDefinition
{
Name = "CVD01.N2Flow",
DataType = "Float",
PhysicalQuantity = "Flow",
Unit = "sccm",
Description = "질소 가스 유량",
MinValue = 0,
MaxValue = 5000
});

manifest.Tags.Add(new TagDefinition
{
Name = "CVD01.SiH4Flow",
DataType = "Float",
PhysicalQuantity = "Flow",
Unit = "sccm",
Description = "실란 가스 유량",
MinValue = 0,
MaxValue = 1000
});

manifest.Tags.Add(new TagDefinition
{
Name = "CVD01.RFPower",
DataType = "Float",
PhysicalQuantity = "Power",
Unit = "W",
Description = "플라즈마 RF 출력",
MinValue = 0,
MaxValue = 3000
});

예상 결과​

  • 장비 매니페스트 1개 (CVD-001)
  • 태그 정의 5개 (ChamberTemp, ChamberPressure, N2Flow, SiH4Flow, RFPower)

Lab 2: AAS Shell 생성​

목표​

Lab 1에서 정의한 매니페스트를 AAS Shell로 변환합니다.

코드​

// IManifestToAasConverter 주입 (DI 컨테이너에서)
var converter = serviceProvider.GetRequiredService<IManifestToAasConverter>();

// 매니페스트 → AAS Shell 변환
AssetAdministrationShell shell = await converter.ToAasShellAsync(
manifest,
cancellationToken);

// 결과 확인
Console.WriteLine($"=== AAS Shell 생성 완료 ===");
Console.WriteLine($"AAS ID: {shell.Id}");
Console.WriteLine($"IdShort: {shell.IdShort}");
Console.WriteLine($"Asset Kind: {shell.AssetInformation.AssetKind}");
Console.WriteLine($"Global Asset ID: {shell.AssetInformation.GlobalAssetId}");
Console.WriteLine($"서브모델 참조 수: {shell.Submodels.Count}");

foreach (var smRef in shell.Submodels)
{
Console.WriteLine($" - {smRef.Keys[0].Value}");
}

예상 출력​

=== AAS Shell 생성 완료 ===
AAS ID: urn:caffeine:aas:CVD-001
IdShort: CVD_Equipment_001
Asset Kind: Instance
Global Asset ID: urn:caffeine:asset:CVD-001
서브모델 참조 수: 3
- urn:caffeine:sm:nameplate:CVD-001
- urn:caffeine:sm:technical-data:CVD-001
- urn:caffeine:sm:operational-data:CVD-001
팁

ToAasShellAsync()는 등록된 ISubmodelProvider를 자동 탐색하여, 매니페스트에 해당하는 서브모델 참조를 Shell에 추가합니다.


Lab 3: Nameplate 서브모델 빌드​

목표​

장비 명판(Nameplate) 정보를 IDTA 02006-2-0 표준 서브모델로 빌드합니다.

코드​

var nameplateProvider = serviceProvider
.GetRequiredService<IEnumerable<ISubmodelProvider>>()
.First(p => p.SubmodelType == "Nameplate");

Submodel nameplate = await nameplateProvider.BuildSubmodelAsync(
manifest,
cancellationToken);

// 서브모델 요소 출력
Console.WriteLine($"=== Nameplate 서브모델 ===");
Console.WriteLine($"ID: {nameplate.Id}");
Console.WriteLine($"SemanticId: {nameplate.SemanticId}");

foreach (var element in nameplate.SubmodelElements)
{
if (element is Property property)
{
Console.WriteLine($" {property.IdShort}: {property.Value} ({property.ValueType})");
}
}

예상 출력​

=== Nameplate 서브모델 ===
ID: urn:caffeine:sm:nameplate:CVD-001
SemanticId: urn:idta:submodel:nameplate:2.0
ManufacturerName: NEXCODE (xs:string)
ManufacturerProductDesignation: CVD-PRO-3000 (xs:string)
SerialNumber: CVD-2024-001 (xs:string)
YearOfConstruction: 2024 (xs:integer)
PhysicalAddress/Street: FAB-A, Bay-03, Slot-12 (xs:string)

Lab 4: TechnicalData 서브모델​

목표​

태그의 물리량 사양을 IDTA 02003-1-2 TechnicalData 서브모델로 빌드합니다.

코드​

var techDataProvider = serviceProvider
.GetRequiredService<IEnumerable<ISubmodelProvider>>()
.First(p => p.SubmodelType == "TechnicalData");

Submodel technicalData = await techDataProvider.BuildSubmodelAsync(
manifest,
cancellationToken);

Console.WriteLine($"=== TechnicalData 서브모델 ===");
Console.WriteLine($"ID: {technicalData.Id}");

foreach (var element in technicalData.SubmodelElements)
{
if (element is SubmodelElementCollection collection)
{
Console.WriteLine($"\n [{collection.IdShort}]");
foreach (var child in collection.Value)
{
if (child is Property prop)
{
Console.WriteLine($" {prop.IdShort}: {prop.Value} {GetUnit(prop)}");
}
}
}
}

예상 출력​

=== TechnicalData 서브모델 ===
ID: urn:caffeine:sm:technical-data:CVD-001

[ChamberTemp]
MaxValue: 800 degC
MinValue: 0 degC
SemanticId: 0173-1#02-AAI835#001

[ChamberPressure]
MaxValue: 101325 Pa
MinValue: 0 Pa
SemanticId: 0173-1#02-AAI836#001

[N2Flow]
MaxValue: 5000 sccm
MinValue: 0 sccm
SemanticId: 0173-1#02-AAI837#001

[SiH4Flow]
MaxValue: 1000 sccm
MinValue: 0 sccm
SemanticId: 0173-1#02-AAI837#001

[RFPower]
MaxValue: 3000 W
MinValue: 0 W
노트

N2Flow와 SiH4Flow는 동일한 Flow 타입이므로 같은 ECLASS IRDI(0173-1#02-AAI837#001)가 매핑됩니다.


Lab 5: IEC 63278-1 JSON 출력​

목표​

AAS Shell과 서브모델을 IEC 63278-1 표준 JSON으로 직렬화합니다.

코드​

var serializer = serviceProvider.GetRequiredService<IAasSerializer>();

// Shell JSON 생성
string shellJson = await serializer.SerializeShellAsync(shell, cancellationToken);
Console.WriteLine("=== AAS Shell JSON ===");
Console.WriteLine(shellJson);

// Submodel JSON 생성
string nameplateJson = await serializer.SerializeSubmodelAsync(
nameplate,
cancellationToken);
Console.WriteLine("\n=== Nameplate Submodel JSON ===");
Console.WriteLine(nameplateJson);

// 파일로 저장
await File.WriteAllTextAsync("cvd-001-shell.json", shellJson);
await File.WriteAllTextAsync("cvd-001-nameplate.json", nameplateJson);

예상 출력 (Shell JSON)​

{
"modelType": "AssetAdministrationShell",
"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": [
{
"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"
}
]
}
]
}

Lab 6: AASX 패키지 생성​

목표​

Shell과 서브모델을 하나의 AASX 패키지 파일로 Export합니다.

코드​

var packageHandler = serviceProvider.GetRequiredService<IAasxPackageHandler>();

// 모든 서브모델 수집
var submodels = new List<Submodel> { nameplate, technicalData, operationalData };

// AASX Export
string outputPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
"cvd-001.aasx");

await packageHandler.ExportAsync(
shell,
submodels,
outputPath,
cancellationToken);

Console.WriteLine($"AASX 패키지 생성 완료: {outputPath}");

// 파일 크기 확인
var fileInfo = new FileInfo(outputPath);
Console.WriteLine($"파일 크기: {fileInfo.Length / 1024.0:F1} KB");

예상 출력​

AASX 패키지 생성 완료: /Users/me/Desktop/cvd-001.aasx
파일 크기: 12.3 KB

AASX 패키지 구조​

생성된 .aasx 파일은 ZIP 포맷이며, 다음과 같은 내부 구조를 가집니다.

cvd-001.aasx (ZIP)
├── [Content_Types].xml
├── aasx/
│ ├── aas-shell.json
│ ├── submodels/
│ │ ├── nameplate.json
│ │ ├── technical-data.json
│ │ └── operational-data.json
│ └── aasx-origin
└── _rels/
└── .rels
팁

AASX 파일은 Eclipse AASX Package Explorer, BaSyx 등 표준 도구에서 열어서 확인할 수 있습니다.


Lab 7: REST API로 CRUD​

목표​

REST API를 통해 AAS를 등록, 조회, 수정, 삭제합니다.

AAS 등록 (Create)​

curl -X POST http://localhost:5000/api/v1/aas \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d @cvd-001-shell.json

응답 (201 Created):

{
"id": "urn:caffeine:aas:CVD-001",
"idShort": "CVD_Equipment_001",
"assetInformation": {
"assetKind": "Instance",
"globalAssetId": "urn:caffeine:asset:CVD-001"
}
}

AAS 조회 (Read)​

curl -X GET http://localhost:5000/api/v1/aas/urn:caffeine:aas:CVD-001 \
-H "Authorization: Bearer {token}"

AAS 수정 (Update)​

curl -X PUT http://localhost:5000/api/v1/aas/urn:caffeine:aas:CVD-001 \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"idShort": "CVD_Equipment_001_Updated",
"assetInformation": {
"assetKind": "Instance",
"globalAssetId": "urn:caffeine:asset:CVD-001"
}
}'

응답 (200 OK)

AAS 삭제 (Delete)​

curl -X DELETE http://localhost:5000/api/v1/aas/urn:caffeine:aas:CVD-001 \
-H "Authorization: Bearer {token}"

응답 (204 No Content)

주의

AAS 삭제 시 연결된 서브모델은 삭제되지 않습니다. 서브모델은 별도로 관리됩니다.


Lab 8: 태그 ↔ AAS 양방향 변환​

목표​

Caffeine 태그와 AAS SubmodelElement 간 양방향 변환을 수행합니다.

태그 → AAS 변환​

var tagToAas = serviceProvider.GetRequiredService<ITagToAasConverter>();

// 단일 태그 변환
var chamberTempTag = manifest.Tags.First(t => t.Name == "CVD01.ChamberTemp");
SubmodelElement element = tagToAas.Convert(chamberTempTag);

Console.WriteLine($"=== 태그 → AAS 변환 ===");
Console.WriteLine($"태그명: {chamberTempTag.Name}");
Console.WriteLine($"→ IdShort: {element.IdShort}");
Console.WriteLine($"→ ValueType: {((Property)element).ValueType}");
Console.WriteLine($"→ SemanticId: {element.SemanticId}");

예상 출력​

=== 태그 → AAS 변환 ===
태그명: CVD01.ChamberTemp
→ IdShort: ChamberTemp
→ ValueType: xs:float
→ SemanticId: 0173-1#02-AAI835#001

AAS → 태그 변환​

var aasToTag = serviceProvider.GetRequiredService<IAasToTagConverter>();

// SubmodelElement → TagDefinition
TagDefinition convertedTag = aasToTag.Convert(element);

Console.WriteLine($"\n=== AAS → 태그 변환 ===");
Console.WriteLine($"IdShort: {element.IdShort}");
Console.WriteLine($"→ 태그명: {convertedTag.Name}");
Console.WriteLine($"→ DataType: {convertedTag.DataType}");
Console.WriteLine($"→ PhysicalQuantity: {convertedTag.PhysicalQuantity}");

예상 출력​

=== AAS → 태그 변환 ===
IdShort: ChamberTemp
→ 태그명: ChamberTemp
→ DataType: Float
→ PhysicalQuantity: Temperature

일괄 변환​

// 전체 태그 → AAS 일괄 변환
IReadOnlyList<SubmodelElement> elements = tagToAas.ConvertMany(manifest.Tags);

Console.WriteLine($"\n=== 일괄 변환 결과 ({elements.Count}개) ===");
foreach (var elem in elements)
{
var prop = (Property)elem;
Console.WriteLine($" {prop.IdShort}: {prop.ValueType} (SemanticId: {elem.SemanticId})");
}

Lab 9: 커스텀 SemanticIdMapper​

목표​

기본 ECLASS 매핑에 없는 CVD 전용 물리량에 대해 커스텀 SemanticId를 매핑합니다.

커스텀 매퍼 구현​

/// <summary>
/// CVD 공정 전용 SemanticId 매퍼.
/// 표준 ECLASS에 없는 반도체 공정 파라미터를 매핑합니다.
/// </summary>
public class CvdSemanticIdMapper : ISemanticIdMapper
{
private static readonly Dictionary<string, string> Mappings = new()
{
["RFPower"] = "0173-1#02-CVD001#001", // RF 플라즈마 출력
["GasFlow_N2"] = "0173-1#02-CVD002#001", // N2 가스 유량
["GasFlow_SiH4"] = "0173-1#02-CVD003#001", // SiH4 가스 유량
["DepositionRate"] = "0173-1#02-CVD004#001", // 증착 속도
["FilmThickness"] = "0173-1#02-CVD005#001", // 박막 두께
};

public SemanticId? Map(string tagType)
{
return Mappings.TryGetValue(tagType, out var irdi)
? new SemanticId(irdi)
: null;
}

public bool CanMap(string tagType) => Mappings.ContainsKey(tagType);
}

등록​

services.AddCaffeineAas()
.AddSemanticIdMapper<CvdSemanticIdMapper>() // 우선순위 1: CVD 전용
.AddSemanticIdMapper<EclassSemanticIdMapper>(); // 우선순위 2: ECLASS 기본

동작 확인​

var mapper = serviceProvider.GetRequiredService<ISemanticIdMapper>();

// CVD 전용 매핑
Console.WriteLine(mapper.Map("RFPower")); // 0173-1#02-CVD001#001
Console.WriteLine(mapper.Map("GasFlow_N2")); // 0173-1#02-CVD002#001

// ECLASS 기본 매핑으로 폴백
Console.WriteLine(mapper.Map("Temperature")); // 0173-1#02-AAI835#001
Console.WriteLine(mapper.Map("Pressure")); // 0173-1#02-AAI836#001

// 어디에도 없는 타입
Console.WriteLine(mapper.Map("UnknownType")); // null
팁

ChainedSemanticIdMapper가 내부적으로 등록된 모든 매퍼를 순서대로 시도합니다. 첫 번째로 null이 아닌 결과를 반환하는 매퍼의 결과가 사용됩니다.


Lab 10: RAG 파이프라인 연동 (Advanced)​

목표​

AAS 문서와 장비 매뉴얼을 RAG 파이프라인에 인제스트하고, 자연어로 질의합니다.

사전 요구사항
  • AddCaffeineAasRag() 활성화
  • 벡터 DB 인프라 구성 완료

Step 1: 문서 인제스트​

var ingestor = serviceProvider.GetRequiredService<IAasDocumentIngestor>();

// AAS 서브모델의 Documentation 첨부 파일 인제스트
await ingestor.IngestFromSubmodelAsync(
shellId: "urn:caffeine:aas:CVD-001",
submodelIdShort: "Documentation",
cancellationToken);

// 외부 PDF 파일 인제스트
await ingestor.IngestFileAsync(
shellId: "urn:caffeine:aas:CVD-001",
filePath: "/manuals/CVD-PRO-3000-Manual.pdf",
cancellationToken);

Console.WriteLine("문서 인제스트 완료");

Step 2: 벡터 검색​

var searcher = serviceProvider.GetRequiredService<IAasVectorSearcher>();

// 자연어 검색
var results = await searcher.SearchAsync(
query: "CVD 챔버 온도 상한 초과 시 조치 방법",
shellId: "urn:caffeine:aas:CVD-001",
maxResults: 5,
cancellationToken);

Console.WriteLine($"=== 검색 결과 ({results.Count}건) ===");
foreach (var result in results)
{
Console.WriteLine($" [{result.Score:F3}] {result.Source}");
Console.WriteLine($" {result.Content[..Math.Min(100, result.Content.Length)]}...");
}

예상 출력​

=== 검색 결과 (3건) ===
[0.923] CVD-PRO-3000-Manual.pdf (p.45)
챔버 온도가 설정값을 초과할 경우, 즉시 RF Power를 차단하고 N2 퍼지를 실행...
[0.891] CVD-PRO-3000-Manual.pdf (p.12)
최대 허용 온도: 800 degC. 정상 공정 온도 범위: 350~550 degC...
[0.856] TechnicalData Submodel
ChamberTemp MaxValue: 800 degC, MinValue: 0 degC...

Step 3: LLM 기반 조치 가이드​

var advisor = serviceProvider.GetRequiredService<IAasMaintenanceAdvisor>();

// 알람 상황에 대한 조치 가이드 요청
var guide = await advisor.GetActionGuideAsync(
shellId: "urn:caffeine:aas:CVD-001",
alarmDescription: "CVD01.ChamberTemp 값이 600 degC로 상한(550 degC) 초과",
cancellationToken);

Console.WriteLine($"=== 조치 가이드 ===");
Console.WriteLine($"심각도: {guide.Severity}");
Console.WriteLine($"즉시 조치:");
foreach (var action in guide.ImmediateActions)
{
Console.WriteLine($" - {action}");
}
Console.WriteLine($"\n근거 문서:");
foreach (var source in guide.Sources)
{
Console.WriteLine($" - {source}");
}

예상 출력​

=== 조치 가이드 ===
심각도: Critical
즉시 조치:
- RF Power 차단 (CVD01.RFPower → 0W)
- N2 퍼지 실행 (CVD01.N2Flow → 2000 sccm)
- 공정 가스 밸브 차단 (CVD01.SiH4Flow → 0 sccm)
- 챔버 쿨다운 대기 (목표: 100 degC 이하)

근거 문서:
- CVD-PRO-3000-Manual.pdf (p.45, 긴급 정지 절차)
- CVD-PRO-3000-Manual.pdf (p.12, 온도 사양)

정리​

완성된 AAS 구조​

Lab별 학습 요약​

Lab주제핵심 인터페이스
1매니페스트 정의EquipmentManifest, TagDefinition
2AAS Shell 생성IManifestToAasConverter
3Nameplate 서브모델ISubmodelProvider
4TechnicalData 서브모델ISubmodelProvider
5JSON 직렬화IAasSerializer
6AASX 패키지IAasxPackageHandler
7REST API CRUDHTTP endpoints
8태그 ↔ AAS 변환ITagToAasConverter, IAasToTagConverter
9커스텀 SemanticIdISemanticIdMapper
10RAG 파이프라인IAasDocumentIngestor, IAasVectorSearcher

다음 단계​