diff --git a/source/RobotAPI/libraries/armem/error/ArMemError.cpp b/source/RobotAPI/libraries/armem/error/ArMemError.cpp index de7edf68c108b10299ef466688297af7d9b38ddc..b6cac6b7c685af1273c670eca1dcb2f25ce644e0 100644 --- a/source/RobotAPI/libraries/armem/error/ArMemError.cpp +++ b/source/RobotAPI/libraries/armem/error/ArMemError.cpp @@ -14,6 +14,27 @@ namespace armarx::armem::error } + InvalidArgument::InvalidArgument(const std::string& argument, const std::string& function, const std::string& message) : + ArMemError(makeMsg(argument, function, message)) + { + } + + std::string InvalidArgument::makeMsg(const std::string& argument, const std::string& function, const std::string& message) + { + std::stringstream ss; + ss << "Invalid value for argument '" << argument << "' in function " << function << "()"; + if (message.empty()) + { + ss << "."; + } + else + { + ss << ":\n" << message; + } + return ss.str(); + } + + StorageNameMismatch::StorageNameMismatch(const std::string& expectedName, const std::string& ownTerm, const std::string& storageName) : ArMemError(makeMsg(expectedName, ownTerm, storageName)) @@ -59,6 +80,22 @@ namespace armarx::armem::error + MissingData::MissingData(const std::string& missingTerm, const std::string& missingName, + const std::string& ownTerm, const std::string& ownName) : + ArMemError(makeMsg(missingTerm, missingName, ownTerm, ownName)) + { + } + + std::string MissingData::makeMsg(const std::string& missingTerm, const std::string& missingName, + const std::string& ownTerm, const std::string& ownName) + { + std::stringstream ss; + ss << "No " << missingTerm << " data at '" << missingName << "' " + << "in " << ownTerm << " '" << ownName << "'."; + return ss.str(); + } + + ParseIntegerError::ParseIntegerError(std::string string, std::string semanticName) : ArMemError(makeMsg(string, semanticName)) { @@ -121,4 +158,5 @@ namespace armarx::armem::error return ss.str(); } + } diff --git a/source/RobotAPI/libraries/armem/error/ArMemError.h b/source/RobotAPI/libraries/armem/error/ArMemError.h index 5dd1a9118fb11a1b66405d15fd41b544955c2bdb..a85d26ce5000e7780208f2d7b1ae3afa75f9fad4 100644 --- a/source/RobotAPI/libraries/armem/error/ArMemError.h +++ b/source/RobotAPI/libraries/armem/error/ArMemError.h @@ -21,6 +21,23 @@ namespace armarx::armem::error }; + /** + * @brief Indicates that an argument was invalid. + */ + class InvalidArgument : public ArMemError + { + public: + + InvalidArgument(const std::string& argument, const std::string& function, + const std::string& message); + + static std::string makeMsg(const std::string& argument, const std::string& function, + const std::string& message); + + }; + + + /** * @brief Indicates that a name in a given ID does not match a storage's own name. */ @@ -68,7 +85,22 @@ namespace armarx::armem::error /** - * @brief Indicates that a storage did not have an entry under a given name. + * @brief Indicates that a container did have an entry, but the entry's data was + * null when trying to access it. + */ + class MissingData : public ArMemError + { + public: + MissingData(const std::string& missingTerm, const std::string& missingName, + const std::string& ownTerm, const std::string& ownName); + + static std::string makeMsg(const std::string& missingTerm, const std::string& missingName, + const std::string& ownTerm, const std::string& ownName); + }; + + + /** + * @brief Indicates that a string could not be parsed as integer. */ class ParseIntegerError : public ArMemError { diff --git a/source/RobotAPI/libraries/armem/memory/Entity.cpp b/source/RobotAPI/libraries/armem/memory/Entity.cpp index 4c56024a850bc1d38063a6061e2749c7001f1d29..2e563ee2e553bfce514a030ae18b55c359e48974 100644 --- a/source/RobotAPI/libraries/armem/memory/Entity.cpp +++ b/source/RobotAPI/libraries/armem/memory/Entity.cpp @@ -50,6 +50,12 @@ namespace armarx::armem return history.count(time) > 0; } + bool Entity::hasSnapshotData(Time time) const + { + auto it = history.find(time); + return it != history.end() && (it->second != nullptr); + } + EntitySnapshot& Entity::getSnapshot(Time time) { return const_cast<EntitySnapshot&>(const_cast<const Entity*>(this)->getSnapshot(time)); @@ -60,8 +66,15 @@ namespace armarx::armem auto it = history.find(time); if (it != history.end()) { - ARMARX_CHECK_NOT_NULL(it->second); - return *it->second; + if (it->second) + { + return *it->second; + } + else + { + throw error::MissingData("entity snapshot", toDateTimeMilliSeconds(time), + getLevelName(), this->name); + } } else { @@ -89,8 +102,15 @@ namespace armarx::armem const EntitySnapshot& Entity::getLatestSnapshot() const { auto& latest = getLatestItem(); - ARMARX_CHECK_NOT_NULL(latest.second); - return *latest.second; + if (latest.second) + { + return *latest.second; + } + else + { + throw error::MissingData("entity snapshot", toDateTimeMilliSeconds(latest.first), + getLevelName(), this->name); + } } Time Entity::getLatestTimestamp() const @@ -111,6 +131,10 @@ namespace armarx::armem EntitySnapshot& Entity::addSnapshot(EntitySnapshotPtr&& snapshot) { + if (!snapshot) + { + throw error::InvalidArgument("snapshot", __FUNCTION__, "Passed snapshot pointer is null."); + } auto it = history.emplace(snapshot->time, std::move(snapshot)).first; return *it->second; } @@ -140,6 +164,12 @@ namespace armarx::armem } + EntitySnapshotPtr& Entity::addSnapshotEntry(Time time) + { + return history.emplace(time, nullptr).first->second; + } + + void Entity::setMaxHistorySize(long maxSize) { this->maxHistorySize = maxSize; diff --git a/source/RobotAPI/libraries/armem/memory/Entity.h b/source/RobotAPI/libraries/armem/memory/Entity.h index 742b6709f3f1f7293e76f1f0bb847224f995af68..a03400cb9881b228f906612cb271abdeebb75511 100644 --- a/source/RobotAPI/libraries/armem/memory/Entity.h +++ b/source/RobotAPI/libraries/armem/memory/Entity.h @@ -33,8 +33,36 @@ namespace armarx::armem Entity& operator=(const Entity& other); + /** + * @brief Indicates whether a history entry for the given time exists. + */ bool hasSnapshot(Time time) const; + /** + * @brief Indicates whether a history entry for the given time exists + * and its data is not null. + */ + bool hasSnapshotData(Time time) const; + + + /** + * @brief Get the latest timestamp. + * @throw `armem::error::EntityHistoryEmpty` If the history is empty. + */ + Time getLatestTimestamp() const; + /** + * @brief Get all timestamps in the history. + */ + std::vector<Time> getTimestamps() const; + + /** + * @brief Get a snapshot. + * @param time The time. + * @return The snapshot, if it exists. + * + * @throws `armem::error::MissingEntry` If there is no such entry. + * @throws `armem::error::MissingData` If the entry has no data. + */ EntitySnapshot& getSnapshot(Time time); const EntitySnapshot& getSnapshot(Time time) const; @@ -45,21 +73,11 @@ namespace armarx::armem * @brief Return the snapshot with the most recent timestamp. * @return The latest snapshot. * @throw `armem::error::EntityHistoryEmpty` If the history is empty. + * @throw `armem::error::MissingData` If the latest snapshot has no data. */ EntitySnapshot& getLatestSnapshot(); const EntitySnapshot& getLatestSnapshot() const; - /** - * @brief Get the latest timestamp. - * @throw `armem::error::EntityHistoryEmpty` If the history is empty. - */ - Time getLatestTimestamp() const; - - /** - * @brief Get all timestamps in the history. - */ - std::vector<Time> getTimestamps() const; - /** * @brief Add the given update to this entity's history. @@ -68,10 +86,22 @@ namespace armarx::armem */ MemoryID update(const InternalEntityUpdate& update); - /// Add a single snapshot. + /** + * @brief Add a single snapshot entry without data. + * @param time The time. + * @return The (empty) snapshot pointer. + */ + EntitySnapshotPtr& addSnapshotEntry(Time time); + + /** + * @brief Add a single snapshot with data. + * @param snapshot The snapshot. + * @return The stored snapshot. + */ EntitySnapshot& addSnapshot(const EntitySnapshot& snapshot); EntitySnapshot& addSnapshot(EntitySnapshotPtr&& snapshot); + /// Get a cleared copy of `*this` (with empty history). EntityPtr getEmptyCopy() const;