diff --git a/BsddRevitPlugin.Logic/IfcJson/IfcJson.cs b/BsddRevitPlugin.Logic/IfcJson/IfcJson.cs index 0efb363..8d5c1bc 100644 --- a/BsddRevitPlugin.Logic/IfcJson/IfcJson.cs +++ b/BsddRevitPlugin.Logic/IfcJson/IfcJson.cs @@ -138,16 +138,16 @@ public class IfcPropertySet public string Name { get; set; } [JsonProperty("hasProperties")] - public List HasProperties { get; set; } + public List HasProperties { get; set; } } /// - /// Represents an IFC property with a single value. + /// IfcProperty is an abstract generalization for all types of properties that can be associated with IFC objects through the property set mechanism. /// - public class IfcPropertySingleValue + public class IfcProperty { [JsonProperty("type")] - public string Type { get; set; } + public virtual string Type { get; } [JsonProperty("name")] public string Name { get; set; } @@ -155,14 +155,53 @@ public class IfcPropertySingleValue [JsonProperty("specification")] public string Specification { get; set; } + public IfcProperty() + { + Type = "IfcProperty"; + } + } + + /// + /// The property with a single value IfcPropertySingleValue defines a property object which has a single (numeric or descriptive) value assigned. + /// + public class IfcPropertySingleValue : IfcProperty + { + [JsonProperty("type")] + public override string Type { get; } + [JsonProperty("nominalValue")] - public NominalValue NominalValue { get; set; } + public IfcValue NominalValue { get; set; } + + public IfcPropertySingleValue() : base() + { + Type = "IfcPropertySingleValue"; + } + } + + /// + /// A property with an enumerated value, IfcPropertyEnumeratedValue, defines a property object which has a value assigned that is chosen from an enumeration. + /// + public class IfcPropertyEnumeratedValue : IfcProperty + { + [JsonProperty("type")] + public override string Type { get; } + + [JsonProperty("enumerationValues")] + public List EnumerationValues { get; set; } + + [JsonProperty("enumerationReference")] + public IfcPropertyEnumeration EnumerationReference { get; set; } + + public IfcPropertyEnumeratedValue() : base() + { + Type = "IfcPropertyEnumeratedValue"; + } } /// /// Represents the nominal value of an IFC property. /// - public class NominalValue + public class IfcValue { [JsonProperty("type")] public string Type { get; set; } @@ -171,5 +210,21 @@ public class NominalValue public object Value { get; set; } } + public class IfcPropertyEnumeration + { + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("enumerationValues")] + public HashSet EnumerationValues { get; set; } + + public IfcPropertyEnumeration() + { + Type = "IfcPropertyEnumeration"; + } + } } \ No newline at end of file diff --git a/BsddRevitPlugin.Logic/IfcJson/IfcJsonConverter.cs b/BsddRevitPlugin.Logic/IfcJson/IfcJsonConverter.cs index 746f022..92b8c99 100644 --- a/BsddRevitPlugin.Logic/IfcJson/IfcJsonConverter.cs +++ b/BsddRevitPlugin.Logic/IfcJson/IfcJsonConverter.cs @@ -59,18 +59,30 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist } } } - + if (jsonObject["isDefinedBy"] != null) { ifcData.IsDefinedBy = new List(); foreach (JObject item in jsonObject["isDefinedBy"]) { - switch (item["type"].ToString()) + IfcPropertySet propertySet = item.ToObject(); + if (item["hasProperties"] != null) { - case "IfcPropertySet": - ifcData.IsDefinedBy.Add(item.ToObject()); - break; + propertySet.HasProperties = new List(); + foreach (JObject propertyItem in item["hasProperties"]) + { + switch (propertyItem["type"].ToString()) + { + case "IfcPropertySingleValue": + propertySet.HasProperties.Add(propertyItem.ToObject()); + break; + case "IfcPropertyEnumeratedValue": + propertySet.HasProperties.Add(propertyItem.ToObject()); + break; + } + } } + ifcData.IsDefinedBy.Add(propertySet); } } diff --git a/BsddRevitPlugin.Logic/Model/ElementsManager.cs b/BsddRevitPlugin.Logic/Model/ElementsManager.cs index 54b0d6e..7aa8593 100644 --- a/BsddRevitPlugin.Logic/Model/ElementsManager.cs +++ b/BsddRevitPlugin.Logic/Model/ElementsManager.cs @@ -11,11 +11,6 @@ using System.Collections.Generic; using System.Linq; using System.IO; -using System.Xaml; -using Revit.IFC.Import.Data; -using System.Windows.Input; -using Revit.IFC.Export.Exporter.PropertySet; -using System.Security.Principal; namespace BsddRevitPlugin.Logic.Model @@ -190,21 +185,29 @@ public static void SetIfcDataToRevitElement(Document doc, IfcEntity ifcEntity) { foreach (var property in propertySet.HasProperties) { - if (property.NominalValue != null) + if (property.Type == null) { - if (property.NominalValue.Type != null) - { - //Else default specType string.text is used - specType = GetParameterTypeFromProperty(property); - } - else + continue; + } + + if (property.Type == "IfcPropertySingleValue") + { + var propertySingleValue = property as IfcPropertySingleValue; + if (propertySingleValue.NominalValue == null) { - specType = SpecTypeId.String.Text; + continue; } + specType = GetParameterTypeFromProperty(propertySingleValue.NominalValue); } - else + else if (property.Type == "IfcPropertyEnumeratedValue") { - specType = SpecTypeId.String.Text; + var propertyEnumeratedValue = property as IfcPropertyEnumeratedValue; + if (propertyEnumeratedValue.EnumerationValues == null || propertyEnumeratedValue.EnumerationValues.Count == 0) + { + continue; + } + var enumerationValue = propertyEnumeratedValue.EnumerationValues.First(); + specType = GetParameterTypeFromProperty(enumerationValue); } bsddParameterName = CreateParameterNameFromPropertySetAndProperty(propertySet.Name, property.Name); parametersToCreate.Add(new ParameterCreation(bsddParameterName, specType)); @@ -302,68 +305,30 @@ public static void SetIfcDataToRevitElement(Document doc, IfcEntity ifcEntity) { foreach (var property in propertySet.HasProperties) { - if (property.NominalValue != null) + if (property.Type == null) { + continue; + } - //Create parameter name for each unique the bsdd property - bsddParameterName = CreateParameterNameFromPropertySetAndProperty(propertySet.Name, property.Name); - - ////Commenting this switch: Issue with LoadBearing etc being allready added as a param without all categories - //switch (property.Name) - //{ - // //Allways add a type - // case "Load Bearing": - // bsddParameterName = "LoadBearing"; - // break; - - // //Allways add a predifined type - // case "Is External": - // //add check if Type even exists - // bsddParameterName = "IsExternal"; - // break; - - // //Allways add a predifined type - // case "Fire Rating": - // //add check if Type even exists - // bsddParameterName = "FireRating"; - // break; - - // default: - // bsddParameterName = CreateParameterNameFromPropertySetAndProperty(propertySet.Name, property.Name); - // break; - //} - - //Add a project parameter for the bsdd parameter in all Revit categorices if it does not exist - //NOTE: THIS IS UP FOR DISCUSSION, AS IT MIGHT NOT BE NECESSARY TO ADD THE PARAMETER TO ALL CATEGORIES - //Utilities.Parameters.CreateProjectParameterForAllCategories(doc, bsddParameterName, "tempGroupName", specType, groupType, false); - - if (property.NominalValue.Value != null) + if (property.Type == "IfcPropertySingleValue") + { + var propertySingleValue = property as IfcPropertySingleValue; + if (propertySingleValue.NominalValue == null) { - dynamic value = GetParameterValueInCorrectDatatype(property); - - //Check each type parameter from the object - foreach (Parameter typeparameter in elementType.Parameters) - { - string typeParameterName = typeparameter.Definition.Name; - - - //Add the bsdd value to the parameter - if (typeParameterName == bsddParameterName) - { - try - { - //because the value is dynamic, always try catch - typeparameter.Set(value); - } - catch (Exception e) - { - logger.Info($"Property {property.Name} of type {property.Type} could not be set for elementType {elementType.Name},'{elementType.Id}'. Exception: {e.Message}"); - } - } - } + continue; } + createAndSetTypeProperty(elementType, propertySet, property.Name, propertySingleValue.NominalValue); + } + else if (property.Type == "IfcPropertyEnumeratedValue") + { + var propertyEnumeratedValue = property as IfcPropertyEnumeratedValue; + if (propertyEnumeratedValue.EnumerationValues == null || propertyEnumeratedValue.EnumerationValues.Count == 0) + { + continue; + } + var enumerationValue = propertyEnumeratedValue.EnumerationValues.First(); + createAndSetTypeProperty(elementType, propertySet, property.Name, enumerationValue); } - } } } @@ -378,6 +343,70 @@ public static void SetIfcDataToRevitElement(Document doc, IfcEntity ifcEntity) throw; } } + + private static void createAndSetTypeProperty(ElementType elementType, IfcPropertySet propertySet, string propertyName, IfcValue propertyValue) + { + + Logger logger = LogManager.GetCurrentClassLogger(); + + //Create parameter name for each unique the bsdd property + string bsddParameterName = CreateParameterNameFromPropertySetAndProperty(propertySet.Name, propertyName); + + ////Commenting this switch: Issue with LoadBearing etc being allready added as a param without all categories + //switch (property.Name) + //{ + // //Allways add a type + // case "Load Bearing": + // bsddParameterName = "LoadBearing"; + // break; + + // //Allways add a predifined type + // case "Is External": + // //add check if Type even exists + // bsddParameterName = "IsExternal"; + // break; + + // //Allways add a predifined type + // case "Fire Rating": + // //add check if Type even exists + // bsddParameterName = "FireRating"; + // break; + + // default: + // bsddParameterName = CreateParameterNameFromPropertySetAndProperty(propertySet.Name, property.Name); + // break; + //} + + //Add a project parameter for the bsdd parameter in all Revit categorices if it does not exist + //NOTE: THIS IS UP FOR DISCUSSION, AS IT MIGHT NOT BE NECESSARY TO ADD THE PARAMETER TO ALL CATEGORIES + //Utilities.Parameters.CreateProjectParameterForAllCategories(doc, bsddParameterName, "tempGroupName", specType, groupType, false); + + if (propertyValue.Value != null) + { + dynamic value = GetParameterValueInCorrectDatatype(propertyValue); + + //Check each type parameter from the object + foreach (Parameter typeparameter in elementType.Parameters) + { + string typeParameterName = typeparameter.Definition.Name; + + + //Add the bsdd value to the parameter + if (typeParameterName == bsddParameterName) + { + try + { + //because the value is dynamic, always try catch + typeparameter.Set(value); + } + catch (Exception e) + { + logger.Info($"Property {propertyName} could not be set for elementType {elementType.Name},'{elementType.Id}'. Exception: {e.Message}"); + } + } + } + } + } public static void SelectElementsWithIfcData(UIDocument uidoc, IfcEntity ifcEntity) { Logger logger = LogManager.GetCurrentClassLogger(); @@ -426,14 +455,14 @@ public static void SelectElementsWithIfcData(UIDocument uidoc, IfcEntity ifcEnti /// /// Converts the value of the given IFC property to the correct datatype. /// - /// The IFC property to convert. + /// The IFC property to convert. /// The converted value, or a default value if the conversion fails. - private static dynamic GetParameterValueInCorrectDatatype(IfcPropertySingleValue property) + private static dynamic GetParameterValueInCorrectDatatype(IfcValue propertyValue) { - dynamic value = property.NominalValue.Value; + dynamic value = propertyValue.Value; // Parse value to correct datatype - switch (property.NominalValue.Type) + switch (propertyValue.Type) { case "IfcBoolean": value = TryConvertValue(value, new Func(v => (bool)v ? 1 : 0), 0); @@ -450,7 +479,7 @@ private static dynamic GetParameterValueInCorrectDatatype(IfcPropertySingleValue value = TryConvertValue(value, new Func(v => Convert.ToDateTime(v).ToString()), ""); break; default: - // IfcString or Default + // IfcText, IfcLabel, IfcIdentifier or Default value = TryConvertValue(value, new Func(v => v.ToString()), ""); break; } @@ -537,13 +566,13 @@ private static dynamic TryConvertValue(dynamic value, Func conv /// /// The IFC property. /// The corresponding Revit parameter type. - private static ForgeTypeId GetParameterTypeFromProperty(IfcPropertySingleValue property) + private static ForgeTypeId GetParameterTypeFromProperty(IfcValue ifcValue) { // The type of the nominal value in the IFC property - string nominalValueType = property.NominalValue.Type; + string valueType = ifcValue.Type; // Map the IFC type to the corresponding Revit parameter type - switch (nominalValueType) + switch (valueType) { case "IfcBoolean": // Map IfcBoolean to Revit's YesNo type @@ -563,6 +592,8 @@ private static ForgeTypeId GetParameterTypeFromProperty(IfcPropertySingleValue p return SpecTypeId.String.Text; case "IfcText": + case "IfcLabel": + case "IfcIdentifier": // Map IfcText to Revit's Text type return SpecTypeId.String.Text; @@ -804,7 +835,6 @@ public static IEnumerable GetActiveDictionaries() /// A IfcData object representing the ifcJSON structure. public static List SelectionToIfcJson(Document doc, List elemList) { - if (doc == null || elemList == null) { throw new ArgumentNullException(doc == null ? nameof(doc) : nameof(elemList)); @@ -843,8 +873,6 @@ private static IfcEntity CreateIfcEntity(ElementType elem, Document doc) string ifcType = IFCMappingValue(doc, elem); string ifcPredefinedType = elem.get_Parameter(BuiltInParameter.IFC_EXPORT_PREDEFINEDTYPE_TYPE)?.AsString(); - var associations = GetElementTypeAssociations(elem); - var ifcEntity = new IfcEntity { Type = ifcType, @@ -855,8 +883,13 @@ private static IfcEntity CreateIfcEntity(ElementType elem, Document doc) }; //embed propertysets bsdd/prop/ in Ifc Defenition - ifcEntity.IsDefinedBy = IfcDefinition(elem); - + List propertySets = IfcDefinition(elem); + if (propertySets != null && propertySets.Count > 0) + { + ifcEntity.IsDefinedBy = propertySets; + } + + var associations = GetElementTypeAssociations(elem); if (associations != null && associations.Count > 0) { ifcEntity.HasAssociations = associations.Values.ToList(); @@ -875,12 +908,12 @@ private static IfcEntity CreateIfcEntity(ElementType elem, Document doc) /// The IfcDefinition with the bsdd parameters public static List IfcDefinition(ElementType elem) { - Dictionary> ifcProps = new Dictionary>(); + Dictionary> ifcProps = new Dictionary>(); List isDefinedBy = new List(); IfcPropertySet ifcPropSet = new IfcPropertySet(); - List hasProperties = new List(); + List hasProperties = new List(); IfcPropertySingleValue ifcPropValue = new IfcPropertySingleValue(); - NominalValue nominalValue = new NominalValue(); + IfcValue nominalValue = new IfcValue(); string[] promptArr = null; List pSetDone = new List(); @@ -895,7 +928,7 @@ public static List IfcDefinition(ElementType elem) if (!pSetDone.Contains(promptArr[0])) { - hasProperties = new List(); + hasProperties = new List(); foreach (Parameter paramPSet in elem.Parameters) { if (paramPSet.Definition.Name.StartsWith("bsdd/prop/" + promptArr[0], false, null) == true) @@ -903,16 +936,39 @@ public static List IfcDefinition(ElementType elem) if (paramPSet.HasValue == true) { //Define nominalvalue Type and Value - nominalValue = new NominalValue(); - nominalValue.Type = "IfcLabel"; - nominalValue.Value = paramPSet.AsValueString(); + nominalValue = new IfcValue(); + ForgeTypeId paramTypeId = paramPSet.Definition.GetDataType(); + + switch (paramTypeId) + { + case var _ when paramTypeId == SpecTypeId.String.Text: + nominalValue.Type = "IfcText"; + nominalValue.Value = paramPSet.AsString(); + break; + case var _ when paramTypeId == SpecTypeId.Number: + nominalValue.Type = "IfcReal"; + nominalValue.Value = paramPSet.AsDouble(); + break; + case var _ when paramTypeId == SpecTypeId.Boolean.YesNo: + nominalValue.Type = "IfcBoolean"; + nominalValue.Value = paramPSet.AsInteger() == 1; + break; + case var _ when paramTypeId == SpecTypeId.Int.Integer: + nominalValue.Type = "IfcInteger"; + nominalValue.Value = paramPSet.AsInteger(); + break; + default: + nominalValue.Type = "IfcText"; + nominalValue.Value = paramPSet.AsValueString(); + break; + } //Define property NominalValue, name and value - //hasProperties = new List { ifcPropValue }; - ifcPropValue = new IfcPropertySingleValue(); - ifcPropValue.NominalValue = nominalValue; - ifcPropValue.Type = "IfcPropertySingleValue"; - ifcPropValue.Name = paramPSet.Definition.Name.Remove(0, 10).Split('/')[1]; + ifcPropValue = new IfcPropertySingleValue + { + NominalValue = nominalValue, + Name = paramPSet.Definition.Name.Remove(0, 10).Split('/')[1] + }; hasProperties.Add(ifcPropValue); } @@ -934,8 +990,7 @@ public static List IfcDefinition(ElementType elem) //embed propertyset in Ifc Defenition return isDefinedBy; } - - + /// /// Retrieves the family name of the given element type. /// @@ -1087,11 +1142,10 @@ public static Uri GetLocationParam(string domain, Element element) return paramValue; } - + public static String IFCMappingValue(Document doc, Element elem) { - - if(elem.get_Parameter(BuiltInParameter.IFC_EXPORT_ELEMENT_TYPE_AS)?.AsString() != null && + if (elem.get_Parameter(BuiltInParameter.IFC_EXPORT_ELEMENT_TYPE_AS)?.AsString() != null && elem.get_Parameter(BuiltInParameter.IFC_EXPORT_ELEMENT_TYPE_AS)?.AsString() != "") { return elem.get_Parameter(BuiltInParameter.IFC_EXPORT_ELEMENT_TYPE_AS)?.AsString(); diff --git a/BsddRevitPlugin.Logic/UI/View/BsddSearch.xaml.cs b/BsddRevitPlugin.Logic/UI/View/BsddSearch.xaml.cs index d7fabb8..de0cbcd 100644 --- a/BsddRevitPlugin.Logic/UI/View/BsddSearch.xaml.cs +++ b/BsddRevitPlugin.Logic/UI/View/BsddSearch.xaml.cs @@ -48,7 +48,7 @@ public BsddSearch(BsddBridgeData bsddBridgeData, ExternalEvent bsddLastSelection // Set the address of the CefSharp browser component to the index.html file of the plugin //_browserService.Address = "https://buildingsmart-community.github.io/bSDD-filter-UI/main/bsdd_search/"; // _browserService.Address = "http://localhost:3000/"; - _browserService.Address = "https://buildingsmart-community.github.io/bSDD-filter-UI/v1.1.2/bsdd_search/"; + _browserService.Address = "https://buildingsmart-community.github.io/bSDD-filter-UI/v1.1.3/bsdd_search/"; var bridgeSearch = new BsddSearchBridge(bsddBridgeData, _bsddLastSelectionEvent); bridgeSearch.SetParentWindow(this); _browserService.RegisterJsObject("bsddBridge", bridgeSearch, true); diff --git a/BsddRevitPlugin.Logic/UI/View/BsddSelection.xaml.cs b/BsddRevitPlugin.Logic/UI/View/BsddSelection.xaml.cs index 10f81dd..dbd8f37 100644 --- a/BsddRevitPlugin.Logic/UI/View/BsddSelection.xaml.cs +++ b/BsddRevitPlugin.Logic/UI/View/BsddSelection.xaml.cs @@ -74,7 +74,7 @@ public BsddSelection(IBrowserService browserService) // Set the address of the CefSharp browser component to the index.html file of the plugin //_browserService.Address = "https://buildingsmart-community.github.io/bSDD-filter-UI/main/bsdd_selection/index.html"; // _browserService.Address = "http://localhost:3000/"; - _browserService.Address = "https://buildingsmart-community.github.io/bSDD-filter-UI/v1.1.2/bsdd_selection/"; + _browserService.Address = "https://buildingsmart-community.github.io/bSDD-filter-UI/v1.1.3/bsdd_selection/"; _browserService.RegisterJsObject("bsddBridge", new BsddSelectionBridge(SelectEULS, updateUIEvent), true); _browserService.IsBrowserInitializedChanged += OnIsBrowserInitializedChanged;