blob: 1730b16aef4b13deddd40545d4de57f222c45c8b [file] [log] [blame]
Constantin Ziesche857c7ab2020-02-25 11:24:51 +01001/*******************************************************************************
2* Copyright (c) 2020 Robert Bosch GmbH
3* Author: Constantin Ziesche (constantin.ziesche@bosch.com)
4*
5* This program and the accompanying materials are made available under the
6* terms of the Eclipse Public License 2.0 which is available at
7* http://www.eclipse.org/legal/epl-2.0
8*
9* SPDX-License-Identifier: EPL-2.0
10*******************************************************************************/
11using BaSyx.Models.Core.AssetAdministrationShell.Generics;
12using BaSyx.Models.Core.AssetAdministrationShell.Generics.SubmodelElementTypes;
13using BaSyx.Models.Core.AssetAdministrationShell.Implementations;
14using BaSyx.Models.Core.AssetAdministrationShell.Implementations.SubmodelElementTypes;
15using BaSyx.Models.Core.AssetAdministrationShell.References;
16using BaSyx.Models.Core.AssetAdministrationShell.Semantics;
17using BaSyx.Models.Core.Common;
18using BaSyx.Models.Export.Converter;
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010019using BaSyx.Models.Export.EnvironmentSubmodelElements;
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010020using BaSyx.Models.Extensions.Semantics.DataSpecifications;
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010021using Newtonsoft.Json;
22using Newtonsoft.Json.Converters;
23using NLog;
24using System;
25using System.Collections.Generic;
26using System.IO;
27using System.Linq;
28using System.Reflection;
29using System.Runtime.Serialization;
30using System.Xml;
31using System.Xml.Schema;
32using System.Xml.Serialization;
33using File = BaSyx.Models.Core.AssetAdministrationShell.Implementations.SubmodelElementTypes.File;
34
35namespace BaSyx.Models.Export
36{
37 [DataContract]
38 [XmlType(AnonymousType = true, Namespace = AAS_NAMESPACE)]
39 [XmlRoot(ElementName = "aasenv", Namespace = AAS_NAMESPACE, IsNullable = false)]
40 public class AssetAdministrationShellEnvironment_V1_0
41 {
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010042 public const string AAS_NAMESPACE = "http://www.admin-shell.io/aas/1/0";
43 public const string IEC61360_NAMESPACE = "http://www.admin-shell.io/IEC61360/1/0";
Constantin Ziesche02817f12020-08-04 21:40:43 +020044 public const string AAS_XSD_FILENAME = "aas-spec-v1.0/AAS.xsd";
45 public const string IEC61360_XSD_FILENAME = "aas-spec-v1.0/IEC61360.xsd";
Constantin Ziesche857c7ab2020-02-25 11:24:51 +010046
47 [DataMember(EmitDefaultValue = false, IsRequired = true, Name = "assetAdministrationShells", Order = 0)]
48 [XmlIgnore, JsonIgnore]
49 public List<IAssetAdministrationShell> AssetAdministrationShells { get; }
50
51 [DataMember(EmitDefaultValue = false, IsRequired = true, Name = "assets", Order = 1)]
52 [XmlIgnore, JsonIgnore]
53 public List<IAsset> Assets { get; }
54
55 [DataMember(EmitDefaultValue = false, IsRequired = true, Name = "submodels", Order = 2)]
56 [XmlIgnore, JsonIgnore]
57 public List<ISubmodel> Submodels { get; }
58
59 [DataMember(EmitDefaultValue = false, IsRequired = true, Name = "conceptDescriptions", Order = 3)]
60 [XmlIgnore, JsonIgnore]
61 public List<IConceptDescription> ConceptDescriptions { get; }
62
63 [JsonProperty("assetAdministrationShells")]
64 [XmlArray("assetAdministrationShells")]
65 [XmlArrayItem("assetAdministrationShell")]
66 public List<EnvironmentAssetAdministationShell_V1_0> EnvironmentAssetAdministationShells { get; set; }
67
68 [JsonProperty("assets")]
69 [XmlArray("assets")]
70 [XmlArrayItem("asset")]
71 public List<EnvironmentAsset_V1_0> EnvironmentAssets { get; set; }
72
73 [JsonProperty("submodels")]
74 [XmlArray("submodels")]
75 [XmlArrayItem("submodel")]
76 public List<EnvironmentSubmodel_V1_0> EnvironmentSubmodels { get; set; }
77
78 [JsonProperty("conceptDescriptions")]
79 [XmlArray("conceptDescriptions")]
80 [XmlArrayItem("conceptDescription")]
81 public List<EnvironmentConceptDescription_V1_0> EnvironmentConceptDescriptions { get; set; }
82
83 [IgnoreDataMember]
84 [XmlIgnore]
85 public Dictionary<string, IFile> SupplementalFiles;
86
87 private string ContentRoot = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
88
89 public static JsonSerializerSettings JsonSettings;
90 private static Logger logger = LogManager.GetCurrentClassLogger();
91
92 public static XmlReaderSettings XmlSettings;
93
94 static AssetAdministrationShellEnvironment_V1_0()
95 {
96 JsonSettings = new JsonSerializerSettings()
97 {
98 Formatting = Newtonsoft.Json.Formatting.Indented,
99 DefaultValueHandling = DefaultValueHandling.Include,
100 NullValueHandling = NullValueHandling.Ignore
101 };
102 JsonSettings.Converters.Add(new StringEnumConverter());
103
104 XmlSettings = new XmlReaderSettings();
105 XmlSettings.ValidationType = ValidationType.Schema;
106 XmlSettings.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema;
107 XmlSettings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation;
108 XmlSettings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
109 XmlSettings.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);
110 XmlSettings.Schemas.Add(AAS_NAMESPACE, AAS_XSD_FILENAME);
111 XmlSettings.Schemas.Add(IEC61360_NAMESPACE, IEC61360_XSD_FILENAME);
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100112 }
113
114 [JsonConstructor]
115 protected AssetAdministrationShellEnvironment_V1_0()
116 {
117 AssetAdministrationShells = new List<IAssetAdministrationShell>();
118 Submodels = new List<ISubmodel>();
119 Assets = new List<IAsset>();
120 ConceptDescriptions = new List<IConceptDescription>();
121 SupplementalFiles = new Dictionary<string, IFile>();
122
123 EnvironmentAssetAdministationShells = new List<EnvironmentAssetAdministationShell_V1_0>();
124 EnvironmentAssets = new List<EnvironmentAsset_V1_0>();
125 EnvironmentSubmodels = new List<EnvironmentSubmodel_V1_0>();
126 EnvironmentConceptDescriptions = new List<EnvironmentConceptDescription_V1_0>();
127 }
128
129 public AssetAdministrationShellEnvironment_V1_0(params IAssetAdministrationShell[] assetAdministrationShells) : this()
130 {
131 foreach (var aas in assetAdministrationShells)
132 AddAssetAdministationShell(aas);
133
134 ConvertToEnvironment();
135 }
136
137 public void AddAssetAdministationShell(IAssetAdministrationShell aas)
138 {
139 AssetAdministrationShells.Add(aas);
140 Assets.Add(aas.Asset);
141 if (aas.Submodels?.Count > 0)
142 {
143 Submodels.AddRange(aas.Submodels);
144 foreach (var submodel in aas.Submodels)
145 {
146 ExtractAndClearConceptDescriptions(submodel.SubmodelElements);
147 ExtractSupplementalFiles(submodel.SubmodelElements);
148 ResetConstraints(submodel.SubmodelElements);
Constantin Ziesche635c2e32020-02-27 18:11:07 +0100149 DeleteEvents(submodel.SubmodelElements);
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100150 }
151 }
152 }
153
154 private void ConvertToEnvironment()
155 {
156 foreach (var asset in Assets)
157 {
158 EnvironmentAsset_V1_0 envAsset = new EnvironmentAsset_V1_0()
159 {
160 Administration = asset.Administration,
161 AssetIdentificationModelReference = asset.AssetIdentificationModel?.ToEnvironmentReference_V1_0(),
162 Category = asset.Category,
163 Description = asset.Description,
164 Identification = asset.Identification,
165 IdShort = asset.IdShort,
166 Kind = asset.Kind
167 };
168 EnvironmentAssets.Add(envAsset);
169 }
170 foreach (var conceptDescription in ConceptDescriptions)
171 {
172 EmbeddedDataSpecification_V1_0 embeddedDataSpecification = null;
173 var dataSpecification = conceptDescription.EmbeddedDataSpecifications?.FirstOrDefault();
174 if(dataSpecification != null && dataSpecification.DataSpecificationContent is DataSpecificationIEC61360Content dataSpecificationContent)
175 {
176 embeddedDataSpecification = new EmbeddedDataSpecification_V1_0()
177 {
178 HasDataSpecification = dataSpecification.HasDataSpecification?.ToEnvironmentReference_V1_0(),
179 DataSpecificationContent = new DataSpecificationContent_V1_0()
180 {
181 DataSpecificationIEC61360 = dataSpecificationContent.ToEnvironmentDataSpecificationIEC61360_V1_0()
182 }
183 };
184 }
185
186 EnvironmentConceptDescription_V1_0 environmentConceptDescription = new EnvironmentConceptDescription_V1_0()
187 {
188 Administration = conceptDescription.Administration,
189 Category = conceptDescription.Category,
190 Description = conceptDescription.Description,
191 Identification = conceptDescription.Identification,
192 IdShort = conceptDescription.IdShort,
193 Parent = conceptDescription.Parent?.First?.Value,
194 IsCaseOf = conceptDescription.IsCaseOf?.ToList()?.ConvertAll(c => c.ToEnvironmentReference_V1_0()),
195 EmbeddedDataSpecification = embeddedDataSpecification
196 };
Constantin Ziesche635c2e32020-02-27 18:11:07 +0100197
198 if (EnvironmentConceptDescriptions.Find(m => m.Identification.Id == conceptDescription.Identification.Id) == null)
199 EnvironmentConceptDescriptions.Add(environmentConceptDescription);
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100200 }
201 foreach (var assetAdministrationShell in AssetAdministrationShells)
202 {
203 EnvironmentAssetAdministationShell_V1_0 environmentAssetAdministationShell = new EnvironmentAssetAdministationShell_V1_0()
204 {
205 Administration = assetAdministrationShell.Administration,
206 Category = assetAdministrationShell.Category,
207 Description = assetAdministrationShell.Description,
208 IdShort = assetAdministrationShell.IdShort,
209 Identification = assetAdministrationShell.Identification,
210 AssetReference = assetAdministrationShell.Asset?.ToEnvironmentReference_V1_0(),
211 SubmodelReferences = assetAdministrationShell.Submodels?.ToList().ConvertAll(c => c.ToEnvironmentReference_V1_0()),
212 Views = null,
213 ConceptDictionaries = null
214 };
215 EnvironmentAssetAdministationShells.Add(environmentAssetAdministationShell);
216 }
217 foreach (var submodel in Submodels)
218 {
219 EnvironmentSubmodel_V1_0 environmentSubmodel = new EnvironmentSubmodel_V1_0()
220 {
221 Administration = submodel.Administration,
222 Category = submodel.Category,
223 Description = submodel.Description,
224 Identification = submodel.Identification,
225 IdShort = submodel.IdShort,
226 Kind = submodel.Kind,
227 Parent = submodel.Parent?.First?.Value,
228 Qualifier = null,
229 SemanticId = submodel.SemanticId?.ToEnvironmentReference_V1_0(),
230 SubmodelElements = submodel.SubmodelElements.ToList().ConvertAll(c => c.ToEnvironmentSubmodelElement_V1_0())
231 };
232 EnvironmentSubmodels.Add(environmentSubmodel);
233 }
234 }
235
236
237 private void ExtractSupplementalFiles(IElementContainer<ISubmodelElement> submodelElements)
238 {
239 foreach (var smElement in submodelElements)
240 {
241 if (smElement is File file)
242 {
243 string filePath = ContentRoot + file.Value.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
244 if (System.IO.File.Exists(filePath))
245 {
246 string destinationPath = file.Value;
247 if (!destinationPath.StartsWith(AASX.AASX_FOLDER))
248 destinationPath = AASX.AASX_FOLDER + destinationPath;
249
250 file.Value = destinationPath;
251 SupplementalFiles.Add(filePath, file);
252 }
253 }
254 else if (smElement.ModelType == ModelType.SubmodelElementCollection)
255 ExtractSupplementalFiles((smElement as SubmodelElementCollection).Value);
256 }
257 }
258
259 private void ExtractAndClearConceptDescriptions(IElementContainer<ISubmodelElement> submodelElements)
260 {
261 foreach (var smElement in submodelElements)
262 {
263 if (smElement.ConceptDescription != null)
264 {
265 ConceptDescriptions.Add(smElement.ConceptDescription);
266 (smElement as SubmodelElement).SemanticId = new Reference(new Key(KeyElements.ConceptDescription, smElement.ConceptDescription.Identification.IdType, smElement.ConceptDescription.Identification.Id, true));
267 (smElement as SubmodelElement).ConceptDescription = null;
268 (smElement as SubmodelElement).EmbeddedDataSpecifications = null;
269 }
Constantin Ziesche635c2e32020-02-27 18:11:07 +0100270 if (smElement.ModelType == ModelType.SubmodelElementCollection)
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100271 ExtractAndClearConceptDescriptions((smElement as SubmodelElementCollection).Value);
272 }
273 }
274
275 public void SetContentRoot(string contentRoot) => ContentRoot = contentRoot;
276
277 private void ResetConstraints(IElementContainer<ISubmodelElement> submodelElements)
278 {
279 foreach (var smElement in submodelElements)
280 {
281 if(smElement.Constraints?.Count > 0)
282 (smElement as SubmodelElement).Constraints = null;
283 if(smElement is IOperation operation)
284 {
285 if(operation.InputVariables?.Count > 0)
286 ResetConstraints((smElement as IOperation).InputVariables.ToElementContainer());
287 if (operation.OutputVariables?.Count > 0)
288 ResetConstraints((smElement as IOperation).OutputVariables.ToElementContainer());
289 }
290 else if (smElement.ModelType == ModelType.SubmodelElementCollection)
291 ResetConstraints((smElement as SubmodelElementCollection).Value);
292 }
293 }
294
Constantin Ziesche635c2e32020-02-27 18:11:07 +0100295 private void DeleteEvents(IElementContainer<ISubmodelElement> submodelElements)
296 {
297 var eventsToDelete = submodelElements.Where(s => s.ModelType == ModelType.Event || s.ModelType == ModelType.BasicEvent).ToList();
298 foreach (var eventable in eventsToDelete)
299 submodelElements.Remove(eventable);
300 }
301
Constantin Ziesche857c7ab2020-02-25 11:24:51 +0100302 public void WriteEnvironment_V1_0(ExportType exportType, string filePath) => WriteEnvironment_V1_0(this, exportType, filePath);
303
304 public static void WriteEnvironment_V1_0(AssetAdministrationShellEnvironment_V1_0 environment, ExportType exportType, string filePath)
305 {
306 if (environment == null)
307 return;
308
309 switch (exportType)
310 {
311 case ExportType.Json:
312 try
313 {
314 string serialized = JsonConvert.SerializeObject(environment, JsonSettings);
315 System.IO.File.WriteAllText(filePath, serialized);
316 }
317 catch (Exception e)
318 {
319 logger.Error(e);
320 }
321 break;
322 case ExportType.Xml:
323 try
324 {
325 using (StreamWriter writer = new StreamWriter(filePath))
326 {
327 XmlSerializer serializer = new XmlSerializer(typeof(AssetAdministrationShellEnvironment_V1_0), AAS_NAMESPACE);
328 XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
329 namespaces.Add("xsi", XmlSchema.InstanceNamespace);
330 namespaces.Add("aas", AAS_NAMESPACE);
331 namespaces.Add("IEC61360", IEC61360_NAMESPACE);
332 serializer.Serialize(writer, environment, namespaces);
333 }
334
335 }
336 catch (Exception e)
337 {
338 logger.Error(e);
339 }
340 break;
341 default:
342 break;
343 }
344 }
345
346
347 public static AssetAdministrationShellEnvironment_V1_0 ReadEnvironment_V1_0(Stream stream, ExportType exportType)
348 {
349 AssetAdministrationShellEnvironment_V1_0 env = null;
350
351 try
352 {
353 switch (exportType)
354 {
355 case ExportType.Xml:
356 {
357 XmlSerializer serializer = new XmlSerializer(typeof(AssetAdministrationShellEnvironment_V1_0), AAS_NAMESPACE);
358
359 using (XmlReader reader = XmlReader.Create(stream, XmlSettings))
360 env = (AssetAdministrationShellEnvironment_V1_0)serializer.Deserialize(reader);
361 }
362 break;
363 case ExportType.Json:
364 {
365 using (StreamReader reader = new StreamReader(stream))
366 env = JsonConvert.DeserializeObject<AssetAdministrationShellEnvironment_V1_0>(reader.ReadToEnd(), JsonSettings);
367 }
368 break;
369 default:
370 throw new InvalidOperationException(exportType + " not supported");
371 }
372
373 ConvertToAssetAdministrationShell(env);
374 return env;
375 }
376 catch (Exception e)
377 {
378 logger.Error(e, "Failed to read environment - Exception: " + e.Message);
379 return null;
380 }
381 }
382
383
384 public static AssetAdministrationShellEnvironment_V1_0 ReadEnvironment_V1_0(string filePath)
385 {
386 if (string.IsNullOrEmpty(filePath))
387 throw new ArgumentNullException(filePath);
388 if (!System.IO.File.Exists(filePath))
389 throw new ArgumentException(filePath + " does not exist");
390
391 AssetAdministrationShellEnvironment_V1_0 env = null;
392
393 string fileExtension = Path.GetExtension(filePath);
394 ExportType exportType;
395 switch (fileExtension)
396 {
397 case ".xml":
398 exportType = ExportType.Xml;
399 break;
400 case ".json":
401 exportType = ExportType.Json;
402 break;
403 default:
404 throw new InvalidOperationException(fileExtension + " not supported");
405 }
406
407 using (FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.Read))
408 env = ReadEnvironment_V1_0(file, exportType);
409
410 if (env == null)
411 return null;
412
413 ConvertToAssetAdministrationShell(env);
414
415 return env;
416 }
417
418 private static void ConvertToAssetAdministrationShell(AssetAdministrationShellEnvironment_V1_0 environment)
419 {
420 foreach (var envAsset in environment.EnvironmentAssets)
421 {
422 Asset asset = new Asset
423 {
424 Administration = envAsset.Administration,
425 Category = envAsset.Category,
426 Description = envAsset.Description,
427 Identification = envAsset.Identification,
428 IdShort = envAsset.IdShort,
429 Kind = envAsset.Kind,
430 AssetIdentificationModel = envAsset.AssetIdentificationModelReference?.ToReference_V1_0<ISubmodel>()
431 };
432 environment.Assets.Add(asset);
433 }
434 foreach (var envConceptDescription in environment.EnvironmentConceptDescriptions)
435 {
436 ConceptDescription conceptDescription = new ConceptDescription()
437 {
438 Administration = envConceptDescription.Administration,
439 Category = envConceptDescription.Category,
440 Description = envConceptDescription.Description,
441 Identification = envConceptDescription.Identification,
442 IdShort = envConceptDescription.IdShort,
443 IsCaseOf = envConceptDescription.IsCaseOf?.ConvertAll(c => c.ToReference_V1_0()),
444 EmbeddedDataSpecifications = (envConceptDescription.EmbeddedDataSpecification?.DataSpecificationContent?.DataSpecificationIEC61360 != null) ? new List<DataSpecificationIEC61360>() : null
445 };
446 if(conceptDescription.EmbeddedDataSpecifications != null)
447 {
448 DataSpecificationIEC61360 dataSpecification = envConceptDescription
449 .EmbeddedDataSpecification
450 .DataSpecificationContent
451 .DataSpecificationIEC61360
452 .ToDataSpecificationIEC61360();
453
454 (conceptDescription.EmbeddedDataSpecifications as List<DataSpecificationIEC61360>).Add(dataSpecification);
455 }
456 environment.ConceptDescriptions.Add(conceptDescription);
457 }
458 foreach (var envSubmodel in environment.EnvironmentSubmodels)
459 {
460 Submodel submodel = new Submodel()
461 {
462 Administration = envSubmodel.Administration,
463 Category = envSubmodel.Category,
464 Description = envSubmodel.Description,
465 Identification = envSubmodel.Identification,
466 IdShort = envSubmodel.IdShort,
467 Kind = envSubmodel.Kind,
468 SemanticId = envSubmodel.SemanticId?.ToReference_V1_0(),
469 ConceptDescription = null,
470 };
471 submodel.SubmodelElements.AddRange(envSubmodel.SubmodelElements.ConvertAll(c => c.submodelElement?.ToSubmodelElement(environment.ConceptDescriptions)));
472
473 environment.Submodels.Add(submodel);
474 }
475 foreach (var envAssetAdministrationShell in environment.EnvironmentAssetAdministationShells)
476 {
477 AssetAdministrationShell assetAdministrationShell = new AssetAdministrationShell()
478 {
479 Administration = envAssetAdministrationShell.Administration,
480 Category = envAssetAdministrationShell.Category,
481 DerivedFrom = envAssetAdministrationShell.DerivedFrom?.ToReference_V1_0<IAssetAdministrationShell>(),
482 Description = envAssetAdministrationShell.Description,
483 Identification = envAssetAdministrationShell.Identification,
484 IdShort = envAssetAdministrationShell.IdShort
485 };
486
487 IAsset asset = environment.Assets.Find(a => a.Identification.Id == envAssetAdministrationShell.AssetReference?.Keys?.FirstOrDefault()?.Value);
488 assetAdministrationShell.Asset = asset;
489
490 foreach (var envSubmodelRef in envAssetAdministrationShell.SubmodelReferences)
491 {
492 ISubmodel submodel = environment.Submodels.Find(s => s.Identification.Id == envSubmodelRef.Keys?.FirstOrDefault()?.Value);
493 if (submodel != null)
494 assetAdministrationShell.Submodels.Add(submodel);
495 }
496
497 environment.AssetAdministrationShells.Add(assetAdministrationShell);
498 }
499 }
500
501 private static void ValidationCallBack(object sender, ValidationEventArgs args)
502 {
503 if (args.Severity == XmlSeverityType.Warning)
504 logger.Warn("Validation warning: " + args.Message);
505 else
506 logger.Error("Validation error: " + args.Message + " | LineNumber: " + args.Exception.LineNumber + " | LinePosition: " + args.Exception.LinePosition);
507
508 }
509 }
510}