Skip to content

Commit

Permalink
Merge pull request #4827 from NREL/4817_BCLXML_Validation
Browse files Browse the repository at this point in the history
#4748 #4817 - Validate BCLXML with schema when loading + make sorting of files in measure.xml consistent when saving
  • Loading branch information
tijcolem authored May 17, 2023
2 parents 53176d7 + c07e7a2 commit ac91aaa
Show file tree
Hide file tree
Showing 27 changed files with 2,824 additions and 140 deletions.
2 changes: 1 addition & 1 deletion ConanInstall.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ if(NOT CONAN_OPENSTUDIO_ALREADY_RUN)
"cpprestsdk/2.10.18#df2f6ac88e47cadd9c9e8e0971e00d89"
"websocketpp/0.8.2#3fd704c4c5388d9c08b11af86f79f616"
"geographiclib/1.52#76536a9315a003ef3511919310b2fe37"
"swig/4.0.2#9fcccb1e39eed9acd53a4363d8129be5"
"swig/4.1.0#f4419c5ab88f877272cbaa36dd0237ff"
"tinygltf/2.5.0#c8b2aca9505e86312bb42aa0e1c639ec"
"cli11/2.3.2#8ccdf14fb1ad53532d498c16ae580b4b"
${CONAN_GTEST}
Expand Down
2 changes: 1 addition & 1 deletion src/cli/test/logger_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
handler.setFormatter(formatter)
logger.addHandler(handler)

openstudio.Logger_instance().standardOutLogger().setLogLevel(openstudio.Error)
openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Error)

