blob: 6901bc7cb2da3a32b2d728f3723179fc1b2872c3 [file] [log] [blame]
using System.Xml;
using System.Runtime;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System;
using System.IO.Packaging;
/* Copyright (c) 2018-2019 Festo AG & Co. KG <https://www.festo.com/net/de_de/Forms/web/contact_international>, author: Michael Hoffmeister
This software is licensed under the Eclipse Public License - v 2.0 (EPL-2.0) (see https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt).
The browser functionality is under the cefSharp license (see https://raw.githubusercontent.com/cefsharp/CefSharp/master/LICENSE).
The JSON serialization is under the MIT license (see https://github.com/JamesNK/Newtonsoft.Json/blob/master/LICENSE.md). */
namespace AdminShellNS
{
#region Utils
//
// Utils
//
public class AdminShellUtil
{
public static string EvalToNonNullString(string fmt, object o, string elseString = "")
{
if (o == null)
return elseString;
return string.Format(fmt, o);
}
public static string EvalToNonEmptyString(string fmt, string o, string elseString = "")
{
if (o == "")
return elseString;
return string.Format(fmt, o);
}
public static string FilterFriendlyName(string src)
{
if (src == null)
return null;
return Regex.Replace(src, @"[^a-zA-Z0-9\-_]", "");
}
public static bool HasWhitespace(string src)
{
if (src == null)
throw new ArgumentNullException("src");
for (var i = 0; i < src.Length; i++)
if (char.IsWhiteSpace(src[i]))
return true;
return false;
}
public static string ShortLocation(Exception ex)
{
if (ex == null || ex.StackTrace == null)
return "";
string[] lines = ex.StackTrace.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
if (lines.Length < 1)
return "";
// search for " in "
var p = lines[0].IndexOf(" in ");
if (p < 0)
return "";
// search last "\" or "/", to get only filename portion and position
p = lines[0].LastIndexOfAny(new char[] { '\\', '/' });
if (p < 0)
return "";
// return this
return lines[0].Substring(p);
}
}
#endregion
/// <summary>
/// This empty class derives always from the current version of the Administration Shell class hierarchy.
/// </summary>
public class AdminShell : AdminShellV10 { }
#region AdminShell_V1_0
/// <summary>
/// Version of Details of Administration Shell Part 1 V1.0 published Nov/Dec/Jan 2018/19
/// </summary>
public class AdminShellV10
{
public class Identification
{
// members
[XmlAttribute]
public string idType = "";
[XmlText]
public string id = "";
// constructors
public Identification() { }
public Identification(Identification src)
{
this.idType = src.idType;
this.id = src.id;
}
public override string ToString()
{
return $"[{this.idType}] {this.id}";
}
}
public class Administration
{
// members
public string version = "";
public string revision = "";
// constructors
public Administration() { }
public Administration(Administration src)
{
this.version = src.version;
this.revision = src.revision;
}
public override string ToString()
{
return $"R={this.version}, V={this.revision}";
}
}
public class Key
{
[XmlAttribute]
public string type = "";
[XmlAttribute]
public bool local = false;
[XmlAttribute]
public string idType = "";
[XmlText]
public string value = "";
public Key()
{
}
public Key(Key src)
{
this.type = src.type;
this.local = src.local;
this.idType = src.idType;
this.value = src.value;
}
public Key(string type, bool local, string idType, string value)
{
this.type = type;
this.local = local;
this.idType = idType;
this.value = value;
}
public static Key CreateNew(string type, bool local, string idType, string value)
{
var k = new Key();
k.type = type;
k.local = local;
k.idType = idType;
k.value = value;
return (k);
}
public static Key GetFromRef(Reference r)
{
if (r == null || r.Count != 1)
return null;
return r[0];
}
public override string ToString()
{
var local = (this.local) ? "Local" : "not Local";
return $"[{this.type}, {local}, {this.idType}, {this.value}]";
}
public static string KeyListToString(List<Key> keys)
{
if (keys == null || keys.Count < 1)
return "";
// normally, exactly one key
if (keys.Count == 1)
return keys[0].ToString();
// multiple!
var s = "[ ";
foreach (var k in keys)
{
if (s.Length > 0)
s += ", ";
s += k.ToString();
}
return s + " ]";
}
public static string[] KeyElements = new string[] {
"GlobalReference",
"AccessPermissionRule",
"Asset",
"AssetAdministrationShell",
"ConceptDescription",
"Submodel",
"SubmodelRef", // not completely right, but used by Package Explorer
"Blob",
"ConceptDictionary",
"DataElement",
"File",
"Event",
"Operation",
"OperationVariable",
"Property",
"ReferenceElement",
"RelationshipElement",
"SubmodelElement",
"SubmodelElementCollection",
"View" };
public static string[] ReferableElements = new string[] {
"AccessPermissionRule",
"Asset",
"AssetAdministrationShell",
"ConceptDescription",
"Submodel",
"Blob",
"ConceptDictionary",
"DataElement",
"File",
"Event",
"Operation",
"OperationVariable",
"Property",
"ReferenceElement",
"RelationshipElement",
"SubmodelElement",
"SubmodelElementCollection",
"View"
};
public static string[] SubmodelElements = new string[] {
"DataElement",
"File",
"Event",
"Operation",
"Property",
"ReferenceElement",
"RelationshipElement",
"SubmodelElementCollection"};
public static string[] IdentifiableElements = new string[] {
"Asset",
"AssetAdministrationShell",
"ConceptDescription",
"Submodel" };
// use this in list to designate all of the above elements
public static string AllElements = "All";
// use this in list to designate the GlobalReference
public static string GlobalReference = "GlobalReference";
public static string ConceptDescription = "ConceptDescription";
public static string[] IdentifierTypeNames = new string[] { "idShort", "Custom", "IRDI", "URI" };
public enum IdentifierType { IdShort = 0, Custom, IRDI, URI };
public static string GetIdentifierTypeName(IdentifierType t)
{
return IdentifierTypeNames[(int)t];
}
}
// the whole class shall not be serialized by having it private
public class KeyList
{
// members
[XmlIgnore] // anyway, as it is privat
private List<Key> key = new List<Key>();
// getters / setters
[XmlIgnore]
public List<Key> Keys { get { return key; } }
[XmlIgnore]
public bool IsEmpty { get { return key == null || key.Count < 1; } }
[XmlIgnore]
public int Count { get { if (key == null) return 0; return key.Count; } }
[XmlIgnore]
public Key this[int index] { get { return key[index]; } }
// constructors / creators
public void Add(Key k)
{
key.Add(k);
}
}
[XmlType(TypeName = "reference")]
public class Reference
{
// members
[XmlIgnore] // anyway, as it is privat
private KeyList keys = new KeyList();
// getters / setters
[XmlArray("keys")]
[XmlArrayItem("key")]
public List<Key> Keys { get { if (keys == null) return null; return keys.Keys; } }
[XmlIgnore]
public bool IsEmpty { get { return keys == null || keys.Count < 1; } }
[XmlIgnore]
public int Count { get { if (keys == null) return 0; return keys.Count; } }
[XmlIgnore]
public Key this[int index] { get { return keys[index]; } }
// constructors / creators
public Reference()
{
}
public Reference(Key k)
{
keys.Keys.Add(k);
}
public Reference(Reference src)
{
foreach (var k in src.Keys)
keys.Add(new Key(k));
}
public static Reference CreateNew(Key k)
{
var r = new Reference();
r.keys.Keys.Add(k);
return r;
}
public static Reference CreateNew(List<Key> k)
{
var r = new Reference();
r.keys.Keys.AddRange(k);
return r;
}
public static Reference CreateIrdiReference(string irdi)
{
var r = new Reference();
r.keys.Keys.Add(new Key(Key.GlobalReference, false, "IRDI", irdi));
return r;
}
public bool IsExactlyOneKey(string type, bool local, string idType, string id)
{
if (keys == null || keys.Keys == null || keys.Count != 1)
return false;
var k = keys.Keys[0];
return k.type == type && k.local == local && k.idType == idType && k.value == id;
}
public override string ToString()
{
var res = "";
if (keys != null && keys.Keys != null)
foreach (var k in keys.Keys)
res += k.ToString() + ",";
return res.TrimEnd(',');
}
public virtual string GetElementName()
{
return "Reference";
}
}
[XmlType(TypeName = "derivedFrom")]
public class AssetAdministrationShellRef : Reference
{
// constructors
public AssetAdministrationShellRef() : base() { }
public AssetAdministrationShellRef(Key k) : base(k) { }
public AssetAdministrationShellRef(Reference src) : base(src) { }
// further methods
public override string GetElementName()
{
return "AssetAdministrationShellRef";
}
}
[XmlType(TypeName = "assetRef")]
public class AssetRef : Reference
{
// constructors
public AssetRef() : base() { }
public AssetRef(AssetRef src) : base(src) { }
// further methods
public override string GetElementName()
{
return "AssetRef";
}
}
[XmlType(TypeName = "submodelRef")]
public class SubmodelRef : Reference
{
// constructors
public SubmodelRef() : base() { }
public SubmodelRef(SubmodelRef src) : base(src) { }
// further methods
public override string GetElementName()
{
return "SubmodelRef";
}
}
[XmlType(TypeName = "conceptDescriptionRef")]
public class ConceptDescriptionRef : Reference
{
// constructors
public ConceptDescriptionRef() : base() { }
public ConceptDescriptionRef(ConceptDescriptionRef src) : base(src) { }
// further methods
public override string GetElementName()
{
return "ConceptDescriptionRef";
}
}
[XmlType(TypeName = "dataSpecificationRef")]
public class DataSpecificationRef : Reference
{
// constructors
public DataSpecificationRef() : base() { }
public DataSpecificationRef(DataSpecificationRef src) : base(src) { }
// further methods
public override string GetElementName()
{
return "DataSpecificationRef";
}
}
[XmlType(TypeName = "conceptDescriptions")]
public class ConceptDescriptionRefs
{
[XmlElement(ElementName = "conceptDescriptionRef")]
public List<ConceptDescriptionRef> conceptDescriptions = new List<ConceptDescriptionRef>();
}
[XmlType(TypeName = "containedElementRef")]
public class ContainedElementRef : Reference
{
public static ContainedElementRef FromReference(Reference otherref)
{
var r = ContainedElementRef.CreateNew(otherref.Keys);
return (ContainedElementRef)r;
}
// further methods
public override string GetElementName()
{
return "ContainedElementRef";
}
}
[XmlType(TypeName = "hasDataSpecification")]
public class HasDataSpecification
{
[XmlElement(ElementName = "reference")] // make "reference" go away by magic?!
public List<Reference> reference = new List<Reference>();
public HasDataSpecification() { }
public HasDataSpecification(HasDataSpecification src)
{
foreach (var r in src.reference)
reference.Add(new Reference(r));
}
}
[XmlType(TypeName = "ContainedElements")]
public class ContainedElements
{
// members
[XmlElement(ElementName = "containedElementRef")] // make "reference" go away by magic?!
public List<ContainedElementRef> reference = new List<ContainedElementRef>();
// getter / setter
public bool IsEmpty { get { return reference == null || reference.Count < 1; } }
public int Count { get { if (reference == null) return 0; return reference.Count; } }
public ContainedElementRef this[int index] { get { return reference[index]; } }
}
[XmlType(TypeName = "langString", Namespace = "http://www.admin-shell.io/1/0")]
public class LangStr
{
// members
[XmlAttribute(Namespace = "http://www.admin-shell.io/1/0")]
public string lang = "";
[XmlText]
public string str = "";
// constructors
public LangStr() { }
public LangStr(LangStr src)
{
this.lang = src.lang;
this.str = src.str;
}
public static LangStr CreateNew(string lang, string str)
{
var l = new LangStr();
l.lang = lang;
l.str = str;
return (l);
}
public static List<LangStr> CreateManyFromStringArray(string[] s)
{
var r = new List<LangStr>();
var i = 0;
while ((i + 1) < s.Length)
{
r.Add(LangStr.CreateNew(s[i], s[i + 1]));
i += 2;
}
return r;
}
}
public class Description
{
// members
[XmlElement(ElementName = "langString")]
public List<LangStr> langString = new List<LangStr>();
// constructors
public Description() { }
public Description(Description src)
{
if (src != null)
foreach (var ls in src.langString)
langString.Add(new LangStr(ls));
}
}
public class Kind
{
[XmlText]
public string kind = "Instance";
// getters / setters
public bool IsInstance { get { return kind.Trim().ToLower() == "instance"; } }
public bool IsType { get { return kind.Trim().ToLower() == "type"; } }
// constructors / creators
public Kind() { }
public Kind(Kind src)
{
kind = src.kind;
}
public static Kind CreateFrom(Kind k)
{
var res = new Kind();
res.kind = k.kind;
return res;
}
}
public class SemanticId
{
// members
[XmlIgnore] // will be ignored, anyway
private KeyList keys = new KeyList();
// getters / setters
[XmlArray("keys")]
[XmlArrayItem("key")]
public List<Key> Keys { get { return keys.Keys; } }
[XmlIgnore]
public bool IsEmpty { get { return keys == null || keys.IsEmpty; } }
[XmlIgnore]
public int Count { get { if (keys == null) return 0; return keys.Count; } }
[XmlIgnore]
public Key this[int index] { get { return keys[index]; } }
public override string ToString()
{
return Key.KeyListToString(keys.Keys);
}
// constructors / creators
public SemanticId()
{
}
public SemanticId(SemanticId src)
{
foreach (var k in src.Keys)
keys.Add(k);
}
public static SemanticId CreateFromKey(Key key)
{
if (key == null)
return null;
var res = new SemanticId();
res.Keys.Add(key);
return res;
}
public static SemanticId CreateFromKeys(List<Key> keys)
{
if (keys == null)
return null;
var res = new SemanticId();
res.Keys.AddRange(keys);
return res;
}
}
public class Referable
{
// members
public string idShort = null;
public string category = null;
[XmlElement(ElementName = "description")]
public Description description = null;
[XmlIgnore]
public Referable parent = null;
public static string[] ReferableCategoryNames = new string[] { "CONSTANT", "PARAMETER", "VARIABLE" };
// constructors
public Referable() { }
public Referable(Referable src)
{
this.idShort = src.idShort;
this.category = src.category;
if (src.description != null)
this.description = new Description(src.description);
}
public void AddDescription(string lang, string str)
{
if (description == null)
description = new Description();
description.langString.Add(LangStr.CreateNew(lang, str));
}
public virtual string GetElementName()
{
return "GlobalReference"; // not correct, but this method wasn't overridden correctly
}
public string GetFriendlyName()
{
return AdminShellUtil.FilterFriendlyName(this.idShort);
}
public void CollectReferencesByParent(List<Key> refs)
{
// check, if this is identifiable
if (this is Identifiable)
{
var idf = this as Identifiable;
var k = Key.CreateNew(idf.GetElementName(), true, idf.identification.idType, idf.identification.id);
refs.Insert(0, k);
}
else
{
var k = Key.CreateNew(this.GetElementName(), true, "idShort", this.idShort);
refs.Insert(0, k);
// recurse upwards!
if (parent != null && parent is Referable)
(this.parent).CollectReferencesByParent(refs);
}
}
}
public class Identifiable : Referable
{
// members
public Identification identification = new Identification();
public Administration administration = null;
// constructors
public Identifiable() : base() { }
public Identifiable(Identifiable src)
: base(src)
{
if (src.identification != null)
this.identification = new Identification(src.identification);
if (src.administration != null)
this.administration = new Administration(src.administration);
}
public void SetIdentification(string idType, string id, string idShort = null)
{
identification.idType = idType;
identification.id = id;
if (idShort != null)
this.idShort = idShort;
}
public void SetAdminstration(string version, string revision)
{
if (administration == null)
administration = new Administration();
administration.version = version;
administration.revision = revision;
}
public new string GetFriendlyName()
{
if (identification != null && identification.id != "")
return AdminShellUtil.FilterFriendlyName(this.identification.id);
return AdminShellUtil.FilterFriendlyName(this.idShort);
}
}
public class AdministrationShell : Identifiable
{
// from hasDataSpecification:
[XmlElement(ElementName = "hasDataSpecification")]
public HasDataSpecification hasDataSpecification = null;
// from this very class
public AssetAdministrationShellRef derivedFrom = null;
public AssetRef assetRef = new AssetRef();
public List<SubmodelRef> submodelRefs = new List<SubmodelRef>();
public Views views = null;
public List<ConceptDictionary> conceptDictionaries = null;
public static AdministrationShell CreateNew(string idType, string id, string version = null, string revision = null)
{
var s = new AdministrationShell();
s.identification.idType = idType;
s.identification.id = id;
if (version != null)
s.SetAdminstration(version, revision);
return (s);
}
public void AddView(View v)
{
if (views == null)
views = new Views();
views.views.Add(v);
}
public void AddConceptDictionary(ConceptDictionary d)
{
if (conceptDictionaries == null)
conceptDictionaries = new List<ConceptDictionary>();
conceptDictionaries.Add(d);
}
public void AddDataSpecification(Key k)
{
if (hasDataSpecification == null)
hasDataSpecification = new HasDataSpecification();
var r = new Reference();
r.Keys.Add(k);
hasDataSpecification.reference.Add(r);
}
public override string GetElementName()
{
return "AssetAdministrationShell";
}
public Tuple<string, string> ToCaptionInfo()
{
var caption = AdminShellUtil.EvalToNonNullString("\"{0}\" ", idShort, "\"AAS\"");
var info = "";
if (identification != null)
info = $"[{identification.idType}, {identification.id}]";
return Tuple.Create(caption, info);
}
public override string ToString()
{
var ci = ToCaptionInfo();
return string.Format("{0}{1}", ci.Item1, (ci.Item2 != "") ? " / " + ci.Item2 : "");
}
}
public class Asset : Identifiable
{
// from hasDataSpecification:
[XmlElement(ElementName = "hasDataSpecification")]
public HasDataSpecification hasDataSpecification = null;
// from HasKind
[XmlElement(ElementName = "kind")]
public Kind kind = new Kind();
// from this very class
[XmlElement(ElementName = "assetIdentificationModelRef")]
public SubmodelRef assetIdentificationModelRef = null;
// Getter & setters
public AssetRef GetReference()
{
var r = new AssetRef();
r.Keys.Add(Key.CreateNew(this.GetElementName(), true, this.identification.idType, this.identification.id));
return r;
}
public override string GetElementName()
{
return "Asset";
}
public Tuple<string, string> ToCaptionInfo()
{
var caption = AdminShellUtil.EvalToNonNullString("\"{0}\" ", idShort, "<no idShort!>");
var info = "";
if (identification != null)
info = $"[{identification.idType}, {identification.id}]";
return Tuple.Create(caption, info);
}
public override string ToString()
{
var ci = ToCaptionInfo();
return string.Format("{0}{1}", ci.Item1, (ci.Item2 != "") ? " / " + ci.Item2 : "");
}
}
public class View : Referable
{
// members
// from hasSemanticId:
[XmlElement(ElementName = "semanticId")]
public SemanticId semanticId = null;
// from hasDataSpecification
[XmlElement(ElementName = "hasDataSpecification")]
public HasDataSpecification hasDataSpecification = null;
// from this very class
public ContainedElements containedElements = null;
// getter / setter
public bool IsEmpty { get { return containedElements == null || containedElements.Count < 1; } }
public int Count { get { if (containedElements == null) return 0; return containedElements.Count; } }
public ContainedElementRef this[int index] { get { return containedElements[index]; } }
// constructors / creators
public static View CreateNew(string idShort)
{
var v = new View();
v.idShort = idShort;
return (v);
}
public void AddDataSpecification(Key k)
{
if (hasDataSpecification == null)
hasDataSpecification = new HasDataSpecification();
var r = new Reference();
r.Keys.Add(k);
hasDataSpecification.reference.Add(r);
}
public void AddContainedElement(Key k)
{
if (containedElements == null)
containedElements = new ContainedElements();
var r = new ContainedElementRef();
r.Keys.Add(k);
containedElements.reference.Add(r);
}
public void AddContainedElement(List<Key> keys)
{
if (containedElements == null)
containedElements = new ContainedElements();
var r = new ContainedElementRef();
foreach (var k in keys)
r.Keys.Add(k);
containedElements.reference.Add(r);
}
public void AddContainedElement(Reference r)
{
if (containedElements == null)
containedElements = new ContainedElements();
containedElements.reference.Add(ContainedElementRef.FromReference(r));
}
public void AddContainedElement(List<Reference> rlist)
{
if (containedElements == null)
containedElements = new ContainedElements();
foreach (var r in rlist)
containedElements.reference.Add(ContainedElementRef.FromReference(r));
}
public override string GetElementName()
{
return "View";
}
public Tuple<string, string> ToCaptionInfo()
{
var caption = AdminShellUtil.EvalToNonNullString("\"{0}\" ", idShort, "<no idShort!>");
var info = "";
if (this.semanticId != null)
info = Key.KeyListToString(this.semanticId.Keys);
if (this.containedElements != null && this.containedElements.reference != null)
info = (info + " ").Trim() + String.Format("({0} elements)", this.containedElements.reference.Count);
return Tuple.Create(caption, info);
}
public override string ToString()
{
var ci = ToCaptionInfo();
return string.Format("{0}{1}", ci.Item1, (ci.Item2 != "") ? " / " + ci.Item2 : "");
}
}
public class Views
{
[XmlElement(ElementName = "view")]
public List<View> views = new List<View>();
}
public class LangStringIEC61360
{
// members
[XmlElement(ElementName = "langString", Namespace = "http://www.admin-shell.io/aas/1/0")]
public List<LangStr> langString = new List<LangStr>();
// getters / setters
public bool IsEmpty { get { return langString == null || langString.Count < 1; } }
public int Count { get { if (langString == null) return 0; return langString.Count; } }
public LangStr this[int index] { get { return langString[index]; } }
// constructors
public LangStringIEC61360() { }
public LangStringIEC61360(LangStringIEC61360 src)
{
if (src.langString != null)
foreach (var ls in src.langString)
this.langString.Add(new LangStr(ls));
}
}
public class UnitId
{
// members
[XmlElement(ElementName = "keys")]
public KeyList keys = new KeyList();
// getter / setters
public List<Key> Keys { get { if (keys == null) return null; return keys.Keys; } }
public bool IsEmpty { get { return keys == null || keys.IsEmpty; } }
public int Count { get { if (keys == null) return 0; return keys.Count; } }
public Key this[int index] { get { return keys.Keys[index]; } }
// constructors / creators
public UnitId() { }
public UnitId(UnitId src)
{
if (src.keys != null)
foreach (var k in src.Keys)
this.keys.Add(new Key(k));
}
public static UnitId CreateNew(string type, bool local, string idType, string value)
{
var u = new UnitId();
u.keys.Keys.Add(Key.CreateNew(type, local, idType, value));
return u;
}
}
[XmlRoot(Namespace = "http://www.admin-shell.io/IEC61360/1/0")]
public class DataSpecificationIEC61360
{
// members
// public List<LangStr> preferredName = new List<LangStr>();
public LangStringIEC61360 preferredName = new LangStringIEC61360();
public string shortName = "";
public string unit = "";
public UnitId unitId = null;
public string valueFormat = null;
public List<LangStr> sourceOfDefinition = new List<LangStr>();
public string symbol = null;
public string dataType = "";
// public List<LangStr> definition = new List<LangStr>();
public LangStringIEC61360 definition = new LangStringIEC61360();
// getter / setters
// constructors
public DataSpecificationIEC61360() { }
public DataSpecificationIEC61360(DataSpecificationIEC61360 src)
{
if (src.preferredName != null)
this.preferredName = new LangStringIEC61360(src.preferredName);
this.shortName = src.shortName;
this.unit = src.unit;
if (src.unitId != null)
this.unitId = new UnitId(src.unitId);
this.valueFormat = src.valueFormat;
if (src.sourceOfDefinition != null)
foreach (var sod in src.sourceOfDefinition)
this.sourceOfDefinition.Add(sod);
this.symbol = src.symbol;
this.dataType = src.dataType;
if (src.definition != null)
this.definition = new LangStringIEC61360(src.definition);
}
public static DataSpecificationIEC61360 CreateNew(
string[] preferredName = null,
string shortName = "",
string unit = "",
UnitId unitId = null,
string valueFormat = null,
string[] sourceOfDefinition = null,
string symbol = null,
string dataType = "",
string[] definition = null
)
{
var d = new DataSpecificationIEC61360();
if (preferredName != null)
d.preferredName.langString = LangStr.CreateManyFromStringArray(preferredName);
d.shortName = shortName;
d.unit = unit;
d.unitId = unitId;
d.valueFormat = valueFormat;
if (sourceOfDefinition != null)
d.sourceOfDefinition = LangStr.CreateManyFromStringArray(sourceOfDefinition);
d.symbol = symbol;
d.dataType = dataType;
if (definition != null)
d.definition.langString = LangStr.CreateManyFromStringArray(definition);
return (d);
}
}
public class DataSpecificationISO99999
{
}
public class DataSpecificationContent
{
// members
public DataSpecificationIEC61360 dataSpecificationIEC61360 = new DataSpecificationIEC61360();
public DataSpecificationISO99999 dataSpecificationISO99999 = null;
// constructors
public DataSpecificationContent() { }
public DataSpecificationContent(DataSpecificationContent src)
{
if (src.dataSpecificationIEC61360 != null)
this.dataSpecificationIEC61360 = new DataSpecificationIEC61360(src.dataSpecificationIEC61360);
}
}
public class EmbeddedDataSpecification
{
// members
public DataSpecificationRef hasDataSpecification = new DataSpecificationRef();
public DataSpecificationContent dataSpecificationContent = new DataSpecificationContent();
// constructors
public EmbeddedDataSpecification() { }
public EmbeddedDataSpecification(EmbeddedDataSpecification src)
{
if (src.hasDataSpecification != null)
this.hasDataSpecification = new DataSpecificationRef(src.hasDataSpecification);
if (src.dataSpecificationContent != null)
this.dataSpecificationContent = new DataSpecificationContent(src.dataSpecificationContent);
}
}
public class ConceptDescription : Identifiable, System.IDisposable
{
// members
// do this in order to be IDisposable, that is: suitable for (using)
void System.IDisposable.Dispose() { }
public void GetData() { }
// from HasDataSpecification
[XmlElement(ElementName = "embeddedDataSpecification")]
public EmbeddedDataSpecification embeddedDataSpecification = new EmbeddedDataSpecification();
// old
// [XmlElement(ElementName="conceptDefinitionRef")]
// public Reference conceptDefinitionRef = null ;
// this class
[XmlIgnore]
private List<Reference> isCaseOf = null;
// getter / setter
[XmlElement(ElementName = "isCaseOf")]
public List<Reference> IsCaseOf
{
get { return isCaseOf; }
set { isCaseOf = value; }
}
// constructors / creators
public ConceptDescription() : base() { }
public ConceptDescription(ConceptDescription src)
: base(src)
{
if (src.embeddedDataSpecification != null)
this.embeddedDataSpecification = new EmbeddedDataSpecification(src.embeddedDataSpecification);
if (src.isCaseOf != null)
foreach (var ico in src.isCaseOf)
{
if (this.isCaseOf == null)
this.isCaseOf = new List<Reference>();
this.isCaseOf.Add(new Reference(ico));
}
}
public static ConceptDescription CreateNew(string idType, string id, string version = null, string revision = null)
{
var cd = new ConceptDescription();
cd.identification.idType = idType;
cd.identification.id = id;
if (version != null)
{
if (cd.administration == null)
cd.administration = new Administration();
cd.administration.version = version;
cd.administration.revision = revision;
}
return (cd);
}
public ConceptDescriptionRef GetReference()
{
var r = new ConceptDescriptionRef();
r.Keys.Add(Key.CreateNew(this.GetElementName(), true, this.identification.idType, this.identification.id));
return r;
}
public Key GetGlobalDataSpecRef()
{
if (embeddedDataSpecification.hasDataSpecification.Count != 1)
return null;
return (embeddedDataSpecification.hasDataSpecification[0]);
}
public void SetIEC61360Spec(
string[] preferredNames = null,
string shortName = "",
string unit = "",
UnitId unitId = null,
string valueFormat = null,
string[] sourceOfDefinition = null,
string symbol = null,
string dataType = "",
string[] definition = null
)
{
this.embeddedDataSpecification = new EmbeddedDataSpecification();
this.embeddedDataSpecification.hasDataSpecification.Keys.Add(Key.CreateNew("GlobalReference", false, "URI", "www.admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360"));
this.embeddedDataSpecification.dataSpecificationContent.dataSpecificationIEC61360 = AdminShell.DataSpecificationIEC61360.CreateNew(
preferredNames, shortName, unit, unitId, valueFormat, sourceOfDefinition, symbol, dataType, definition
);
this.AddIsCaseOf(Reference.CreateNew(new Key("ConceptDescription", false, this.identification.idType, this.identification.id)));
}
public string GetShortName()
{
if (embeddedDataSpecification != null && embeddedDataSpecification.dataSpecificationContent != null && embeddedDataSpecification.dataSpecificationContent.dataSpecificationIEC61360 != null)
return embeddedDataSpecification.dataSpecificationContent.dataSpecificationIEC61360.shortName;
return "";
}
public override string GetElementName()
{
return "ConceptDescription";
}
public Tuple<string, string> ToCaptionInfo()
{
var caption = "";
if (this.idShort != null && this.idShort.Trim() != "")
caption = $"\"{this.idShort.Trim()}\"";
if (this.identification != null)
caption = (caption + " " + this.identification).Trim();
var info = "";
if (embeddedDataSpecification != null && embeddedDataSpecification.dataSpecificationContent != null && embeddedDataSpecification.dataSpecificationContent.dataSpecificationIEC61360 != null)
info += embeddedDataSpecification.dataSpecificationContent.dataSpecificationIEC61360.shortName;
return Tuple.Create(caption, info);
}
public override string ToString()
{
var ci = ToCaptionInfo();
return string.Format("{0}{1}", ci.Item1, (ci.Item2 != "") ? " / " + ci.Item2 : "");
}
public void AddIsCaseOf(Reference ico)
{
if (isCaseOf == null)
isCaseOf = new List<Reference>();
isCaseOf.Add(ico);
}
}
public class ConceptDictionary : Referable
{
[XmlElement(ElementName = "conceptDescriptions")]
public ConceptDescriptionRefs conceptDescriptionsRefs = null;
public static ConceptDictionary CreateNew(string idShort = null)
{
var d = new ConceptDictionary();
if (idShort != null)
d.idShort = idShort;
return (d);
}
public void AddReference(Reference r)
{
var cdr = (ConceptDescriptionRef)(ConceptDescriptionRef.CreateNew(r.Keys));
if (conceptDescriptionsRefs == null)
conceptDescriptionsRefs = new ConceptDescriptionRefs();
conceptDescriptionsRefs.conceptDescriptions.Add(cdr);
}
public override string GetElementName()
{
return "ConceptDictionary";
}
}
[XmlRoot(ElementName = "aasenv", Namespace = "http://www.admin-shell.io/aas/1/0")]
public class AdministrationShellEnv
{
// [XmlElement(ElementName="assetAdministrationShells")]
[XmlIgnore] // will be ignored, anyway
private List<AdministrationShell> administrationShells = new List<AdministrationShell>();
[XmlIgnore] // will be ignored, anyway
private List<Asset> assets = new List<Asset>();
[XmlIgnore] // will be ignored, anyway
private List<Submodel> submodels = new List<Submodel>();
[XmlIgnore] // will be ignored, anyway
private List<ConceptDescription> conceptDescriptions = new List<ConceptDescription>();
// getter / setters
[XmlArray("assetAdministrationShells")]
[XmlArrayItem("assetAdministrationShell")]
public List<AdministrationShell> AdministrationShells
{
get { return administrationShells; }
set { administrationShells = value; }
}
[XmlArray("assets")]
[XmlArrayItem("asset")]
public List<Asset> Assets
{
get { return assets; }
set { assets = value; }
}
[XmlArray("submodels")]
[XmlArrayItem("submodel")]
public List<Submodel> Submodels
{
get { return submodels; }
set { submodels = value; }
}
[XmlArray("conceptDescriptions")]
[XmlArrayItem("conceptDescription")]
public List<ConceptDescription> ConceptDescriptions
{
get { return conceptDescriptions; }
set { conceptDescriptions = value; }
}
// constructor / creators
public Asset FindAsset(AssetRef aref)
{
// trivial
if (aref == null)
return null;
// can only refs with 1 key
if (aref.Count != 1)
return null;
// and we're picky
var key = aref[0];
if (!key.local || key.type.ToLower().Trim() != "asset")
return null;
// brute force
foreach (var a in assets)
if (a.identification.idType.ToLower().Trim() == key.idType.ToLower().Trim()
&& a.identification.id.ToLower().Trim() == key.value.ToLower().Trim())
return a;
// uups
return null;
}
public Submodel FindSubmodel(SubmodelRef smref)
{
// trivial
if (smref == null)
return null;
// can only refs with 1 key
if (smref.Count != 1)
return null;
// and we're picky
var key = smref.Keys[0];
if (!key.local || key.type.ToLower().Trim() != "submodel")
return null;
// brute force
foreach (var sm in submodels)
if (sm.identification.idType.ToLower().Trim() == key.idType.ToLower().Trim()
&& sm.identification.id.ToLower().Trim() == key.value.ToLower().Trim())
return sm;
// uups
return null;
}
public ConceptDescription FindConceptDescription(List<Key> keys)
{
// trivial
if (keys == null)
return null;
// can only refs with 1 key
if (keys.Count != 1)
return null;
// and we're picky
var key = keys[0];
if (!key.local || key.type.ToLower().Trim() != "conceptdescription")
return null;
// brute force
foreach (var cd in conceptDescriptions)
if (cd.identification.idType.ToLower().Trim() == key.idType.ToLower().Trim()
&& cd.identification.id.ToLower().Trim() == key.value.ToLower().Trim())
return cd;
// uups
return null;
}
public ConceptDescription FindConceptDescription(Key key)
{
if (key == null)
return null;
var l = new List<Key>();
l.Add(key);
return (FindConceptDescription(l));
}
private void CopyConceptDescriptionsFrom(AdministrationShellEnv srcEnv, SubmodelElement src, bool shallowCopy = false)
{
// access
if (srcEnv == null || src == null || src.semanticId == null)
return;
// check for this SubmodelElement in Surce
var cdSrc = srcEnv.FindConceptDescription(src.semanticId.Keys);
if (cdSrc == null)
return;
// check for this SubmodelElement in Destnation (this!)
var cdDest = this.FindConceptDescription(src.semanticId.Keys);
if (cdDest != null)
return;
// copy new
this.ConceptDescriptions.Add(new ConceptDescription(cdSrc));
// recurse?
if (!shallowCopy && src is SubmodelElementCollection)
foreach (var m in (src as SubmodelElementCollection).value)
CopyConceptDescriptionsFrom(srcEnv, m.submodelElement, shallowCopy);
}
public SubmodelElementWrapper CopySubmodelElementAndCD(AdministrationShellEnv srcEnv, SubmodelElement srcElem, bool copyCD = false, bool shallowCopy = false)
{
// access
if (srcEnv == null || srcElem == null)
return null;
// 1st result pretty easy (calling function will add this to the appropriate Submodel)
var res = new SubmodelElementWrapper(srcElem);
// copy the CDs..
if (copyCD)
CopyConceptDescriptionsFrom(srcEnv, srcElem, shallowCopy);
// give back
return res;
}
public SubmodelRef CopySubmodelRefAndCD(AdministrationShellEnv srcEnv, SubmodelRef srcSubRef, bool copySubmodel = false, bool copyCD = false, bool shallowCopy = false)
{
// access
if (srcEnv == null || srcSubRef == null)
return null;
// need to have the source Submodel
var srcSub = srcEnv.FindSubmodel(srcSubRef);
if (srcSub == null)
return null;
// 1st result pretty easy (calling function will add this to the appropriate AAS)
var dstSubRef = new SubmodelRef(srcSubRef);
// maybe we need the Submodel in our environment, as well
var dstSub = this.FindSubmodel(dstSubRef);
if (dstSub == null && copySubmodel)
{
dstSub = new Submodel(srcSub, shallowCopy);
this.Submodels.Add(dstSub);
}
else
{
// there is already an submodel, just add members
if (!shallowCopy && srcSub.submodelElements != null)
{
if (dstSub.submodelElements == null)
dstSub.submodelElements = new List<SubmodelElementWrapper>();
foreach (var smw in srcSub.submodelElements)
dstSub.submodelElements.Add(new SubmodelElementWrapper(smw.submodelElement, shallowCopy));
}
}
// copy the CDs..
if (copyCD && srcSub.submodelElements != null)
foreach (var smw in srcSub.submodelElements)
CopyConceptDescriptionsFrom(srcEnv, smw.submodelElement, shallowCopy);
// give back
return dstSubRef;
}
}
//
// Submodel + Submodel elements
//
public interface IGetReference
{
Reference GetReference();
}
public class Qualifier
{
// member
// from hasSemantics:
[XmlElement(ElementName = "semanticId")]
public SemanticId semanticId = null;
// this class
public string qualifierType = null;
public string qualifierValue = null;
// constructors
public Qualifier() { }
public Qualifier(Qualifier src)
{
if (src.semanticId != null)
this.semanticId = new SemanticId(src.semanticId);
this.qualifierType = src.qualifierType;
this.qualifierValue = src.qualifierValue;
}
}
public class SubmodelElement : Referable, System.IDisposable, IGetReference
{
// members
// do this in order to be IDisposable, that is: suitable for (using)
void System.IDisposable.Dispose() { }
public void GetData() { }
// from hasDataSpecification:
[XmlElement(ElementName = "hasDataSpecification")]
public HasDataSpecification hasDataSpecification = null;
// from hasSemantics:
[XmlElement(ElementName = "semanticId")]
public SemanticId semanticId = null;
// from hasKind:
[XmlElement(ElementName = "kind")]
public Kind kind = null;
// from Qualifiable:
public List<Qualifier> qualifier = null;
// getter / setter
// constructors / creators
public SubmodelElement()
: base() { }
public SubmodelElement(SubmodelElement src)
: base(src)
{
if (src.hasDataSpecification != null)
hasDataSpecification = new HasDataSpecification(src.hasDataSpecification);
if (src.semanticId != null)
semanticId = new SemanticId(src.semanticId);
if (src.kind != null)
kind = new Kind(src.kind);
if (src.qualifier != null)
foreach (var q in src.qualifier)
qualifier.Add(new Qualifier(q));
}
public void CreateNewLogic(string idShort = null, string category = null, Key semanticIdKey = null)
{
if (idShort != null)
this.idShort = idShort;
if (category != null)
this.category = category;
if (semanticIdKey != null)
{
if (this.semanticId == null)
this.semanticId = new SemanticId();
this.semanticId.Keys.Add(semanticIdKey);
}
}
public void AddQualifier(string qualifierType = null, string qualifierValue = null, KeyList semanticKeys = null)
{
if (this.qualifier == null)
this.qualifier = new List<Qualifier>();
var q = new Qualifier();
q.qualifierType = qualifierType;
q.qualifierValue = qualifierValue;
q.semanticId = SemanticId.CreateFromKeys(semanticKeys.Keys);
this.qualifier.Add(q);
}
public override string GetElementName()
{
return "SubmodelElement";
}
public Reference GetReference()
{
Reference r = new Reference();
// this is the tail of our referencing chain ..
r.Keys.Add(Key.CreateNew(GetElementName(), true, "idShort", this.idShort));
// try to climb up ..
var current = this.parent;
while (current != null)
{
if (current is Identifiable)
{
// add big information set
r.Keys.Insert(0, Key.CreateNew(
current.GetElementName(),
true,
(current as Identifiable).identification.idType,
(current as Identifiable).identification.id));
}
else
{
// reference via idShort
r.Keys.Insert(0, Key.CreateNew(
current.GetElementName(),
true,
"idShort", this.idShort));
}
current = current.parent;
}
return r;
}
public Tuple<string, string> ToCaptionInfo()
{
var caption = AdminShellUtil.EvalToNonNullString("\"{0}\" ", idShort, "<no idShort!>");
var info = "";
if (semanticId != null)
AdminShellUtil.EvalToNonEmptyString("\u21e8 {0}", semanticId.ToString(), "");
return Tuple.Create(caption, info);
}
public override string ToString()
{
var ci = ToCaptionInfo();
return string.Format("{0}{1}", ci.Item1, (ci.Item2 != "") ? " / " + ci.Item2 : "");
}
}
[XmlType(TypeName = "submodelElement")]
public class SubmodelElementWrapper
{
// members
[XmlElement(ElementName = "property", Type = typeof(Property))]
[XmlElement(ElementName = "file", Type = typeof(File))]
[XmlElement(ElementName = "blob", Type = typeof(Blob))]
[XmlElement(ElementName = "referenceElement", Type = typeof(ReferenceElement))]
[XmlElement(ElementName = "relationshipElement", Type = typeof(RelationshipElement))]
[XmlElement(ElementName = "submodelElementCollection", Type = typeof(SubmodelElementCollection))]
public SubmodelElement submodelElement;
// constructors
public SubmodelElementWrapper() { }
/*
public SubmodelElementWrapper(SubmodelElementWrapper src, bool shallowCopy = false)
{
if (src.submodelElement is Property)
this.submodelElement = new Property(src.submodelElement as Property);
if (src.submodelElement is File)
this.submodelElement = new File(src.submodelElement as File);
if (src.submodelElement is Blob)
this.submodelElement = new Blob(src.submodelElement as Blob);
if (src.submodelElement is ReferenceElement)
this.submodelElement = new ReferenceElement(src.submodelElement as ReferenceElement);
if (src.submodelElement is RelationshipElement)
this.submodelElement = new RelationshipElement(src.submodelElement as RelationshipElement);
if (src.submodelElement is SubmodelElementCollection)
this.submodelElement = new SubmodelElementCollection(src.submodelElement as SubmodelElementCollection, shallowCopy: shallowCopy);
}
*/
public SubmodelElementWrapper(SubmodelElement src, bool shallowCopy = false)
{
if (src is Property)
this.submodelElement = new Property(src as Property);
if (src is File)
this.submodelElement = new File(src as File);
if (src is Blob)
this.submodelElement = new Blob(src as Blob);
if (src is ReferenceElement)
this.submodelElement = new ReferenceElement(src as ReferenceElement);
if (src is RelationshipElement)
this.submodelElement = new RelationshipElement(src as RelationshipElement);
if (src is SubmodelElementCollection)
this.submodelElement = new SubmodelElementCollection(src as SubmodelElementCollection, shallowCopy: shallowCopy);
}
}
public class Submodel : Identifiable, System.IDisposable, IGetReference
{
// members
// do this in order to be IDisposable, that is: suitable for (using)
void System.IDisposable.Dispose() { }
public void GetData() { }
// from hasDataSpecification:
[XmlElement(ElementName = "hasDataSpecification")]
public HasDataSpecification hasDataSpecification = null;
// from hasSemanticId:
[XmlElement(ElementName = "semanticId")]
public SemanticId semanticId = new SemanticId();
[XmlElement(ElementName = "kind")]
public Kind kind = new Kind();
// from this very class
public List<SubmodelElementWrapper> submodelElements = null;
// getter / setter
// constructors / creators
public Submodel() : base() { }
public Submodel(Submodel src, bool shallowCopy = false)
: base(src)
{
if (src.hasDataSpecification != null)
this.hasDataSpecification = new HasDataSpecification(src.hasDataSpecification);
if (src.semanticId != null)
this.semanticId = new SemanticId(src.semanticId);
if (src.kind != null)
this.kind = new Kind(src.kind);
if (!shallowCopy && src.submodelElements != null)
{
if (this.submodelElements == null)
this.submodelElements = new List<SubmodelElementWrapper>();
foreach (var smw in src.submodelElements)
this.submodelElements.Add(new SubmodelElementWrapper(smw.submodelElement, shallowCopy));
}
}
public static Submodel CreateNew(string idType, string id, string version = null, string revision = null)
{
var s = new Submodel();
s.identification.idType = idType;
s.identification.id = id;
if (version != null)
{
if (s.administration == null)
s.administration = new Administration();
s.administration.version = version;
s.administration.revision = revision;
}
return (s);
}
public override string GetElementName()
{
return "Submodel";
}
public Reference GetReference()
{
SubmodelRef l = new SubmodelRef();
l.Keys.Add(Key.CreateNew(this.GetElementName(), true, this.identification.idType, this.identification.id));
return l;
}
public void Add(SubmodelElement se)
{
if (submodelElements == null)
submodelElements = new List<SubmodelElementWrapper>();
var sew = new SubmodelElementWrapper();
se.parent = this; // track parent here!
sew.submodelElement = se;
submodelElements.Add(sew);
}
public void AddDataSpecification(Key k)
{
if (hasDataSpecification == null)
hasDataSpecification = new HasDataSpecification();
var r = new Reference();
r.Keys.Add(k);
hasDataSpecification.reference.Add(r);
}
public Tuple<string, string> ToCaptionInfo()
{
var caption = AdminShellUtil.EvalToNonNullString("\"{0}\" ", idShort, "<no idShort!>");
var info = "";
if (identification != null)
info = $"[{identification.idType}, {identification.id}]";
return Tuple.Create(caption, info);
}
public override string ToString()
{
var ci = ToCaptionInfo();
return string.Format("{0}{1}", ci.Item1, (ci.Item2 != "") ? " / " + ci.Item2 : "");
}
private static void SetParentsForSME(Referable parent, SubmodelElement se)
{
se.parent = parent;
var smc = se as SubmodelElementCollection;
if (smc != null)
foreach (var sme in smc.value)
SetParentsForSME(se, sme.submodelElement);
}
public void SetAllParents()
{
foreach (var sme in this.submodelElements)
SetParentsForSME(this, sme.submodelElement);
}
}
//
// Derived from SubmodelElements
//
public class DataElement : SubmodelElement
{
public DataElement() { }
public DataElement(DataElement src)
: base(src)
{ }
public override string GetElementName()
{
return "DataElement";
}
}
public class Property : DataElement
{
// members
public string valueType = "";
public string value = "";
public Reference valueId = null;
// constructors
public Property() { }
public Property(Property src)
: base(src)
{
this.valueType = src.value;
this.value = src.value;
if (src.valueId != null)
src.valueId = new Reference(src.valueId);
}
public static Property CreateNew(string idShort = null, string category = null, Key semanticIdKey = null)
{
var x = new Property();
x.CreateNewLogic(idShort, category, semanticIdKey);
return (x);
}
public void Set(string valueType = "", string value = "")
{
this.valueType = valueType;
this.value = value;
}
public void Set(string type, bool local, string idType, string value)
{
this.valueId = Reference.CreateNew(Key.CreateNew(type, local, idType, value));
}
public override string GetElementName()
{
return "Property";
}
}
public class Blob : DataElement
{
// members
public string mimeType = "";
public string value = "";
// constructors
public Blob() { }
public Blob(Blob src)
: base(src)
{
this.mimeType = src.mimeType;
this.value = src.value;
}
public static Blob CreateNew(string idShort = null, string category = null, Key semanticIdKey = null)
{
var x = new Blob();
x.CreateNewLogic(idShort, category, semanticIdKey);
return (x);
}
public void Set(string mimeType = "", string value = "")
{
this.mimeType = mimeType;
this.value = value;
}
public override string GetElementName()
{
return "Blob";
}
}
public class File : DataElement
{
// members
public string mimeType = "";
public string value = "";
// constructors
public File() { }
public File(File src)
: base(src)
{
this.mimeType = src.mimeType;
this.value = src.value;
}
public static File CreateNew(string idShort = null, string category = null, Key semanticIdKey = null)
{
var x = new File();
x.CreateNewLogic(idShort, category, semanticIdKey);
return (x);
}
public void Set(string mimeType = "", string value = "")
{
this.mimeType = mimeType;
this.value = value;
}
public override string GetElementName()
{
return "File";
}
public static string[] GetPopularMimeTypes()
{
return
new string[] {
System.Net.Mime.MediaTypeNames.Text.Plain,
System.Net.Mime.MediaTypeNames.Text.Xml,
"application/json",
"application/rdf+xml",
System.Net.Mime.MediaTypeNames.Application.Pdf,
System.Net.Mime.MediaTypeNames.Image.Jpeg,
"image/png",
System.Net.Mime.MediaTypeNames.Image.Gif,
"application/iges",
"application/step"
};
}
}
public class ReferenceElement : DataElement
{
// members
public Reference value = new Reference();
// constructors
public ReferenceElement() { }
public ReferenceElement(ReferenceElement src)
: base(src)
{
if (src.value != null)
this.value = new Reference(src.value);
}
public static ReferenceElement CreateNew(string idShort = null, string category = null, Key semanticIdKey = null)
{
var x = new ReferenceElement();
x.CreateNewLogic(idShort, category, semanticIdKey);
return (x);
}
public void Set(Reference value = null)
{
this.value = value;
}
public override string GetElementName()
{
return "ReferenceElement";
}
}
public class RelationshipElement : DataElement
{
// members
public Reference first = new Reference();
public Reference second = new Reference();
// constructors
public RelationshipElement() { }
public RelationshipElement(RelationshipElement src)
: base(src)
{
if (src.first != null)
this.first = new Reference(src.first);
if (src.second != null)
this.second = new Reference(src.second);
}
public static RelationshipElement CreateNew(string idShort = null, string category = null, Key semanticIdKey = null)
{
var x = new RelationshipElement();
x.CreateNewLogic(idShort, category, semanticIdKey);
return (x);
}
public void Set(Reference first = null, Reference second = null)
{
this.first = first;
this.second = second;
}
public override string GetElementName()
{
return "RelationshipElement";
}
}
public class SubmodelElementCollection : SubmodelElement
{
// members
public List<SubmodelElementWrapper> value = new List<SubmodelElementWrapper>();
public bool ordered = false;
public bool allowDuplicates = false;
// constructors
public SubmodelElementCollection() { }
public SubmodelElementCollection(SubmodelElementCollection src, bool shallowCopy = false)
: base(src)
{
this.ordered = src.ordered;
this.allowDuplicates = src.allowDuplicates;
if (!shallowCopy)
foreach (var smw in src.value)
value.Add(new SubmodelElementWrapper(smw.submodelElement));
}
public static SubmodelElementCollection CreateNew(string idShort = null, string category = null, Key semanticIdKey = null)
{
var x = new SubmodelElementCollection();
x.CreateNewLogic(idShort, category, semanticIdKey);
return (x);
}
public void Set(bool allowDuplicates = false, bool ordered = false)
{
this.allowDuplicates = allowDuplicates;
this.ordered = ordered;
}
public void Add(SubmodelElement se)
{
if (value == null)
value = new List<SubmodelElementWrapper>();
var sew = new SubmodelElementWrapper();
se.parent = this; // track parent here!
sew.submodelElement = se;
value.Add(sew);
}
public override string GetElementName()
{
return "SubmodelElementCollection";
}
}
//
// Handling of packages
//
public class PackageSupplementaryFile : Referable
{
public enum LocationType { InPackage, AddPending, DeletePending }
public enum SpecialHandlingType { None, EmbedAsThumbnail }
public Uri uri = null;
public string sourcePath = null;
public LocationType location = LocationType.InPackage;
public SpecialHandlingType specialHandling = SpecialHandlingType.None;
public PackageSupplementaryFile(Uri uri, string sourcePath = null, LocationType location = LocationType.InPackage, SpecialHandlingType specialHandling = SpecialHandlingType.None)
{
this.uri = uri;
this.sourcePath = sourcePath;
this.location = location;
this.specialHandling = specialHandling;
}
// class derives from Referable in order to provide GetElementName
public override string GetElementName()
{
return "File";
}
}
public class PackageEnv
{
private string fn = "New Package";
private AdministrationShellEnv aasenv = new AdministrationShellEnv();
private Package openPackage = null;
private List<PackageSupplementaryFile> pendingFilesToAdd = new List<PackageSupplementaryFile>();
private List<PackageSupplementaryFile> pendingFilesToDelete = new List<PackageSupplementaryFile>();
public PackageEnv()
{
}
public PackageEnv(AdministrationShellEnv env)
{
if (env != null)
this.aasenv = env;
}
public PackageEnv(string fn)
{
Load(fn);
}
public bool IsOpen
{
get
{
return openPackage != null;
}
}
public string Filename
{
get
{
return fn;
}
}
public AdminShell.AdministrationShellEnv AasEnv
{
get
{
return aasenv;
}
}
public bool Load(string fn)
{
this.fn = fn;
if (this.openPackage != null)
this.openPackage.Close();
this.openPackage = null;
if (fn.ToLower().EndsWith(".xml"))
{
// load only XML
try
{
XmlSerializer serializer = new XmlSerializer(typeof(AdminShell.AdministrationShellEnv), "http://www.admin-shell.io/aas/1/0");
TextReader reader = new StreamReader(fn);
this.aasenv = serializer.Deserialize(reader) as AdminShell.AdministrationShellEnv;
if (this.aasenv == null)
throw (new Exception("Type error for XML file!"));
reader.Close();
}
catch (Exception ex)
{
throw (new Exception(string.Format("While reading AAS {0} at {1} gave: {2}", fn, AdminShellUtil.ShortLocation(ex), ex.Message)));
}
return true;
}
if (fn.ToLower().EndsWith(".aasx"))
{
// load package AASX
try
{
var package = Package.Open(fn, FileMode.Open);
// get the origin from the package
PackagePart originPart = null;
var xs = package.GetRelationshipsByType("http://www.admin-shell.io/aasx/relationships/aasx-origin");
foreach (var x in xs)
if (x.SourceUri.ToString() == "/")
{
originPart = package.GetPart(x.TargetUri);
break;
}
if (originPart == null)
throw (new Exception(string.Format("Unable to find AASX origin. Aborting!")));
// get the specs from the package
PackagePart specPart = null;
xs = originPart.GetRelationshipsByType("http://www.admin-shell.io/aasx/relationships/aas-spec");
foreach (var x in xs)
{
specPart = package.GetPart(x.TargetUri);
break;
}
if (specPart == null)
throw (new Exception(string.Format("Unable to find AASX spec(s). Aborting!")));
// open spec part to read
using (var s = specPart.GetStream(FileMode.Open))
{
// own catch loop to be more specific
try
{
XmlSerializer serializer = new XmlSerializer(typeof(AdminShell.AdministrationShellEnv), "http://www.admin-shell.io/aas/1/0");
this.aasenv = serializer.Deserialize(s) as AdminShell.AdministrationShellEnv;
this.openPackage = package;
if (this.aasenv == null)
throw (new Exception("Type error for XML file!"));
s.Close();
}
catch (Exception ex)
{
throw (new Exception(string.Format("While reading AAS {0} spec at {1} gave: {2}", fn, AdminShellUtil.ShortLocation(ex), ex.Message)));
}
}
}
catch (Exception ex)
{
throw (new Exception(string.Format("While reading AASX {0} at {1} gave: {2}", fn, AdminShellUtil.ShortLocation(ex), ex.Message)));
}
return true;
}
// Don't know to handle
throw (new Exception(string.Format($"Not able to handle {fn}.")));
}
public bool SaveAs(string fn, bool writeFreshly = false)
{
if (this.fn.ToLower().EndsWith(".xml"))
{
// save only XML
this.fn = fn;
try
{
using (var s = new StreamWriter(this.fn))
{
var serializer = new XmlSerializer(typeof(AdminShell.AdministrationShellEnv));
var nss = new XmlSerializerNamespaces();
nss.Add("aas", "http://www.admin-shell.io/aas/1/0");
nss.Add("IEC61360", "http://www.admin-shell.io/IEC61360/1/0");
serializer.Serialize(s, this.aasenv, nss);
}
}
catch (Exception ex)
{
throw (new Exception(string.Format("While writing AAS {0} at {1} gave: {2}", fn, AdminShellUtil.ShortLocation(ex), ex.Message)));
}
return true;
}
if (fn.ToLower().EndsWith(".aasx"))
{
// save package AASX
try
{
// we want existing contents to be preserved, but no possiblity to change file name
// therefore: copy file to new name, re-open!
// fn could be changed, therefore close "old" package first
if (this.openPackage != null)
{
try
{
this.openPackage.Close();
if (!writeFreshly)
System.IO.File.Copy(this.fn, fn);
}
catch { }
this.openPackage = null;
}
// approach is to utilize the existing package, if possible. If not, create from scratch
var package = Package.Open(fn, (writeFreshly) ? FileMode.Create : FileMode.OpenOrCreate);
this.fn = fn;
// get the origin from the package
PackagePart originPart = null;
var xs = package.GetRelationshipsByType("http://www.admin-shell.io/aasx/relationships/aasx-origin");
foreach (var x in xs)
if (x.SourceUri.ToString() == "/")
{
originPart = package.GetPart(x.TargetUri);
break;
}
if (originPart == null)
{
// create, as not existing
originPart = package.CreatePart(new Uri("/aasx/aasx-origin", UriKind.RelativeOrAbsolute), System.Net.Mime.MediaTypeNames.Text.Plain, CompressionOption.Maximum);
using (var s = originPart.GetStream(FileMode.Create))
{
var bytes = System.Text.Encoding.ASCII.GetBytes("Intentionally empty.");
s.Write(bytes, 0, bytes.Length);
}
package.CreateRelationship(originPart.Uri, TargetMode.Internal, "http://www.admin-shell.io/aasx/relationships/aasx-origin");
}
// get the specs from the package
PackagePart specPart = null;
xs = originPart.GetRelationshipsByType("http://www.admin-shell.io/aasx/relationships/aas-spec");
foreach (var x in xs)
{
specPart = package.GetPart(x.TargetUri);
break;
}
if (specPart == null)
{
// create, as not existing
var frn = "aasenv-with-no-id";
if (this.aasenv.AdministrationShells.Count > 0)
frn = this.aasenv.AdministrationShells[0].GetFriendlyName() ?? frn;
var aas_spec_fn = "/aasx/#/#.aas.xml".Replace("#", "" + frn);
specPart = package.CreatePart(new Uri(aas_spec_fn, UriKind.RelativeOrAbsolute), System.Net.Mime.MediaTypeNames.Text.Xml, CompressionOption.Maximum);
originPart.CreateRelationship(specPart.Uri, TargetMode.Internal, "http://www.admin-shell.io/aasx/relationships/aas-spec");
}
// now, shall be != null!
using (var s = specPart.GetStream(FileMode.Create))
{
var serializer = new XmlSerializer(typeof(AdminShell.AdministrationShellEnv));
var nss = new XmlSerializerNamespaces();
nss.Add("aas", "http://www.admin-shell.io/aas/1/0");
nss.Add("IEC61360", "http://www.admin-shell.io/IEC61360/1/0");
serializer.Serialize(s, this.aasenv, nss);
}
// there might be pending files to be deleted (first delete, then add, in case of identical files in both categories)
foreach (var psfDel in pendingFilesToDelete)
{
// try find an existing part for that file ..
var found = false;
xs = specPart.GetRelationshipsByType("http://www.admin-shell.io/aasx/relationships/aas-suppl");
foreach (var x in xs)
if (x.TargetUri == psfDel.uri)
{
// try to delete
specPart.DeleteRelationship(x.Id);
package.DeletePart(psfDel.uri);
found = true;
break;
}
if (!found)
throw (new Exception($"Not able to delete pending file {psfDel.uri} in saving package {fn}"));
}
// after this, there are no more pending for delete files
pendingFilesToDelete.Clear();
// write pending supplementary files
foreach (var psfAdd in pendingFilesToAdd)
{
// make sure ..
if (psfAdd.sourcePath == null || psfAdd.location != PackageSupplementaryFile.LocationType.AddPending)
continue;
// normal file?
if (psfAdd.specialHandling == PackageSupplementaryFile.SpecialHandlingType.None)
{
// try find an existing part for that file ..
PackagePart filePart = null;
xs = specPart.GetRelationshipsByType("http://www.admin-shell.io/aasx/relationships/aas-suppl");
foreach (var x in xs)
if (x.TargetUri == psfAdd.uri)
{
filePart = package.GetPart(x.TargetUri);
break;
}
if (filePart == null)
{
// create new part and link
filePart = package.CreatePart(psfAdd.uri, AdminShell.PackageEnv.GuessMimeType(psfAdd.sourcePath), CompressionOption.Maximum);
specPart.CreateRelationship(filePart.Uri, TargetMode.Internal, "http://www.admin-shell.io/aasx/relationships/aas-suppl");
}
// now should be able to write
using (var s = filePart.GetStream(FileMode.Create))
{
var bytes = System.IO.File.ReadAllBytes(psfAdd.sourcePath);
s.Write(bytes, 0, bytes.Length);
}
}
// thumbnail file?
if (psfAdd.specialHandling == PackageSupplementaryFile.SpecialHandlingType.EmbedAsThumbnail)
{
// try find an existing part for that file ..
PackagePart filePart = null;
xs = package.GetRelationshipsByType("http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail");
foreach (var x in xs)
if (x.SourceUri.ToString() == "/" && x.TargetUri == psfAdd.uri)
{
filePart = package.GetPart(x.TargetUri);
break;
}
if (filePart == null)
{
// create new part and link
filePart = package.CreatePart(psfAdd.uri, AdminShell.PackageEnv.GuessMimeType(psfAdd.sourcePath), CompressionOption.Maximum);
package.CreateRelationship(filePart.Uri, TargetMode.Internal, "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail");
}
// now should be able to write
using (var s = filePart.GetStream(FileMode.Create))
{
var bytes = System.IO.File.ReadAllBytes(psfAdd.sourcePath);
s.Write