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