logger.info("STDOUT Info")
logger.warning("STDOUT Warn")
Expand Down
4 changes: 3 additions & 1 deletion src/utilities/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ set(bcl_src
bcl/BCL.cpp
bcl/BCLComponent.hpp
bcl/BCLComponent.cpp
bcl/BCLEnums.hpp
bcl/BCLFileReference.hpp
bcl/BCLFileReference.cpp
bcl/BCLMeasure.hpp
Expand Down Expand Up @@ -504,6 +505,7 @@ add_library(openstudio_utilities_minimal
core/StringStreamLogSink.hpp
core/StringStreamLogSink_Impl.hpp
core/StringStreamLogSink.cpp
bcl/BCLEnums.hpp
)
target_link_libraries(openstudio_utilities_minimal PUBLIC ${${target_name}_depends})

Expand Down Expand Up @@ -572,6 +574,6 @@ MAKE_SWIG_TARGET(OpenStudioUtilitiesSql utilitiessql "${CMAKE_CURRENT_SOURCE_DIR
MAKE_SWIG_TARGET(OpenStudioUtilitiesTime utilitiestime "${CMAKE_CURRENT_SOURCE_DIR}/UtilitiesTime.i" "${${target_name}_swig_src}" ${swig_target_name} OpenStudioUtilitiesSql)
MAKE_SWIG_TARGET(OpenStudioUtilitiesUnits utilitiesunits "${CMAKE_CURRENT_SOURCE_DIR}/UtilitiesUnits.i" "${${target_name}_swig_src}" ${swig_target_name} OpenStudioUtilitiesTime)
MAKE_SWIG_TARGET(OpenStudioUtilitiesFileTypes utilitiesfiletypes "${CMAKE_CURRENT_SOURCE_DIR}/UtilitiesFileTypes.i" "${${target_name}_swig_src}" ${swig_target_name} OpenStudioUtilitiesUnits)
MAKE_SWIG_TARGET(OpenStudioUtilitiesXML utilitiesxml "${CMAKE_CURRENT_SOURCE_DIR}/UtilitiesXML.i" "${${target_name}_swig_src}" ${swig_target_name} OpenStudioUtilitiesCore)
MAKE_SWIG_TARGET(OpenStudioUtilitiesXML utilitiesxml "${CMAKE_CURRENT_SOURCE_DIR}/UtilitiesXML.i" "${${target_name}_swig_src}" ${swig_target_name} OpenStudioUtilitiesBCL)

MAKE_SWIG_TARGET(OpenStudioUtilities utilities "${CMAKE_CURRENT_SOURCE_DIR}/Utilities.i" "${${target_name}_swig_src}" ${swig_target_name} OpenStudioUtilitiesFileTypes)
94 changes: 94 additions & 0 deletions src/utilities/bcl/BCLEnums.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/***********************************************************************************************************************
* OpenStudio(R), Copyright (c) 2008-2022, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
* (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following
* disclaimer.
*
* (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the distribution.
*
* (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse or promote products
* derived from this software without specific prior written permission from the respective party.
*
* (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other derivative works
* may not use the "OpenStudio" trademark, "OS", "os", or any other confusingly similar designation without specific prior
* written permission from Alliance for Sustainable Energy, LLC.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE UNITED STATES GOVERNMENT, OR THE UNITED
* STATES DEPARTMENT OF ENERGY, NOR ANY OF THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
***********************************************************************************************************************/

#ifndef UTILITIES_BCL_BCLENUMS_HPP
#define UTILITIES_BCL_BCLENUMS_HPP

#include "../core/Enum.hpp"

namespace openstudio {

// clang-format off

/** \class MeasureType
* \brief Enumeration of the types of BCLMeasure, by input file type.
* \details ModelMeasures accept OpenStudio Models as input; EnergyPlusMeasures accept
* EnergyPlus IDF files as input; and UtilityMeasures do not operate on any sort of energy
* model. See the OPENSTUDIO_ENUM documentation in utilities/core/Enum.hpp. The actual macro
* call is:
* \code
OPENSTUDIO_ENUM( MeasureType,
((ModelMeasure)(Model Measure))
((EnergyPlusMeasure)(EnergyPlus Measure))
((UtilityMeasure)(Utility Measure))
((ReportingMeasure)(Reporting Measure))
);
* \endcode */
OPENSTUDIO_ENUM( MeasureType,
((ModelMeasure)(Model Measure))
((EnergyPlusMeasure)(EnergyPlus Measure))
((UtilityMeasure)(Utility Measure))
((ReportingMeasure)(Reporting Measure))
);

OPENSTUDIO_ENUM( MeasureBadgeType,
((BCLMeasure))
((MyMeasure))
((OSMeasure))
);

OPENSTUDIO_ENUM( MeasureLanguage,
((Ruby))
((Python))
);

/** \class BCLXMLType
* \brief Enumeration of the BCL XML file types.
* \details The Building Component Library (BCL) hosts both components and measures. The
* meta-data for individual instances of these two types of items are transmitted using XML
* files that with slightly different structures. Thus, this enum helps distinguish between the
* expected schema.
*
* See the OPENSTUDIO_ENUM documentation in utilities/core/Enum.hpp. The actual macro call is:
* \code
OPENSTUDIO_ENUM(BCLXMLType,
((ComponentXML)(ComponentXML))
((MeasureXML)(MeasureXML))
);
* \endcode */
OPENSTUDIO_ENUM(BCLXMLType,
((ComponentXML)(ComponentXML))
((MeasureXML)(MeasureXML))
);

// clang-format on

} // namespace openstudio

#endif // UTILITIES_BCL_BCLENUMS_HPP
13 changes: 13 additions & 0 deletions src/utilities/bcl/BCLFileReference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,19 @@ bool BCLFileReference::checkForUpdate() {
return false;
}

// bool operator==(const BCLFileReference& lhs, const BCLFileReference& rhs) {
// return lhs.m_path == rhs.m_path;
// }
//
// std::strong_ordering operator<=>(const BCLFileReference& lhs, const BCLFileReference& rhs) {
// if (lhs.m_path < rhs.m_path) {
// return std::strong_ordering::less;
// } else if (lhs.m_path == rhs.m_path) {
// return std::strong_ordering::equal;
// }
// return std::strong_ordering::greater;
// }

std::ostream& operator<<(std::ostream& os, const BCLFileReference& file) {
pugi::xml_document doc;
auto element = doc.append_child("File");
Expand Down
30 changes: 27 additions & 3 deletions src/utilities/bcl/BCLFileReference.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,8 @@

#include "../core/Logger.hpp"
#include "../core/Path.hpp"
#include "../core/Compare.hpp"
#include "../UtilitiesAPI.hpp"

#include <vector>

namespace pugi {
class xml_node;
}
Expand Down Expand Up @@ -122,6 +119,20 @@ class UTILITIES_API BCLFileReference

//@}

protected:
// Declaring the equality operator and the spaceship operator (three-way comparison operator) will end up defining all comparison operators
// We really need only the operator< for sorting a STL container of BCLFileReference, but might as well be consistent
// Comparison is done on m_path. Sorting is useful for BCLXML to avoid reordering of files (see #4748)
// TODO: compiler/SWIG support still seems too sparse (need GCC 10+, Apple Clang 13 at least), let's avoid it for now
// friend bool operator==(const BCLFileReference& lhs, const BCLFileReference& rhs);
// friend std::strong_ordering operator<=>(const BCLFileReference& lhs, const BCLFileReference& rhs);
friend inline bool operator==(const BCLFileReference& lhs, const BCLFileReference& rhs) {
return lhs.m_path == rhs.m_path;
}
friend inline bool operator<(const BCLFileReference& lhs, const BCLFileReference& rhs) {
return lhs.m_path < rhs.m_path;
}

private:
// configure logging
REGISTER_LOGGER("utilities.bcl.BCLFileReference");
Expand All @@ -141,6 +152,19 @@ class UTILITIES_API BCLFileReference
/** Prints BCLFileReference to os. \relates BCLFileReference */
UTILITIES_API std::ostream& operator<<(std::ostream& os, const BCLFileReference& file);

inline bool operator!=(const BCLFileReference& lhs, const BCLFileReference& rhs) {
return !operator==(lhs, rhs);
}
inline bool operator>(const BCLFileReference& lhs, const BCLFileReference& rhs) {
return operator<(rhs, lhs);
}
inline bool operator<=(const BCLFileReference& lhs, const BCLFileReference& rhs) {
return !operator>(lhs, rhs);
}
inline bool operator>=(const BCLFileReference& lhs, const BCLFileReference& rhs) {
return !operator<(lhs, rhs);
}

} // namespace openstudio

#endif // UTILITIES_BCL_BCLFILEREFERENCE_HPP
37 changes: 1 addition & 36 deletions src/utilities/bcl/BCLMeasure.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#define UTILITIES_BCL_BCLMEASURE_HPP

#include "BCLXML.hpp"
#include "BCLEnums.hpp"
#include "../core/Optional.hpp"
#include "../core/Path.hpp"
#include "../core/Deprecated.hpp"
Expand All @@ -43,42 +44,6 @@ namespace openstudio {

class FileReferenceType;

// clang-format off

/** \class MeasureType
* \brief Enumeration of the types of BCLMeasure, by input file type.
* \details ModelMeasures accept OpenStudio Models as input; EnergyPlusMeasures accept
* EnergyPlus IDF files as input; and UtilityMeasures do not operate on any sort of energy
* model. See the OPENSTUDIO_ENUM documentation in utilities/core/Enum.hpp. The actual macro
* call is:
* \code
OPENSTUDIO_ENUM( MeasureType,
((ModelMeasure)(Model Measure))
((EnergyPlusMeasure)(EnergyPlus Measure))
((UtilityMeasure)(Utility Measure))
((ReportingMeasure)(Reporting Measure))
);
* \endcode */
OPENSTUDIO_ENUM( MeasureType,
((ModelMeasure)(Model Measure))
((EnergyPlusMeasure)(EnergyPlus Measure))
((UtilityMeasure)(Utility Measure))
((ReportingMeasure)(Reporting Measure))
);

OPENSTUDIO_ENUM( MeasureBadgeType,
((BCLMeasure))
((MyMeasure))
((OSMeasure))
);

OPENSTUDIO_ENUM( MeasureLanguage,
((Ruby))
((Python))
);

// clang-format on

/** BCLMeasure is a class for managing the contents of a BCL Measure directory including the xml description file.
**/
class UTILITIES_API BCLMeasure
Expand Down
41 changes: 25 additions & 16 deletions src/utilities/bcl/BCLMeasureArgument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,28 @@
namespace openstudio {

BCLMeasureArgument::BCLMeasureArgument(const pugi::xml_node& element) {

// TODO: escape name
// cppcheck-suppress useInitializationList
m_name = element.child("name").text().as_string();

m_displayName = element.child("display_name").text().as_string();

m_description = element.child("description").text().as_string();
auto getOptionaString = [&element](const char* name) -> boost::optional<std::string> {
if (auto subelement = element.child(name)) {
const std::string text = subelement.text().as_string();
if (!text.empty()) {
return {text};
}
}
return boost::none;
};

m_description = getOptionaString("description");

m_type = element.child("type").text().as_string();

m_units = element.child("units").text().as_string();
m_units = getOptionaString("units");

m_required = false;
std::string test = element.child("required").text().as_string();
Expand All @@ -60,28 +71,26 @@ BCLMeasureArgument::BCLMeasureArgument(const pugi::xml_node& element) {
m_modelDependent = true;
}

m_defaultValue = element.child("default_value").text().as_string();
m_defaultValue = getOptionaString("default_value");

auto subelement = element.child("choices");
if (subelement) {
if (auto subelement = element.child("choices")) {
for (auto& choiceElement : subelement.children("choice")) {
std::string choiceValue = choiceElement.child("value").text().as_string();
const std::string choiceValue = choiceElement.child("value").text().as_string();
if (!choiceValue.empty()) { // Not exactly the same test as before, probably needs a better test and/or error reporting
m_choiceValues.push_back(choiceValue);
auto display_name = choiceElement.child("display_name");
if (!display_name) {
// DLM: this is technically an invalid file, attempt to fix it here
m_choiceDisplayNames.push_back(choiceValue);
} else {
m_choiceDisplayNames.push_back(display_name.text().as_string());
m_choiceDisplayNames.emplace_back(display_name.text().as_string());
}
}
}
}

m_minValue = element.child("min_value").text().as_string();

m_maxValue = element.child("max_value").text().as_string();
m_minValue = getOptionaString("min_value");
m_maxValue = getOptionaString("max_value");
}

BCLMeasureArgument::BCLMeasureArgument(const std::string& name, const std::string& displayName, const boost::optional<std::string>& description,
Expand Down Expand Up @@ -168,7 +177,7 @@ void BCLMeasureArgument::writeValues(pugi::xml_node& element) const {
if (m_description) {
subElement = element.append_child("description");
text = subElement.text();
text.set((*m_description).c_str());
text.set(m_description->c_str());
}

subElement = element.append_child("type");
Expand All @@ -178,7 +187,7 @@ void BCLMeasureArgument::writeValues(pugi::xml_node& element) const {
if (m_units) {
subElement = element.append_child("units");
text = subElement.text();
text.set((*m_units).c_str());
text.set(m_units->c_str());
}

subElement = element.append_child("required");
Expand All @@ -192,10 +201,10 @@ void BCLMeasureArgument::writeValues(pugi::xml_node& element) const {
if (m_defaultValue) {
subElement = element.append_child("default_value");
text = subElement.text();
text.set((*m_defaultValue).c_str());
text.set(m_defaultValue->c_str());
}

unsigned n = m_choiceValues.size();
const unsigned n = m_choiceValues.size();
OS_ASSERT(n == m_choiceDisplayNames.size());
if (n > 0) {
auto choicesElement = element.append_child("choices");
Expand All @@ -215,13 +224,13 @@ void BCLMeasureArgument::writeValues(pugi::xml_node& element) const {
if (m_minValue) {
subElement = element.append_child("min_value");
text = subElement.text();
text.set((*m_minValue).c_str());
text.set(m_minValue->c_str());
}

if (m_maxValue) {
subElement = element.append_child("max_value");
text = subElement.text();
text.set((*m_maxValue).c_str());
text.set(m_maxValue->c_str());
}
}

Expand Down
Loading

0 comments on commit ac91aaa

Please sign in to comment.