Skip to main content

dpp_aas/
model.rs

1use serde::{Deserialize, Serialize};
2
3// ─── Semantic ID reference ────────────────────────────────────────────────────
4
5/// IDTA AAS Part 1 §5.3.11 Key — one segment of a semantic identifier reference.
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
7#[serde(rename_all = "camelCase")]
8pub struct AasSemIdKey {
9    #[serde(rename = "type")]
10    pub key_type: String,
11    pub value: String,
12}
13
14/// IDTA AAS Part 1 §5.3.11 Reference — typed container for semantic identifiers.
15///
16/// External semantic IDs (ECLASS IRDIs, IDTA URNs, Catena-X URNs) use
17/// `type = "ExternalReference"` with a single `GlobalReference` key.
18#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
19#[serde(rename_all = "camelCase")]
20pub struct AasSemId {
21    #[serde(rename = "type")]
22    pub ref_type: String,
23    pub keys: Vec<AasSemIdKey>,
24}
25
26impl AasSemId {
27    /// Build an external semantic ID reference from a URI or IRDI string.
28    pub fn external(value: &str) -> Self {
29        Self {
30            ref_type: "ExternalReference".into(),
31            keys: vec![AasSemIdKey {
32                key_type: "GlobalReference".into(),
33                value: value.to_owned(),
34            }],
35        }
36    }
37}
38
39// ─── Submodel element types ───────────────────────────────────────────────────
40
41/// AAS data type for `Property` values.
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
43#[serde(rename_all = "camelCase")]
44pub enum AasDataType {
45    #[serde(rename = "xs:string")]
46    String,
47    #[serde(rename = "xs:double")]
48    Double,
49    #[serde(rename = "xs:integer")]
50    Integer,
51    #[serde(rename = "xs:boolean")]
52    Boolean,
53    #[serde(rename = "xs:dateTime")]
54    DateTime,
55}
56
57/// An AAS Property — a single leaf-level value.
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
59#[serde(rename_all = "camelCase")]
60pub struct AasProperty {
61    pub id_short: String,
62    pub value_type: AasDataType,
63    /// Value serialised as a string (AAS convention for all types).
64    pub value: String,
65    /// Physical unit of the value, e.g. `"kgCO2e"`, `"kg"`, `"V"`, `"%"`.
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub unit: Option<String>,
68    /// Semantic identifier per IDTA AAS Part 1 §5.3.11 Reference type.
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub semantic_id: Option<AasSemId>,
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub description: Option<String>,
73}
74
75/// An AAS SubmodelElementCollection — a named group of elements.
76#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
77#[serde(rename_all = "camelCase")]
78pub struct AasCollection {
79    pub id_short: String,
80    pub value: Vec<AasSubmodelElement>,
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub semantic_id: Option<AasSemId>,
83}
84
85/// An AAS ReferenceElement — an external link (URL/URN).
86///
87/// Used for repair manuals, due-diligence documents, disassembly instructions,
88/// and other external resources linked from DPP fields.
89#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
90#[serde(rename_all = "camelCase")]
91pub struct AasReference {
92    pub id_short: String,
93    /// Target URI or URN.
94    pub value: String,
95    #[serde(skip_serializing_if = "Option::is_none")]
96    pub semantic_id: Option<AasSemId>,
97}
98
99/// AAS SubmodelElement — a property, collection, or external reference.
100#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
101#[serde(tag = "modelType")]
102pub enum AasSubmodelElement {
103    Property(AasProperty),
104    SubmodelElementCollection(AasCollection),
105    Reference(AasReference),
106}
107
108/// An AAS Submodel — one named grouping of product data.
109#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
110#[serde(rename_all = "camelCase")]
111pub struct AasSubmodel {
112    pub id: String,
113    pub id_short: String,
114    /// IDTA AAS Part 2 §5.2.4: `"Submodel"` for all AAS submodels.
115    pub model_type: String,
116    /// IDTA AAS Part 2 §5.2.4: `"Instance"` for runtime data (not templates).
117    pub kind: String,
118    #[serde(skip_serializing_if = "Option::is_none")]
119    pub semantic_id: Option<AasSemId>,
120    pub submodel_elements: Vec<AasSubmodelElement>,
121}
122
123// ─── Shell container types ────────────────────────────────────────────────────
124
125/// A name/value specific asset identifier (e.g., `gtin`, `batchId`).
126#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
127#[serde(rename_all = "camelCase")]
128pub struct SpecificAssetId {
129    pub name: String,
130    pub value: String,
131}
132
133/// AAS asset identification block.
134///
135/// `global_asset_id` is the canonical URI for the physical product built from
136/// the GTIN. `specific_asset_ids` carry GTIN, batch, and serial identifiers
137/// for GS1 Digital Link resolution and registry look-up.
138#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
139#[serde(rename_all = "camelCase")]
140pub struct AssetInformation {
141    pub global_asset_id: String,
142    pub specific_asset_ids: Vec<SpecificAssetId>,
143}
144
145/// A reference from an `AasShell` to one of its submodels (ID only).
146#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
147#[serde(rename_all = "camelCase")]
148pub struct AasSubmodelRef {
149    pub id: String,
150}
151
152/// AAS Shell — the top-level container for a product's digital twin.
153///
154/// Holds the asset identification and references to submodels. Submodels are
155/// served alongside the shell as `Vec<AasSubmodel>` from [`build_aas_from_passport`](super::build_aas_from_passport)
156/// and would be exposed from separate API endpoints in a running AAS server
157/// (`/shells/{aasId}` vs. `/submodels/{submodelId}`).
158#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
159#[serde(rename_all = "camelCase")]
160pub struct AasShell {
161    pub id: String,
162    pub id_short: String,
163    /// IDTA AAS Part 2 §5.2.4: always `"AssetAdministrationShell"`.
164    pub model_type: String,
165    /// IDTA AAS Part 2 §5.2.4: `"Instance"` for runtime data (not templates).
166    pub kind: String,
167    pub asset_information: AssetInformation,
168    pub submodels: Vec<AasSubmodelRef>,
169}
170
171// ─── Environment ──────────────────────────────────────────────────────────────
172
173/// IDTA AAS `Environment` — the self-contained document form, carrying shells
174/// and submodels together in one payload.
175///
176/// This is the *file* shape, not the API shape. A running AAS server serves
177/// [`AasShell`] and [`AasSubmodel`] from separate endpoints
178/// (`/shells/{aasId}`, `/submodels/{submodelId}`); an `Environment` is what you
179/// serve when a caller wants the whole twin in one response, and it is what an
180/// AASX package contains.
181///
182/// It lives here rather than in a consumer because there must be exactly one
183/// serialisation of it: an HTTP door and an AASX package that each built their
184/// own would drift, and the drift would be invisible until a partner's tooling
185/// disagreed with ours.
186///
187/// **No conformance is claimed.** These field names follow the IDTA JSON
188/// serialisation, but nothing here has been checked against the published
189/// schema or a reference implementation. Validating against a vendored AAS
190/// schema and IDTA's own test engine is separate, deliberate work.
191#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
192#[serde(rename_all = "camelCase")]
193pub struct AasEnvironment {
194    pub asset_administration_shells: Vec<AasShell>,
195    pub submodels: Vec<AasSubmodel>,
196    /// Always empty: this crate coins no concept descriptions. Emitted rather
197    /// than omitted so the document shape is stable for a consumer that
198    /// iterates it, and so adding them later is not a shape change.
199    pub concept_descriptions: Vec<serde_json::Value>,
200}