Writer.cpp
Go to the documentation of this file.
1#include "Writer.h"
2
3#include <SimoxUtility/algorithm/get_map_keys_values.h>
4
7
10#include <RobotAPI/libraries/ArmarXObjects/aron/ObjectID.aron.generated.h>
16#include <RobotAPI/libraries/armem_objects/aron/ObjectClass.aron.generated.h>
17#include <RobotAPI/libraries/armem_objects/aron/ObjectInstance.aron.generated.h>
19#include <RobotAPI/libraries/armem_robot_state/aron/Robot.aron.generated.h>
20#include <RobotAPI/libraries/armem_robot_state/aron/RobotDescription.aron.generated.h>
23
24#include "utils.h"
25
27{
28 void
30 {
31 ARMARX_DEBUG << "Writer: registerPropertyDefinitions";
32
33 const std::string prefix = propertyPrefix;
34
35 def->optional(properties.memoryName, prefix + "MemoryName");
36
37 def->optional(properties.coreInstanceSegmentName,
38 prefix + "CoreSegment",
39 "Name of the memory core segment to use for object instances.");
40 def->optional(properties.coreClassSegmentName,
41 prefix + "CoreSegment",
42 "Name of the memory core segment to use for object classes.");
43
44 ARMARX_IMPORTANT << "Writer: add property '" << prefix << "ProviderName'";
45 def->required(
46 properties.providerName, prefix + "write.ProviderName", "Name of this provider");
47 }
48
49 void
51 {
52 // Wait for the memory to become available and add it as dependency.
53 ARMARX_IMPORTANT << "Writer: Waiting for memory '" << properties.memoryName << "' ...";
54 try
55 {
56 memoryWriter = memoryNameSystem.useWriter(properties.memoryName);
57 memoryReader = memoryNameSystem.useReader(properties.memoryName);
58 ARMARX_IMPORTANT << "Writer: Connected to memory '" << properties.memoryName << "'";
59 }
61 {
62 ARMARX_ERROR << e.what();
63 return;
64 }
65
66 const auto resultCoreClassSegment =
67 memoryWriter.addSegment(properties.coreClassSegmentName, properties.providerName);
68
69 const auto resultCoreInstanceSegmentName =
70 memoryWriter.addSegment(properties.coreInstanceSegmentName, properties.providerName);
71 }
72
73
74 std::optional<armem::MemoryID>
75 Writer::storeOrGetClass(const ArticulatedObject& obj) const
76 {
78
79 // key: name of object: RobotDescription::name
80 const std::unordered_map<std::string, MemoryID> knownObjects = queryDescriptions(Time::Now());
81 ARMARX_VERBOSE << "Known articulated objects " << simox::alg::get_keys(knownObjects);
82
83 const auto objectId = knownObjects.find(obj.description.name);
84
85 // check if exists
86 if (objectId != knownObjects.end())
87 {
88 return objectId->second;
89 }
90
91 ARMARX_INFO << simox::alg::get_keys(knownObjects);
92
93 throw LocalException("articulated object class " + obj.description.name + " not found");
94
95 // otherwise create
96 if (properties.allowClassCreation)
97 {
98 return storeClass(obj);
99 }
100
101 return std::nullopt;
102 }
103
104 std::optional<armem::MemoryID>
106 {
107 std::lock_guard g{memoryWriterMutex};
108
109 ARMARX_DEBUG << "Trying to create core segment + provider segment";
110
111 // TODO(fabian.reister): variable provider segment
112 const auto result =
113 memoryWriter.addSegment(properties.coreClassSegmentName, properties.providerName);
114
115 if (not result.success)
116 {
117 ARMARX_ERROR << "Creating core segment failed. Reason: " << result.errorMessage;
118 return std::nullopt;
119 }
120
121 const auto& timestamp = obj.timestamp;
122
123 const auto providerId = armem::MemoryID(result.segmentID);
124 const auto entityID =
125 providerId.withEntityName(obj.description.name).withTimestamp(timestamp);
126
127 armem::EntityUpdate update;
128 update.entityID = entityID;
129
130 arondto::RobotDescription aronArticulatedObjectDescription;
131 toAron(aronArticulatedObjectDescription, obj.description);
132
133 update.instancesData = {aronArticulatedObjectDescription.toAron()};
134 update.referencedTime = timestamp;
135
136 ARMARX_DEBUG << "Committing " << update << " at time " << timestamp;
137 armem::EntityUpdateResult updateResult = memoryWriter.commit(update);
138
139 ARMARX_DEBUG << updateResult;
140
141 if (not updateResult.success)
142 {
143 ARMARX_ERROR << updateResult.errorMessage;
144 return std::nullopt;
145 }
146
147 return updateResult.snapshotID;
148 }
149
150 std::string
152 {
153 return properties.providerName;
154 }
155
156 void
157 Writer::setProviderName(const std::string& providerName)
158 {
159 this->properties.providerName = providerName;
160 }
161
162 bool
163 Writer::storeInstance(const ArticulatedObject& obj, const bool isStatic) const
164 {
165 std::lock_guard g{memoryWriterMutex};
166
167 const auto& timestamp = obj.timestamp;
168
169 ARMARX_CHECK(not obj.instance.empty()) << "An object instance name must be provided!";
170 const std::string entityName = obj.description.name + "/" + obj.instance;
171
172 ARMARX_DEBUG << "Storing articulated object instance '" << entityName << "' (provider '"
173 << properties.providerName << "')";
174
175 const auto providerId = armem::MemoryID()
176 .withMemoryName(properties.memoryName)
177 .withCoreSegmentName(properties.coreInstanceSegmentName)
178 .withProviderSegmentName(properties.providerName);
179
180 armem::EntityUpdate update;
181 update.entityID = providerId.withEntityName(entityName);
182 // .withTimestamp(timestamp); // You only need to specify the entity ID, not the snapshot ID
183
184 // arondto::Robot aronArticulatedObject;
185 // robot::toAron(aronArticulatedObject, obj);
186
187 arondto::ObjectInstance objectInstance;
188 toAron(objectInstance, obj.config);
189
190 const std::optional<armem::MemoryID> classId = storeOrGetClass(obj);
191
192 if (not classId)
193 {
194 ARMARX_WARNING << "Could not get class for object " << obj.description.name;
195 return false;
196 }
197
198 // install memory link
199 toAron(objectInstance.classID, *classId);
200
201 // set object instance id
202 const MemoryID memoryInstanceId = classId->withEntityName(entityName);
203
205 id.setEntityID(memoryInstanceId.getEntityID());
206
207 armarx::ObjectID objectId(id.entityName);
208 ARMARX_DEBUG << "Object ID: " << objectId;
209
211 << "An object instance name must be provided!";
212
213 armarx::arondto::ObjectID cs;
214 cs.className = objectId.className();
215 cs.instanceName = objectId.instanceName();
216 cs.dataset = objectId.dataset();
217
218 objectInstance.pose.objectID = cs;
219 objectInstance.pose.providerName = properties.providerName;
220 objectInstance.pose.attachmentValid = false;
221
222 objectInstance.pose.isStatic = isStatic;
223 objectInstance.pose.confidence = 1.0;
224
225 update.instancesData = {objectInstance.toAron()};
226 update.referencedTime = timestamp;
227
228 ARMARX_DEBUG << "Committing " << update << " at time " << timestamp;
229 armem::EntityUpdateResult updateResult = memoryWriter.commit(update);
230
231 ARMARX_DEBUG << updateResult;
232
233 if (not updateResult.success)
234 {
235 ARMARX_WARNING << updateResult.errorMessage;
236 }
237
238 return updateResult.success;
239 }
240
241 bool
242 Writer::store(const ArticulatedObject& obj, const bool isStatic) const
243 {
244 const std::optional<armem::MemoryID> classId = storeOrGetClass(obj);
245
246 if (not classId)
247 {
248 ARMARX_WARNING << "Could not get class id for object " << obj.description.name << "! "
249 << "Known classes are " << simox::alg::get_keys(queryDescriptions(Time::Now()));
250 return false;
251 }
252
253 return storeInstance(obj, isStatic);
254 }
255
256 // TODO this is a duplicate
257 std::optional<robot_state::description::RobotDescription>
258 Writer::getRobotDescription(const armarx::armem::wm::Memory& memory) const
259 {
260 // clang-format off
261 const armem::wm::ProviderSegment& providerSegment = memory
262 .getCoreSegment(properties.coreClassSegmentName)
263 .getProviderSegment(properties.providerName); // TODO(fabian.reister): all
264 // clang-format on
265
266 if (const armem::wm::EntityInstance* instance = findFirstInstance(providerSegment))
267 {
268 return convertRobotDescription(*instance);
269 }
270 else
271 {
272 ARMARX_WARNING << "No entity snapshots found";
273 return std::nullopt;
274 }
275 }
276
277 std::unordered_map<std::string, armem::MemoryID>
278 Writer::getRobotDescriptions(const armarx::armem::wm::Memory& memory) const
279 {
280 const armem::wm::CoreSegment& coreSegment =
281 memory.getCoreSegment(properties.coreClassSegmentName);
282
283 std::unordered_map<std::string, armem::MemoryID> descriptions;
284 coreSegment.forEachEntity(
285 [&descriptions](const wm::Entity& entity)
286 {
287 if (entity.empty())
288 {
289 ARMARX_WARNING << "No entity found";
290 return true;
291 }
292
293 const armem::wm::EntitySnapshot& sn = entity.getFirstSnapshot();
294 if (const auto robotDescription = convertRobotDescription(sn.getInstance(0)))
295 {
296 const armem::MemoryID snapshotID(sn.id());
297 descriptions.insert({robotDescription->name, snapshotID});
298 }
299 return true;
300 });
301
302 return descriptions;
303 }
304
305 std::unordered_map<std::string, armem::MemoryID>
306 Writer::queryDescriptions(const armem::Time& timestamp) const
307 {
308 // Query all entities from provider.
309 armem::client::query::Builder qb;
310
311 // clang-format off
312 qb
313 .coreSegments().withName(properties.coreClassSegmentName)
315 .entities().all()
317 // clang-format on
318
319 const armem::client::QueryResult qResult = memoryReader.query(qb.buildQueryInput());
320
321 ARMARX_DEBUG << "Lookup result in reader: " << qResult;
322
323 if (not qResult.success) /* c++20 [[unlikely]] */
324 {
325 return {};
326 }
327
328 return getRobotDescriptions(qResult.memory);
329 }
330
331} // namespace armarx::armem::articulated_object
std::string timestamp()
#define ARMARX_CHECK_NOT_EMPTY(c)
A known object ID of the form "Dataset/ClassName" or "Dataset/ClassName/InstanceName".
Definition ObjectID.h:11
std::string className() const
Definition ObjectID.h:30
std::string instanceName() const
Definition ObjectID.h:36
std::string dataset() const
Definition ObjectID.h:24
MemoryID withProviderSegmentName(const std::string &name) const
Definition MemoryID.cpp:417
MemoryID withCoreSegmentName(const std::string &name) const
Definition MemoryID.cpp:409
MemoryID withMemoryName(const std::string &name) const
Definition MemoryID.cpp:401
MemoryID withEntityName(const std::string &name) const
Definition MemoryID.cpp:425
void setEntityID(const MemoryID &id)
Definition MemoryID.cpp:380
MemoryID getEntityID() const
Definition MemoryID.cpp:310
void connect(armem::client::MemoryNameSystem &memoryNameSystem)
Definition Writer.cpp:50
bool store(const ArticulatedObject &obj, bool isStatic) const override
Definition Writer.cpp:242
bool storeInstance(const ArticulatedObject &obj, bool isStatic) const
Definition Writer.cpp:163
void setProviderName(const std::string &providerName)
Definition Writer.cpp:157
std::optional< armem::MemoryID > storeClass(const ArticulatedObject &obj) const
Definition Writer.cpp:105
void registerPropertyDefinitions(armarx::PropertyDefinitionsPtr &def)
Definition Writer.cpp:29
EntitySnapshotT & getFirstSnapshot()
Return the snapshot with the least recent timestamp.
Definition EntityBase.h:262
EntityInstanceT & getInstance(int index)
Get the given instance.
CoreSegmentT & getCoreSegment(const std::string &name)
Definition MemoryBase.h:134
The memory name system (MNS) client.
Writer useWriter(const MemoryID &memoryID)
Use a memory server and get a writer for it.
Reader useReader(const MemoryID &memoryID)
Use a memory server and get a reader for it.
CoreSegmentSelector & coreSegments()
Start specifying core segments.
Definition Builder.cpp:42
CoreSegmentSelector & withName(const std::string &name) override
ProviderSegmentSelector & providerSegments()
Start specifying provider segments.
SnapshotSelector & snapshots()
Start specifying entity snapshots.
Definition selectors.cpp:92
EntitySelector & entities()
Start specifying entities.
ProviderSegmentSelector & all() override
SnapshotSelector & beforeTime(Time timestamp, long maxEntries=1)
Definition selectors.cpp:81
Indicates that a query to the Memory Name System failed.
Definition mns.h:25
Client-side working memory core segment.
Client-side working entity instance.
Client-side working memory.
Client-side working memory provider segment.
static DateTime Now()
Definition DateTime.cpp:51
Brief description of class memory.
Definition memory.h:39
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
armarx::armem::robot_state::Robot ArticulatedObject
Definition types.h:140
std::optional< robot_state::description::RobotDescription > convertRobotDescription(const armem::wm::EntityInstance &instance)
Definition utils.cpp:11
const ContainerT::EntityInstanceT * findFirstInstance(const ContainerT &container)
Definition operations.h:37
armarx::core::time::DateTime Time
void toAron(arondto::MemoryID &dto, const MemoryID &bo)
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
Result of an EntityUpdate.
Definition Commit.h:75
An update of an entity for a specific point in time.
Definition Commit.h:26
#define ARMARX_TRACE
Definition trace.h:77