From 2c6909e98edc6577cd4dc0b4165ef3fd90c30580 Mon Sep 17 00:00:00 2001 From: Ralph Wessel Date: Tue, 22 Oct 2024 20:09:01 +0100 Subject: [PATCH] Added property::Group Added property Group database Property template group & group name access added --- .../Speckle/Database/BIMGroupDatabase.cpp | 129 ++++++++++ .../Speckle/Database/BIMGroupDatabase.h | 77 ++++++ .../Speckle/Database/BIMPropertyDatabase.cpp | 6 +- .../Property/ArchicadGroupDBaseEngine.cpp | 232 ++++++++++++++++++ .../Property/ArchicadGroupDBaseEngine.h | 136 ++++++++++ .../Property/ArchicadPropertyDBaseEngine.cpp | 4 +- .../Property/ArchicadPropertyDBaseEngine.h | 14 +- SpeckleLib/Speckle/Environment/Project.cpp | 2 + SpeckleLib/Speckle/Environment/Project.h | 16 +- SpeckleLib/Speckle/Record/Property/Group.cpp | 93 +++++++ SpeckleLib/Speckle/Record/Property/Group.h | 100 ++++++++ .../Speckle/Record/Property/Template.cpp | 24 +- SpeckleLib/Speckle/Record/Property/Template.h | 12 +- .../SpeckleLib.xcodeproj/project.pbxproj | 24 ++ 14 files changed, 842 insertions(+), 27 deletions(-) create mode 100644 SpeckleLib/Speckle/Database/BIMGroupDatabase.cpp create mode 100644 SpeckleLib/Speckle/Database/BIMGroupDatabase.h create mode 100644 SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadGroupDBaseEngine.cpp create mode 100644 SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadGroupDBaseEngine.h create mode 100644 SpeckleLib/Speckle/Record/Property/Group.cpp create mode 100644 SpeckleLib/Speckle/Record/Property/Group.h diff --git a/SpeckleLib/Speckle/Database/BIMGroupDatabase.cpp b/SpeckleLib/Speckle/Database/BIMGroupDatabase.cpp new file mode 100644 index 0000000..2b653a5 --- /dev/null +++ b/SpeckleLib/Speckle/Database/BIMGroupDatabase.cpp @@ -0,0 +1,129 @@ +#include "Speckle/Database/BIMGroupDatabase.h" + +#include "Active/Database/Storage/Storage.h" +#include "Active/Serialise/UnboxedTransport.h" +#include "Speckle/Database/Identity/RecordID.h" +#include "Speckle/Database/Storage/ArchicadDBase/Property/ArchicadGroupDBaseEngine.h" +#include "Speckle/Record/Property/Setting.h" + +#include + +using namespace active::container; +using namespace active::database; +using namespace active::event; +using namespace active::serialise; +using namespace speckle::database; +using namespace speckle::record; +using namespace speckle::record::property; +using namespace speckle::database; +using namespace speckle::utility; + +namespace speckle::database { + + ///Define other platform engines here as required +#ifdef ARCHICAD + using GroupDatabaseEngine = ArchicadGroupDBaseEngine; +#endif + + ///Group database engine declaration + class BIMGroupDatabase::Engine : public GroupDatabaseEngine { + using base = ArchicadGroupDBaseEngine; + using base::base; + }; + + ///Group database storage declaration + class BIMGroupDatabase::Store : public Storage { + using base = Storage; + using base::base; + }; + +} + +namespace { + + ///The database storage identifier for groups + const char* groupDBaseName = "speckle::database::BIMGroupDatabase"; + ///The primary groups table + const char* groupTableName = "Groups"; + +} + +/*-------------------------------------------------------------------- + Constructor + --------------------------------------------------------------------*/ +BIMGroupDatabase::BIMGroupDatabase() { + m_engine = std::make_shared(groupDBaseName, + //Schema + DBaseSchema{active::utility::String{groupDBaseName}, + //Tables + { + //Model group table + { + groupTableName, 0, 0, {} + } + } + } + ); + m_store = std::make_shared(m_engine); +} //BIMGroupDatabase::BIMGroupDatabase + + +/*-------------------------------------------------------------------- + Destructor + --------------------------------------------------------------------*/ +BIMGroupDatabase::~BIMGroupDatabase() {} + + +/*-------------------------------------------------------------------- + Get a specified group + + groupID: The ID of the target group + + return: The requested group (nullptr on failure) + --------------------------------------------------------------------*/ +Group::Unique BIMGroupDatabase::getGroup(const BIMRecordID& groupID, std::optional tableID, + std::optional documentID) const { + return m_engine->getObject(groupID, tableID, documentID); +} //BIMGroupDatabase::getGroup + + +/*-------------------------------------------------------------------- + Get a specified group + + link: A link to the target group + + return: The requested group (nullptr on failure) + --------------------------------------------------------------------*/ +Group::Unique BIMGroupDatabase::getGroup(const BIMLink& link) const { + return getGroup(link, link.tableID, link.docID); +} //BIMGroupDatabase::getGroup + + +/*-------------------------------------------------------------------- + Get all groups + + return: All the groups + --------------------------------------------------------------------*/ +Vector BIMGroupDatabase::getGroups() const { + return m_store->getObjects(); +} //BIMGroupDatabase::getGroups + + +/*-------------------------------------------------------------------- + Write an group to storage + + group: The group to write + --------------------------------------------------------------------*/ +void BIMGroupDatabase::write(const Group& group) const { + m_store->write(group); +} //BIMGroupDatabase::write + + +/*-------------------------------------------------------------------- + Erase an group + + groupID: The ID of the group to erase + --------------------------------------------------------------------*/ +void BIMGroupDatabase::erase(const Guid& groupID) const { + m_store->erase(groupID); +} //BIMGroupDatabase::erase diff --git a/SpeckleLib/Speckle/Database/BIMGroupDatabase.h b/SpeckleLib/Speckle/Database/BIMGroupDatabase.h new file mode 100644 index 0000000..df18e4f --- /dev/null +++ b/SpeckleLib/Speckle/Database/BIMGroupDatabase.h @@ -0,0 +1,77 @@ +#ifndef CONNECTOR_DATABASE_BIM_GROUP_DATABASE +#define CONNECTOR_DATABASE_BIM_GROUP_DATABASE + +#include "Speckle/Database/Identity/BIMLink.h" +#include "Speckle/Record/Property/Group.h" +#include "Speckle/Utility/Guid.h" + +namespace active::event { + class Subscriber; +} + +namespace speckle::database { + + /*! + Database of group templates relating to a specific project + + Note that this database manages just the group templates, not the values. Group values are attached to elements + */ + class BIMGroupDatabase { + public: + + // MARK: - Constructors + + /*! + Constructor + */ + BIMGroupDatabase(); + BIMGroupDatabase(const BIMGroupDatabase&) = delete; + /*! + Destructor + */ + ~BIMGroupDatabase(); + + // MARK: - Functions (const) + + /*! + Get a specified group + @param groupID The ID of the target group + @param tableID Optional table ID (defaults to the floor plan) + @param documentID Optional document ID (when the object is bound to a specific document) + @return The requested group (nullptr on failure) + */ + record::property::Group::Unique getGroup(const BIMRecordID& groupID, std::optional tableID = std::nullopt, + std::optional documentID = std::nullopt) const; + /*! + Get a specified group + @param link A link to the target group + @return The requested group (nullptr on failure) + */ + record::property::Group::Unique getGroup(const BIMLink& link) const; + /*! + Get all model groups + @return All the groups + */ + active::container::Vector getGroups() const; + /*! + Write an group to storage + @param group The group to write + */ + void write(const record::property::Group& group) const; + /*! + Erase an group + @param groupID The ID of the group to erase + */ + void erase(const speckle::utility::Guid& groupID) const; + + private: + class Engine; + class Store; + ///Model group database storage + std::shared_ptr m_engine; + std::shared_ptr m_store; + }; + +} + +#endif //CONNECTOR_DATABASE_BIM_GROUP_DATABASE diff --git a/SpeckleLib/Speckle/Database/BIMPropertyDatabase.cpp b/SpeckleLib/Speckle/Database/BIMPropertyDatabase.cpp index ffb0f5f..5456f62 100644 --- a/SpeckleLib/Speckle/Database/BIMPropertyDatabase.cpp +++ b/SpeckleLib/Speckle/Database/BIMPropertyDatabase.cpp @@ -43,8 +43,8 @@ namespace { ///The database storage identifier for properties const char* propertyDBaseName = "speckle::database::BIMPropertyDatabase"; - ///The primary model table, e.g. floor plan in Archicad - const char* modelTableName = "Model"; + ///The primary properties table + const char* propertyTableName = "Properties"; } @@ -59,7 +59,7 @@ BIMPropertyDatabase::BIMPropertyDatabase() { { //Model property table { - modelTableName, 0, 0, {} //The primary model. Additonal tables could be linked to other drawings/layouts in future + propertyTableName, 0, 0, {} } } } diff --git a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadGroupDBaseEngine.cpp b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadGroupDBaseEngine.cpp new file mode 100644 index 0000000..fecc5d2 --- /dev/null +++ b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadGroupDBaseEngine.cpp @@ -0,0 +1,232 @@ +#include "Speckle/Database/Storage/ArchicadDBase/Property/ArchicadGroupDBaseEngine.h" + +#ifdef ARCHICAD + +#include "Active/Utility/Memory.h" +#include "Speckle/Database/Identity/BIMLink.h" +#include "Speckle/Environment/Addon.h" +#include "Speckle/Environment/Project.h" +#include "Speckle/Event/Type/ProjectEvent.h" +#include "Speckle/Record/Property/Setting.h" +#include "Speckle/Utility/Guid.h" +#include "Speckle/Utility/String.h" + +#include +#include + +using namespace active::event; +using namespace active::setting; +using namespace speckle::database; +using namespace speckle::environment; +using namespace speckle::event; +using namespace speckle::record::property; +using namespace speckle::utility; + +using enum ArchicadDBaseCore::Status; + +namespace { + + /*! + Make a new group object + @param groupData The API group representation + @return A new group object (nullptr on failure) + */ + Group::Shared makeGroup(const API_PropertyGroup& groupData) { + //NB: This function has been written to allow for the future possibility of different methods for constructing a group and/or + //failure to make one + return std::make_shared(groupData); + } + +} + +namespace speckle::database { + + class ArchicadGroupDBaseEngine::Cache : public std::unordered_map> { + public: + /*! + Default constructor + */ + Cache() { rebuild(); } + + /*! + Rebuild the property group cache + */ + void rebuild() { + //Request all Archicad group groups + GS::Array groups; + if (auto err = ACAPI_Property_GetPropertyGroups(groups); err != NoError) + return; + //Populate the group cache from the collected groups + for (auto iter = groups.Begin(); iter != groups.End(); ++iter) + if (auto propGroup = makeGroup(*iter); propGroup) + insert({propGroup->getBIMID(), propGroup}); + } + }; + +} + +/*-------------------------------------------------------------------- + Constructor + + id: The document storage identifier + schema: The document storage schema + --------------------------------------------------------------------*/ +ArchicadGroupDBaseEngine::ArchicadGroupDBaseEngine(const active::utility::NameID& id, ArchicadDBaseSchema&& schema) : + ArchicadDBaseCore{id, std::move(schema)} { +} //ArchicadGroupDBaseEngine::ArchicadGroupDBaseEngine + + +/*-------------------------------------------------------------------- + Destructor + --------------------------------------------------------------------*/ +ArchicadGroupDBaseEngine::~ArchicadGroupDBaseEngine() { +} //ArchicadGroupDBaseEngine::~ArchicadGroupDBaseEngine + + +/*-------------------------------------------------------------------- + Get an object by ID + + objID: The object index + tableID: Optional table ID (defaults to the floor plan) + documentID: Optional document ID (when the object is bound to a specific document) + + return: The requested object (nullptr on failure) + --------------------------------------------------------------------*/ +std::unique_ptr ArchicadGroupDBaseEngine::getObject(const BIMRecordID& objID, std::optional tableID, + std::optional documentID) const { + if (!validateCache() || (tableID && (tableID != Group::propertyGroupTableID))) + return nullptr; + if (auto found = m_cache->find(objID); found != m_cache->end()) + return std::make_unique(*found->second); + return nullptr; +} //ArchicadGroupDBaseEngine::getObject + + +/*-------------------------------------------------------------------- + Get an object in a transportable form, e.g. packaged for serialisation + + index: The object index + tableID: Optional table ID (defaults to the floor plan) + documentID: Optional document ID (when the object is bound to a specific document) + + return: The requested wrapped cargo (nullptr on failure) + --------------------------------------------------------------------*/ +active::serialise::Cargo::Unique ArchicadGroupDBaseEngine::getObjectCargo(const BIMRecordID& ID, std::optional tableID, + std::optional documentID) const { + return nullptr; //TODO: Implement +} //ArchicadGroupDBaseEngine::getObject + + +/*-------------------------------------------------------------------- + Get all objects + + tableID: Optional table ID (defaults to the floor plan) + documentID: Optional document ID (filter for this document only - nullopt = all objects) + + return: The requested objects (nullptr on failure) + --------------------------------------------------------------------*/ +active::container::Vector ArchicadGroupDBaseEngine::getObjects(std::optional tableID, + std::optional documentID) const { + return {}; //TODO: Implement +} //ArchicadGroupDBaseEngine::getObjects + + +/*-------------------------------------------------------------------- + Get all objects + + filter: The object filter + tableID: Optional table ID (defaults to the floor plan) + documentID: Optional document ID (filter for this document only - nullopt = all objects) + + return: The requested objects (nullptr on failure) + --------------------------------------------------------------------*/ +active::container::Vector ArchicadGroupDBaseEngine::getObjects(const Filter& filter, std::optional tableID, + std::optional documentID) const { + return {}; //TODO: Implement +} //ArchicadGroupDBaseEngine::getObjects + + +/*-------------------------------------------------------------------- + Write an object to the database + + object: The object to write + objID: The object ID + objDocID: The object document-specific ID (unique within a specific document - nullopt if not document-bound) + tableID: Optional table ID (defaults to the floor plan) + documentID: Optional document ID (when the object is bound to a specific document) + --------------------------------------------------------------------*/ +void ArchicadGroupDBaseEngine::write(const Group& object, const BIMRecordID& objID, std::optional objDocID, + std::optional tableID, std::optional documentID) const { + //TODO: Implement +} //ArchicadGroupDBaseEngine::write + + +/*-------------------------------------------------------------------- + Erase an object by index + + objID: The object ID + tableID: Optional table ID (defaults to the floor plan) + documentID: Optional document ID (when the object is bound to a specific document) + + return: True if the object was successfully erased + --------------------------------------------------------------------*/ +void ArchicadGroupDBaseEngine::erase(const BIMRecordID& ID, std::optional tableID, + std::optional documentID) const { + //TODO: Implement +} //ArchicadGroupDBaseEngine::erase + + +/*-------------------------------------------------------------------- + Erase all objects + + tableID: Optional table ID (defaults to the floor plan) + documentID: Optional document ID (filter for this document only - nullopt = all objects) + --------------------------------------------------------------------*/ +void ArchicadGroupDBaseEngine::erase(std::optional tableID, std::optional documentID) const { + //TODO: Implement +} //ArchicadGroupDBaseEngine::erase + + +/*-------------------------------------------------------------------- + Get the database outline + + return: The database outline + --------------------------------------------------------------------*/ +ArchicadGroupDBaseEngine::Outline ArchicadGroupDBaseEngine::getOutline() const { + return {}; //TODO: Implement +} //ArchicadGroupDBaseEngine::getOutline + + +/*-------------------------------------------------------------------- + Handle a project event + + event: The project event + + return: True if the event should be closed + --------------------------------------------------------------------*/ +bool ArchicadGroupDBaseEngine::handle(const event::ProjectEvent& event) { + using enum ProjectEvent::Type; + switch (event.getType()) { + case newDocument: case newAndReset: case open: case close: case quit: + //Reset the group cache on any event that changes the active project + m_cache.reset(); + break; + default: + break; + } + return false; +} //ArchicadGroupDBaseEngine::handle + + +/*-------------------------------------------------------------------- + Ensure the cache is current + + return: True if the cache contains valid te groups + --------------------------------------------------------------------*/ +bool ArchicadGroupDBaseEngine::validateCache() const { + if (!m_cache) + m_cache = std::make_unique(); + return !m_cache->empty(); +} //ArchicadGroupDBaseEngine::validateCache + +#endif //ARCHICAD diff --git a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadGroupDBaseEngine.h b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadGroupDBaseEngine.h new file mode 100644 index 0000000..e6f8f1a --- /dev/null +++ b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadGroupDBaseEngine.h @@ -0,0 +1,136 @@ +#ifndef SPECKLE_DATABASE_ARCHICAD_GROUP_DBASE_ENGINE +#define SPECKLE_DATABASE_ARCHICAD_GROUP_DBASE_ENGINE + +#include "Active/Database/Storage/DBaseEngine.h" +#include "Active/Serialise/UnboxedTransport.h" +#include "Speckle/Database/Storage/ArchicadDBase/ArchicadDBaseCore.h" +#include "Speckle/Database/Identity/BIMLink.h" +#include "Speckle/Record/Property/Group.h" +#include "Speckle/Utility/Guid.h" +#include "Speckle/Utility/String.h" + +#include +#include + +namespace speckle::database { + + /*! + A database engine to read/write property groups in an Archicad project database (local file or cloud-based) + + Property groups can be attached to property templates to support collections of types linked to a specific role + */ + class ArchicadGroupDBaseEngine : public ArchicadDBaseCore, + public active::database::DBaseEngine { + public: + + // MARK: - Types + + using base = active::database::DBaseEngine; + using Group = record::property::Group; + using Filter = base::Filter; + using Outline = base::Outline; + + // MARK: - Constructors + + /*! + Constructor + @param id The document storage identifier + @param schema The document storage schema + */ + ArchicadGroupDBaseEngine(const active::utility::NameID& id, ArchicadDBaseSchema&& schema); + ArchicadGroupDBaseEngine(const ArchicadGroupDBaseEngine&) = delete; + /*! + Destructor + */ + ~ArchicadGroupDBaseEngine(); + + // MARK: - Functions (const) + + /*! + Get an object by ID + @param objID The object ID + @param tableID Optional table ID (default selected based on record type) + @param documentID Optional document ID (when the object is bound to a specific document) + @return The requested object (nullptr on failure) + */ + std::unique_ptr getObject(const BIMRecordID& objID, std::optional tableID = std::nullopt, std::optional documentID = std::nullopt) const override; + /*! + Get an object in a transportable form, e.g. packaged for serialisation + @param objID The object ID + @param tableID Optional table ID (default selected based on record type) + @param documentID Optional document ID (when the object is bound to a specific document) + @return: The requested wrapped cargo (nullptr on failure) + */ + active::serialise::Cargo::Unique getObjectCargo(const BIMRecordID& objID, std::optional tableID = std::nullopt, std::optional documentID = std::nullopt) const override; + /*! + Get all objects + @param tableID Optional table ID (default selected based on record type) + @param documentID Optional document ID (filter for this document only - nullopt = all objects) + @return The requested objects (nullptr on failure) + */ + active::container::Vector getObjects(std::optional tableID = std::nullopt, std::optional documentID = std::nullopt) const override; + /*! + Get a filtered list of objects + @param filter The object filter + @param tableID Optional table ID (default selected based on record type) + @param documentID Optional document ID (filter for this document only - nullopt = all objects) + @return The filtered objects (nullptr on failure) + */ + active::container::Vector getObjects(const Filter& filter, std::optional tableID = std::nullopt, + std::optional documentID = std::nullopt) const override; + /*! + Write an object to the database + @param object The object to write + @param objID The object ID + @param objDocID The object document-specific ID (unique within a specific document - nullopt if not document-bound) + @param tableID Optional table ID (default selected based on record type) + @param documentID Optional document ID (when the object is bound to a specific document) + */ + void write(const Group& object, const BIMRecordID& objID, std::optional objDocID = std::nullopt, + std::optional tableID = std::nullopt, std::optional documentID = std::nullopt) const override; + /*! + Erase an object by index + @param ID The object ID + @param tableID Optional table ID (default selected based on record type) + @param documentID Optional document ID (when the object is bound to a specific document) + @throw Exception thrown on SQL error + */ + void erase(const BIMRecordID& ID, std::optional tableID = std::nullopt, + std::optional documentID = std::nullopt) const override; + /*! + Erase all objects + @param tableID Optional table ID (default selected based on record type) + @param documentID Optional document ID (when the object is bound to a specific document) + @throw Exception thrown on SQL error + */ + void erase(std::optional tableID = std::nullopt, std::optional documentID = std::nullopt) const override; + /*! + Get the database outline + @return The database outline + */ + Outline getOutline() const override; + + // MARK: - Functions (mutating) + + /*! + Handle a project event + @param event The project event + @return True if the event should be closed + */ + bool handle(const event::ProjectEvent& event) override; + + private: + /*! + Ensure the cache is current + @return True if the cache contains valid te templates + */ + bool validateCache() const; + + //Cached templates + class Cache; + mutable std::unique_ptr m_cache; + }; + +} + +#endif //SPECKLE_DATABASE_ARCHICAD_GROUP_DBASE_ENGINE diff --git a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadPropertyDBaseEngine.cpp b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadPropertyDBaseEngine.cpp index 06ff07a..ab27992 100644 --- a/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadPropertyDBaseEngine.cpp +++ b/SpeckleLib/Speckle/Database/Storage/ArchicadDBase/Property/ArchicadPropertyDBaseEngine.cpp @@ -118,7 +118,7 @@ std::vector> ArchicadPropertyDBaseEngine::findTemplate --------------------------------------------------------------------*/ std::unique_ptr