ExampleMemory.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package RobotAPI::ArmarXObjects::ExampleMemory
17 * @author Rainer Kartmann ( rainer dot kartmann at kit dot edu )
18 * @date 2020
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
23#include "ExampleMemory.h"
24
25#include <atomic>
26
27#include <SimoxUtility/algorithm/string.h>
28
32
33#include <RobotAPI/components/armem/server/ExampleMemory/aron/ExampleData.aron.generated.h>
34#include <RobotAPI/components/armem/server/ExampleMemory/aron/PythonExampleData.aron.generated.h>
40
42
44
45namespace armarx
46{
49 {
52
53 defs->topic(debugObserver);
54
55 setMemoryName("Example");
56
57 p.core._defaultSegmentsStr = simox::alg::join(p.core.defaultCoreSegments, ", ");
58 defs->optional(p.core._defaultSegmentsStr,
59 "core.DefaultSegments",
60 "Core segments to add on start up (just as example).");
61 defs->optional(
62 p.core.addOnUsage,
63 "core.AddOnUsage",
64 "If enabled, core segments are added when required by a new provider segment."
65 "This will usually be off for most memory servers.");
66
67 defs->optional(p.enableRemoteGui,
68 "p.enableRemoteGui",
69 "If true, the remote gui is enabled.");
70 defs->optional(p.showMemoryContent,
71 "p.showMemoryContent",
72 "If true, memory content is shown in remote gui (requires enableRemoteGui=true). "
73 "Can be very slow for high-frequency updates and may cause nullptr errors!");
74 defs->optional(p.showClientStats,
75 "p.showClientStats",
76 "If true, client statistics are shown in the remote gui (requires enableRemoteGui=true).");
77 defs->optional(p.publishToDebugObserver,
78 "p.publishToDebugObserver",
79 "If true, client statistics are published to DebugObserver. "
80 "Channel format: '<ServerName> | Client<N>' with datafields: commits, queries, address, port, pid (if available).");
81 return defs;
82 }
83
84 std::string
86 {
87 return "ExampleMemory";
88 }
89
90 void
92 {
93 // Usually, the memory server will specify a number of core segments with a specific aron type.
94 workingMemory().addCoreSegment("ExampleData", armem::example::ExampleData::ToAronType());
95 workingMemory().addCoreSegment("LinkedData", armem::example::LinkedData::ToAronType());
96
97 workingMemory().addCoreSegment("PythonExampleData",
98 armem::example::PythonExampleData::ToAronType());
99
100
101 // We support the "Latest" prediction engine for the entire memory.
102 workingMemory().addPredictor(armem::PredictionEngine{.engineID = "Latest"},
103 [this](const armem::PredictionRequest& request)
104 { return this->predictLatest(request); });
105
106 // For illustration purposes, we add more segments (without types).
107 bool trim = true;
108 p.core.defaultCoreSegments = simox::alg::split(p.core._defaultSegmentsStr, ",", trim);
109 p.core._defaultSegmentsStr.clear();
110
111 for (const std::string& name : p.core.defaultCoreSegments)
112 {
113 auto& c = workingMemory().addCoreSegment(name);
114 c.setMaxHistorySize(100);
115 }
116 }
117
118 void
120 {
121 if (p.enableRemoteGui)
122 {
123 tab.lastStatsUpdate = armem::Time::Now();
126 }
127 }
128
129 void
131 {
132 // Log client statistics before shutdown
133 logClientStatistics();
134 }
135
136 void
140
141 // WRITING
142
143 armem::data::AddSegmentsResult
144 ExampleMemory::addSegments(const armem::data::AddSegmentsInput& input, const Ice::Current&)
145 {
146 // This function is overloaded to trigger the remote gui rebuild.
147 armem::data::AddSegmentsResult result =
148 ReadWritePluginUser::addSegments(input, p.core.addOnUsage);
149 tab.rebuild = true;
150 return result;
151 }
152
153 armem::data::CommitResult
154 ExampleMemory::commit(const armem::data::Commit& commit, const Ice::Current& current)
155 {
156 // Track client statistics
157 recordCommit(current);
158
159 // This function is overloaded to trigger the remote gui rebuild.
160 armem::data::CommitResult result = ReadWritePluginUser::commit(commit);
161 tab.rebuild = true;
162 return result;
163 }
164
165 armem::query::data::Result
166 ExampleMemory::query(const armem::query::data::Input& input, const Ice::Current& current)
167 {
168 // Track client statistics (GUI updates only via RemoteGui_update timer)
169 recordQuery(current);
170
171 // Forward to base implementation
172 return ReadWritePluginUser::query(input);
173 }
174
175 // READING
176
177 // Inherited from Plugin
178
179
180 // ACTIONS
181 armem::actions::GetActionsOutputSeq
182 ExampleMemory::getActions(const armem::actions::GetActionsInputSeq& input)
183 {
184 using namespace armem::actions;
185 Action greeting{"hi", "Say hello to " + input[0].id.entityName};
186 Action failure{"fail", "Fail dramatically"};
187 Action nothing{"null", "Do nothing, but deeply nested"};
188
189 SubMenu one{"one", "One", {nothing}};
190 SubMenu two{"two", "Two", {one}};
191 SubMenu three{"three", "Three", {two}};
192 SubMenu four{"four", "Four", {three}};
193
194 Menu menu{greeting,
195 failure,
196 four,
197 SubMenu{"mut", "Mutate", {Action{"copy", "Copy latest instance"}}}};
198
199 return {{menu.toIce()}};
200 }
201
202 armem::actions::ExecuteActionOutputSeq
203 ExampleMemory::executeActions(const armem::actions::ExecuteActionInputSeq& input)
204 {
205 using namespace armem::actions;
206
207 ExecuteActionOutputSeq output;
208 for (const auto& [id, path] : input)
209 {
211 if (path == ActionPath{"hi"})
212 {
213 ARMARX_INFO << "Hello, " << memoryID.str() << "!";
214 output.emplace_back(true, "");
215 }
216 else if (path == ActionPath{"fail"})
217 {
218 ARMARX_WARNING << "Alas, I am gravely wounded!";
219 output.emplace_back(false, "Why would you do that to him?");
220 }
221 else if (not path.empty() and path.front() == "four" and path.back() == "null")
222 {
223 // Do nothing.
224 ARMARX_INFO << "Nested action (path: " << path << ")";
225 output.emplace_back(true, "");
226 }
227 else if (path == ActionPath{"mut", "copy"})
228 {
229 auto* instance = workingMemory().findLatestInstance(memoryID);
230 if (instance != nullptr)
231 {
232 armem::EntityUpdate update;
233 armem::MemoryID newID =
234 memoryID.getCoreSegmentID()
236 .withEntityName(memoryID.entityName);
237 update.entityID = newID;
238 update.referencedTime = armem::Time::Now();
239 update.instancesData = {instance->data()};
240
241 armem::Commit newCommit;
242 newCommit.add(update);
244
245 tab.rebuild = true;
246 ARMARX_INFO << "Duplicated " << memoryID;
247 output.emplace_back(true, "");
248 }
249 else
250 {
251 output.emplace_back(false, "Couldn't duplicate " + memoryID.str());
252 }
253 }
254 }
255
256 return output;
257 }
258
259 // PREDICTING
261 ExampleMemory::predictLatest(const armem::PredictionRequest& request)
262 {
264 auto memID = request.snapshotID;
265 result.snapshotID = memID;
266
268 builder.latestEntitySnapshot(memID);
269 auto queryResult =
271 if (queryResult.success)
272 {
273 auto readMemory = fromIce<armem::wm::Memory>(queryResult.memory);
274 auto* latest = readMemory.findLatestSnapshot(memID);
275 if (latest != nullptr)
276 {
277 auto instance = memID.hasInstanceIndex()
278 ? latest->getInstance(memID)
279 : latest->getInstance(latest->getInstanceIndices().at(0));
280 result.success = true;
281 result.prediction = instance.data();
282 }
283 else
284 {
285 result.success = false;
286 result.errorMessage =
287 "Could not find entity referenced by MemoryID '" + memID.str() + "'.";
288 }
289 }
290 else
291 {
292 result.success = false;
293 result.errorMessage =
294 "Could not find entity referenced by MemoryID '" + memID.str() + "'.";
295 }
296
297 return result;
298 }
299
300 // REMOTE GUI
301
302 void
304 {
305 using namespace armarx::RemoteGui::Client;
306
307 VBoxLayout root;
308
309 // Show memory content (if enabled)
310 if (p.showMemoryContent)
311 {
312 try
313 {
314 // Core segments are locked by MemoryRemoteGui.
316 root.addChild(tab.memoryGroup);
317 }
318 catch (const std::exception& e)
319 {
320 ARMARX_WARNING << "Failed to create memory content display: " << e.what();
321 GroupBox errorBox;
322 errorBox.setLabel("Memory Content");
323 errorBox.addChild(Label("Error displaying memory content - see logs"));
324 root.addChild(errorBox);
325 }
326 }
327
328 // Create client statistics group box (if enabled)
329 if (p.showClientStats)
330 {
331 tab.clientStatsGroup = createClientStatisticsGroupBox();
332 root.addChild(tab.clientStatsGroup);
333 }
334
335 root.addChild(VSpacer());
336 RemoteGui_createTab(getName(), root, &tab);
337 }
338
339 void
341 {
342 // Full rebuild if explicitly requested (e.g., memory content changed)
343 if (tab.rebuild.exchange(false))
344 {
346 tab.lastStatsUpdate = armem::Time::Now();
347 return;
348 }
349
350 // Periodic update for client statistics (every 10 seconds) - just update labels, no rebuild
351 if (p.showClientStats)
352 {
354 if ((now - tab.lastStatsUpdate).toMilliSecondsDouble() > tab.statsUpdateIntervalMs)
355 {
356 // Check if we need to rebuild (new client appeared)
357 bool needsRebuild = false;
358 {
359 std::lock_guard lock(clientStatsMutex);
360 // Rebuild if number of clients changed (new client or client disconnected would require new labels)
361 if (clientStats.size() != tab.clientLabels.size())
362 {
363 needsRebuild = true;
364 }
365 }
366
367 if (needsRebuild)
368 {
369 // Rebuild structure when clients added/removed
370 tab.rebuild = true;
371 }
372 else
373 {
374 // Just update existing labels (much faster)
375 updateClientStatisticsLabels();
376 }
377
378 tab.lastStatsUpdate = now;
379 }
380 }
381 }
382
384 ExampleMemory::createClientStatisticsGroupBox()
385 {
386 using namespace armarx::RemoteGui::Client;
387
388 std::lock_guard lock(clientStatsMutex);
389
390 GroupBox statsBox;
391 statsBox.setLabel("Client Statistics");
392
393 // Create summary label
394 tab.summaryLabel = Label("No clients connected yet");
395 statsBox.addChild(tab.summaryLabel);
396
397 // Create labels for each existing client
398 tab.clientLabels.clear();
399 tab.connectionLabels.clear();
400
401 int clientIndex = 1;
402 for (const auto& [clientId, stats] : clientStats)
403 {
404 // Client info label
405 Label clientLabel = Label("");
406 tab.clientLabels[clientId] = clientLabel;
407 statsBox.addChild(clientLabel);
408
409 // Connection label
410 Label connLabel = Label("");
411 tab.connectionLabels[clientId] = connLabel;
412 statsBox.addChild(connLabel);
413
414 clientIndex++;
415 }
416
417 // Update the text content (mutex already locked)
418 updateClientStatisticsLabelsInternal();
419
420 return statsBox;
421 }
422
423 void
424 ExampleMemory::updateClientStatisticsLabels()
425 {
426 std::lock_guard lock(clientStatsMutex);
427 updateClientStatisticsLabelsInternal();
428 }
429
430 void
431 ExampleMemory::updateClientStatisticsLabelsInternal()
432 {
433 using namespace armarx::RemoteGui::Client;
434
435 // NOTE: Assumes clientStatsMutex is already locked by caller
436
437 // Update summary
438 if (clientStats.empty())
439 {
440 tab.summaryLabel.setText("No clients connected yet");
441 }
442 else
443 {
444 std::stringstream summary;
445 summary << "Total clients: " << clientStats.size();
446 tab.summaryLabel.setText(summary.str());
447 }
448
449 // Update each client's labels
450 int clientIndex = 1;
451 for (const auto& [clientId, stats] : clientStats)
452 {
453 // Update client info
454 if (tab.clientLabels.count(clientId) > 0)
455 {
456 std::stringstream clientInfo;
457 clientInfo << "Client " << clientIndex << ": "
458 << "Commits=" << stats.commitCount << ", "
459 << "Queries=" << stats.queryCount;
460 tab.clientLabels[clientId].setText(clientInfo.str());
461 }
462
463 // Update connection info
464 if (tab.connectionLabels.count(clientId) > 0)
465 {
466 std::stringstream connInfo;
467 connInfo << " " << stats.remoteAddress;
468 if (stats.remotePort > 0)
469 {
470 connInfo << ":" << stats.remotePort;
471 }
472 if (stats.clientPid > 0)
473 {
474 connInfo << " (PID: " << stats.clientPid << ")";
475 }
476
477 std::string connStr = connInfo.str();
478 if (connStr.length() > 70)
479 {
480 connStr = connStr.substr(0, 67) + "...";
481 }
482 tab.connectionLabels[clientId].setText(connStr);
483 }
484
485 clientIndex++;
486 }
487 }
488
489 // CLIENT STATISTICS TRACKING
490
491 std::string
492 ExampleMemory::extractClientIdentifier(const Ice::Current& current) const
493 {
494 if (current.con)
495 {
496 // Get connection string which uniquely identifies the client connection
497 return current.con->toString();
498 }
499 return "unknown";
500 }
501
502 std::string
503 ExampleMemory::formatClientIdentifier(const std::string& connectionString) const
504 {
505 // Ice connection strings have format:
506 // "local address = <IP>:<port>\nremote address = <IP>:<port>"
507 // Extract the remote address for cleaner display
508
509 // Find "remote address = " marker
510 size_t remotePos = connectionString.find("remote address = ");
511 if (remotePos != std::string::npos)
512 {
513 // Extract from "remote address = " to end (or newline)
514 std::string remote = connectionString.substr(remotePos + 17); // 17 = length of "remote address = "
515
516 // Remove any trailing whitespace or newlines
517 size_t end = remote.find_first_of("\r\n");
518 if (end != std::string::npos)
519 {
520 remote = remote.substr(0, end);
521 }
522
523 return remote;
524 }
525
526 // If no "remote address" found, try to clean up the full string
527 // Replace newlines with spaces for single-line display
528 std::string cleaned = connectionString;
529 size_t pos = 0;
530 while ((pos = cleaned.find('\n', pos)) != std::string::npos)
531 {
532 cleaned.replace(pos, 1, " | ");
533 pos += 3;
534 }
535
536 return cleaned;
537 }
538
539 void
540 ExampleMemory::parseConnectionInfo(const std::string& connectionString, std::string& address, int& port) const
541 {
542 // Extract remote address and port from Ice connection string
543 std::string fullAddress = formatClientIdentifier(connectionString);
544
545 // Find last colon to separate address from port
546 // Need to handle IPv6 addresses which contain colons
547 size_t lastColon = fullAddress.rfind(':');
548
549 if (lastColon != std::string::npos)
550 {
551 // Extract port
552 std::string portStr = fullAddress.substr(lastColon + 1);
553 try
554 {
555 port = std::stoi(portStr);
556 address = fullAddress.substr(0, lastColon);
557 }
558 catch (const std::exception&)
559 {
560 // If port parsing fails, treat entire string as address
561 address = fullAddress;
562 port = -1;
563 }
564 }
565 else
566 {
567 // No colon found
568 address = fullAddress;
569 port = -1;
570 }
571 }
572
573 int
574 ExampleMemory::extractClientPid(const Ice::Current& current) const
575 {
576 // Try to extract PID from Ice context
577 auto it = current.ctx.find("clientPid");
578 if (it != current.ctx.end())
579 {
580 try
581 {
582 return std::stoi(it->second);
583 }
584 catch (const std::exception&)
585 {
586 return -1;
587 }
588 }
589 return -1;
590 }
591
592 int
593 ExampleMemory::getClientIndex(const std::string& clientId)
594 {
595 // NOTE: Assumes clientStatsMutex is already locked by caller
596
597 // Check if client already has an index
598 auto it = clientIndices.find(clientId);
599 if (it != clientIndices.end())
600 {
601 return it->second;
602 }
603
604 // Assign new index
605 int index = nextClientIndex++;
606 clientIndices[clientId] = index;
607 return index;
608 }
609
610 void
611 ExampleMemory::recordCommit(const Ice::Current& current)
612 {
613 std::lock_guard lock(clientStatsMutex);
614 std::string clientId = extractClientIdentifier(current);
615
616 auto& stats = clientStats[clientId];
617 bool isNewClient = stats.connectionString.empty();
618
619 if (isNewClient)
620 {
621 stats.connectionString = clientId;
622 stats.firstSeen = armem::Time::Now();
623
624 // Parse connection info to extract IP and port
625 parseConnectionInfo(clientId, stats.remoteAddress, stats.remotePort);
626
627 // Extract PID from Ice context if available
628 stats.clientPid = extractClientPid(current);
629
630 ARMARX_INFO << "New client connected: " << clientId;
631 }
632
633 stats.commitCount++;
634 stats.lastCommit = armem::Time::Now();
635
636 // Publish to DebugObserver if enabled
637 if (p.publishToDebugObserver && debugObserver)
638 {
639 int clientIndex = getClientIndex(clientId);
640 std::string channel = getName() + " | Client" + std::to_string(clientIndex);
642 values["commits"] = new armarx::Variant(static_cast<int>(stats.commitCount));
643 values["queries"] = new armarx::Variant(static_cast<int>(stats.queryCount));
644 values["address"] = new armarx::Variant(stats.remoteAddress);
645 values["port"] = new armarx::Variant(stats.remotePort);
646 if (stats.clientPid > 0)
647 {
648 values["pid"] = new armarx::Variant(stats.clientPid);
649 }
650 debugObserver->setDebugChannel(channel, values);
651 }
652 }
653
654 void
655 ExampleMemory::recordQuery(const Ice::Current& current)
656 {
657 std::lock_guard lock(clientStatsMutex);
658 std::string clientId = extractClientIdentifier(current);
659
660 auto& stats = clientStats[clientId];
661 bool isNewClient = stats.connectionString.empty();
662
663 if (isNewClient)
664 {
665 stats.connectionString = clientId;
666 stats.firstSeen = armem::Time::Now();
667
668 // Parse connection info to extract IP and port
669 parseConnectionInfo(clientId, stats.remoteAddress, stats.remotePort);
670
671 // Extract PID from Ice context if available
672 stats.clientPid = extractClientPid(current);
673
674 ARMARX_INFO << "New client connected: " << clientId;
675 }
676
677 stats.queryCount++;
678 stats.lastQuery = armem::Time::Now();
679
680 // Publish to DebugObserver if enabled
681 if (p.publishToDebugObserver && debugObserver)
682 {
683 int clientIndex = getClientIndex(clientId);
684 std::string channel = getName() + " | Client" + std::to_string(clientIndex);
686 values["commits"] = new armarx::Variant(static_cast<int>(stats.commitCount));
687 values["queries"] = new armarx::Variant(static_cast<int>(stats.queryCount));
688 values["address"] = new armarx::Variant(stats.remoteAddress);
689 values["port"] = new armarx::Variant(stats.remotePort);
690 if (stats.clientPid > 0)
691 {
692 values["pid"] = new armarx::Variant(stats.clientPid);
693 }
694 debugObserver->setDebugChannel(channel, values);
695 }
696 }
697
698 void
699 ExampleMemory::logClientStatistics() const
700 {
701 std::lock_guard lock(clientStatsMutex);
702
703 if (clientStats.empty())
704 {
705 ARMARX_INFO << "No client statistics available.";
706 return;
707 }
708
709 ARMARX_IMPORTANT << "=== Client Statistics ===";
710 ARMARX_IMPORTANT << "Total clients tracked: " << clientStats.size();
711
712 for (const auto& [clientId, stats] : clientStats)
713 {
714 ARMARX_IMPORTANT << "\nClient: " << stats.remoteAddress
715 << (stats.remotePort > 0 ? ":" + std::to_string(stats.remotePort) : "");
716 if (stats.clientPid > 0)
717 {
718 ARMARX_IMPORTANT << " PID: " << stats.clientPid;
719 }
720 ARMARX_IMPORTANT << " Commits: " << stats.commitCount;
721 ARMARX_IMPORTANT << " Queries: " << stats.queryCount;
722 ARMARX_IMPORTANT << " First seen: " << stats.firstSeen.toDateTimeString();
723
724 if (stats.commitCount > 0)
725 {
726 ARMARX_IMPORTANT << " Last commit: " << stats.lastCommit.toDateTimeString();
727 }
728 if (stats.queryCount > 0)
729 {
730 ARMARX_IMPORTANT << " Last query: " << stats.lastQuery.toDateTimeString();
731 }
732
733 // Show full connection string for debugging
734 ARMARX_INFO << " Raw connection: " << stats.connectionString;
735 }
736
737 ARMARX_IMPORTANT << "========================";
738 }
739
740} // namespace armarx
int Label(int n[], int size, int *curLabel, MiscLib::Vector< std::pair< int, size_t > > *labels)
Definition Bitmap.cpp:801
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
uint8_t index
if(!yyvaluep)
Definition Grammar.cpp:645
constexpr T c
Default component property definition container.
Definition Component.h:70
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
Brief description of class ExampleMemory.
void onInitComponent() override
Pure virtual hook for the subclass.
armem::query::data::Result query(const armem::query::data::Input &input, const Ice::Current &=Ice::emptyCurrent) override
void onDisconnectComponent() override
Hook for subclass.
void RemoteGui_update() override
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
armem::data::CommitResult commit(const armem::data::Commit &commit, const Ice::Current &=Ice::emptyCurrent) override
armem::actions::ExecuteActionOutputSeq executeActions(const armem::actions::ExecuteActionInputSeq &input) override
armem::actions::GetActionsOutputSeq getActions(const armem::actions::GetActionsInputSeq &input) override
void onConnectComponent() override
Pure virtual hook for the subclass.
void onExitComponent() override
Hook for subclass.
armem::data::AddSegmentsResult addSegments(const armem::data::AddSegmentsInput &input, const Ice::Current &) override
std::string getDefaultName() const override
std::string getName() const
Retrieve name of object.
MemoryID withProviderSegmentName(const std::string &name) const
Definition MemoryID.cpp:417
MemoryID getCoreSegmentID() const
Definition MemoryID.cpp:294
std::string str(bool escapeDelimiters=true) const
Get a string representation of this memory ID.
Definition MemoryID.cpp:102
MemoryID withEntityName(const std::string &name) const
Definition MemoryID.cpp:425
std::string entityName
Definition MemoryID.h:53
std::string providerSegmentName
Definition MemoryID.h:52
void latestEntitySnapshot(const MemoryID &entityID)
Definition Builder.cpp:131
Utility for memory Remote Guis.
GroupBox makeGroupBox(const armem::wm::Memory &memory) const
CoreSegment & addCoreSegment(const std::string &name, Args... args)
void addPredictor(const PredictionEngine &engine, Predictor &&predictor)
Definition Prediction.h:68
static DateTime Now()
Definition DateTime.cpp:51
#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_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
armarx::core::time::DateTime Time
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::map< std::string, VariantBasePtr > StringVariantBaseMap
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
void fromIce(const std::map< IceKeyT, IceValueT > &iceMap, boost::container::flat_map< CppKeyT, CppValueT > &cppMap)
void toIce(std::map< IceKeyT, IceValueT > &iceMap, const boost::container::flat_map< CppKeyT, CppValueT > &cppMap)
void RemoteGui_createTab(std::string const &name, RemoteGui::Client::Widget const &rootWidget, RemoteGui::Client::Tab *tab)
void addChild(Widget const &child)
Definition Widgets.cpp:95
void setLabel(std::string const &text)
Definition Widgets.cpp:420
A bundle of updates to be sent to the memory.
Definition Commit.h:90
EntityUpdate & add()
Definition Commit.cpp:80
An update of an entity for a specific point in time.
Definition Commit.h:26
aron::data::DictPtr prediction
Definition Prediction.h:63
const auto * findLatestInstance(int instanceIndex=0) const
Find the latest entity instance.