RobotStateMemory.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::RobotSensorMemory
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 "RobotStateMemory.h"
24
25#include <SimoxUtility/algorithm/contains.h>
26#include <SimoxUtility/algorithm/get_map_keys_values.h>
27#include <SimoxUtility/algorithm/string.h>
28
33
34#include <RobotAPI/interface/core/PoseBase.h>
40
42
44 "RobotStateMemory");
45
47{
48
50 descriptionSegment(iceAdapter()),
51 proprioceptionSegment(iceAdapter()),
52 exteroceptionSegment(iceAdapter()),
53 localizationSegment(iceAdapter()),
54 commonVisu(descriptionSegment, proprioceptionSegment, localizationSegment)
55 {
56 addPlugin(debugObserver);
57 ARMARX_CHECK_NOT_NULL(debugObserver);
58
59 addPlugin(robotUnit.plugin);
60 ARMARX_CHECK_NOT_NULL(robotUnit.plugin);
61 robotUnit.plugin->setRobotUnitAsOptionalDependency(true);
62 robotUnit.dataBuffer = std::make_unique<RobotUnit::Queue>();
63 }
64
66
69 {
72
73 const std::string robotUnitPrefix = RobotUnit::sensorValuePrefix;
74
75
76 defs->optional(robotUnit.waitForRobotUnit,
77 "WaitForRobotUnit",
78 "Add the robot unit as dependency to the component. This memory requires a "
79 "running RobotUnit, therefore we should add it as explicit dependency.");
80
81 defs->optional(robotUnit.reader.properties.sensorPrefix,
82 robotUnitPrefix + "SensorValuePrefix",
83 "Prefix of all sensor values.");
84 defs->optional(robotUnit.pollFrequency,
85 robotUnitPrefix + "UpdateFrequency",
86 "The frequency to store values in Hz. All other values get discarded. "
87 "Minimum is 1, max is " +
88 std::to_string(RobotUnit::ROBOT_UNIT_MAXIMUM_FREQUENCY) + ".")
89 .setMin(1)
90 .setMax(RobotUnit::ROBOT_UNIT_MAXIMUM_FREQUENCY);
91
92 defs->optional(robotUnit.writer.properties.queueSizeWarningThreshold,
93 robotUnitPrefix + "QueueSizeWarningThreshold",
94 "If the RobotStateWriter's data queue grows larger than this value, "
95 "a warning is logged (rate-limited).");
96
97
98 const std::string prefix = "mem.";
99
101
102 descriptionSegment.defineProperties(defs, prefix + "desc.");
103 exteroceptionSegment.defineProperties(defs, prefix + "ext.");
104 proprioceptionSegment.defineProperties(defs, prefix + "prop.");
105 localizationSegment.defineProperties(defs, prefix + "loc.");
106 commonVisu.defineProperties(defs, prefix + "visu.");
107
108 return defs;
109 }
110
111 std::string
113 {
114 return "RobotStateMemory";
115 }
116
117 void
119 {
120 descriptionSegment.init();
121 proprioceptionSegment.init();
122 exteroceptionSegment.init();
123 localizationSegment.init();
124 commonVisu.init();
125
126 robotUnit.pollFrequency =
127 std::clamp(robotUnit.pollFrequency, 0.F, RobotUnit::ROBOT_UNIT_MAXIMUM_FREQUENCY);
128
129 std::vector<std::string> includePaths;
130 std::vector<std::string> packages = armarx::Application::GetProjectDependencies();
131 packages.push_back(Application::GetProjectName());
132
133 for (const std::string& projectName : packages)
134 {
135 if (projectName.empty())
136 {
137 continue;
138 }
139
140 const CMakePackageFinder project(projectName);
141 const std::string pathsString = project.getIncludePaths();
142 std::vector<std::string> projectIncludePaths = simox::alg::split(pathsString, ";,");
143 includePaths.insert(
144 includePaths.end(), projectIncludePaths.begin(), projectIncludePaths.end());
145 }
146
147 if (robotUnit.waitForRobotUnit)
148 {
149 usingProxy(robotUnit.plugin->getRobotUnitName());
150 }
151 }
152
153 void
155 {
156 {
157 ARMARX_CHECK(not robotUnit.reader.task or not robotUnit.reader.task->isRunning());
158 ARMARX_CHECK(not robotUnit.writer.task or not robotUnit.writer.task->isRunning());
159 robotUnit.dataBuffer = std::make_unique<RobotUnit::Queue>();
160 }
161 ARMARX_CHECK_NOT_NULL(debugObserver->getDebugObserver());
162
163 // Pass the debug observer to the long-term memory for timing measurements
164 longtermMemory().setDebugObserver(debugObserver->getDebugObserver());
165 ARMARX_INFO << "Debug observer set for LTM timing measurements";
166
167 // 1. General setup
168 localizationSegment.onConnect();
169 commonVisu.connect(getArvizClient(), debugObserver->getDebugObserver());
170
171 // 2. Check for RobotUnit. If RobotUnit is enabled. Otherwise, we do not wait for a streaming service and do not set up the segments
172 if (robotUnit.plugin->hasRobotUnitName())
173 {
174 robotUnit.plugin->waitUntilRobotUnitIsRunning();
175 const RobotUnitInterfacePrx robotUnitPrx = robotUnit.plugin->getRobotUnit();
176
177 robotUnit.reader.setTag(getName());
178 robotUnit.writer.setTag(getName());
179
180 descriptionSegment.onConnect(robotUnitPrx);
181 proprioceptionSegment.onConnect(robotUnitPrx);
182
183 robotUnit.reader.connect(
184 *robotUnit.plugin,
185 *debugObserver,
186 proprioceptionSegment.getRobotUnitProviderID().providerSegmentName);
187 robotUnit.writer.connect(*debugObserver);
188 robotUnit.writer.properties.robotUnitProviderID =
189 proprioceptionSegment.getRobotUnitProviderID();
190
191 robotUnit.reader.task = new SimpleRunningTask<>(
192 [this]() { robotUnit.reader.run(robotUnit.pollFrequency, *robotUnit.dataBuffer); },
193 "Robot Unit Reader");
194 robotUnit.writer.task = new SimpleRunningTask<>(
195 [this]()
196 {
197 robotUnit.writer.run(robotUnit.pollFrequency,
198 *robotUnit.dataBuffer,
199 iceAdapter(),
200 localizationSegment);
201 },
202 "Robot State Writer");
203
204 startRobotUnitStream();
205 }
206
207 createRemoteGuiTab();
209 }
210
211 void
213 {
214 stopRobotUnitStream();
215 }
216
217 void
219 {
220 stopRobotUnitStream();
221 }
222
223 armarx::PoseBasePtr
225 const std::string& robotName,
226 const ::Ice::Current& /*unused*/)
227 {
229 ARMARX_DEBUG << "Getting robot pose of robot " << robotName << " at timestamp " << timestamp
230 << ".";
231
232 auto poseMap = localizationSegment.getRobotGlobalPoses(timestamp);
233
234 const bool robotNameFound = simox::alg::contains_key(poseMap, robotName);
235 ARMARX_CHECK(robotNameFound)
236 << "Robot with name " << robotName << " does not exist at or before timestamp "
237 << timestamp << ".\n"
238 << "Available robots are: " << simox::alg::get_keys(poseMap);
239
240 return {new Pose(poseMap[robotName].matrix())};
241 }
242
243 /*************************************************************/
244 // RobotUnit Streaming functions
245 /*************************************************************/
246
247
248 void
249 RobotStateMemory::startRobotUnitStream()
250 {
251 ARMARX_INFO << "Starting RobotUnit stream";
252 ARMARX_CHECK(robotUnit.plugin->robotUnitIsRunning());
253
254 if (robotUnit.reader.task->isRunning() || robotUnit.writer.task->isRunning())
255 {
256 if (robotUnit.reader.task->isRunning() && robotUnit.writer.task->isRunning())
257 {
258 return;
259 }
260 ARMARX_WARNING << "Found inconsistency in running tasks. Restarting all!";
261 stopRobotUnitStream();
262 }
263 {
264 std::stringstream ss;
265 ss << "Getting sensor values for:" << std::endl;
266 for (const auto& [name, dataEntry] : robotUnit.reader.description.entries)
267 {
268 const std::string type =
269 RobotUnitDataStreaming::DataEntryNames.to_name(dataEntry.type);
270 ss << "\t" << name << " (type: '" << type << "')" << std::endl;
271 }
272 ARMARX_INFO << ss.str();
273 }
274
275 robotUnit.reader.task->start();
276 robotUnit.writer.task->start();
277 }
278
279 void
280 RobotStateMemory::stopRobotUnitStream()
281 {
282 ARMARX_INFO << "Stopping RobotUnit stream";
283 robotUnit.dataBuffer->close();
284 robotUnit.reader.task->stop();
285 ARMARX_VERBOSE << "RobotUnit reader stopped";
286 robotUnit.writer.task->stop();
287 ARMARX_VERBOSE << "RobotUnit writer stopped";
288 }
289
290 void
291 RobotStateMemory::createRemoteGuiTab()
292 {
293 using namespace armarx::RemoteGui::Client;
294
295 VBoxLayout root;
296
297 tab.clearRobotStateWriterQueueButton.setLabel("Clear RobotStateWriter Queue");
298 root.addChild(tab.clearRobotStateWriterQueueButton);
299
300 RemoteGui_createTab(getName(), root, &tab);
301 }
302
303 void
304 RobotStateMemory::RemoteGui_update()
305 {
306 try
307 {
308 if (tab.clearRobotStateWriterQueueButton.wasClicked())
309 {
310 clearRobotStateWriterQueue();
311 }
312 }
313 catch (const std::exception& e)
314 {
315 ARMARX_WARNING << "RemoteGui_update: exception while handling button click: "
316 << e.what();
317 }
318 catch (...)
319 {
320 ARMARX_WARNING << "RemoteGui_update: unknown exception while handling button click.";
321 }
322 }
323
324 void
325 RobotStateMemory::clearRobotStateWriterQueue()
326 {
327 std::size_t dropped = 0;
328 try
329 {
330 if (not robotUnit.dataBuffer)
331 {
332 ARMARX_WARNING << "Cannot clear RobotStateWriter queue: data buffer is null.";
333 return;
334 }
335
336 proprioception::RobotUnitData discarded;
337 while (true)
338 {
339 boost::queue_op_status status = boost::queue_op_status::empty;
340 try
341 {
342 status = robotUnit.dataBuffer->try_pull(discarded);
343 }
344 catch (const std::exception& e)
345 {
346 ARMARX_WARNING << "Exception during queue try_pull after dropping " << dropped
347 << " entries: " << e.what() << ". Stopping drain.";
348 break;
349 }
350 catch (...)
351 {
352 ARMARX_WARNING << "Unknown exception during queue try_pull after dropping "
353 << dropped << " entries. Stopping drain.";
354 break;
355 }
356
357 if (status != boost::queue_op_status::success)
358 {
359 break;
360 }
361 ++dropped;
362 }
363
364 ARMARX_IMPORTANT << "Cleared RobotStateWriter queue: dropped " << dropped
365 << " entries.";
366 }
367 catch (const std::exception& e)
368 {
369 ARMARX_WARNING << "clearRobotStateWriterQueue failed after dropping " << dropped
370 << " entries: " << e.what();
371 }
372 catch (...)
373 {
374 ARMARX_WARNING << "clearRobotStateWriterQueue failed with unknown exception after "
375 "dropping "
376 << dropped << " entries.";
377 }
378 }
379
380} // namespace armarx::armem::server::robot_state
std::string timestamp()
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
static const std::string & GetProjectName()
static const Ice::StringSeq & GetProjectDependencies()
armarx::viz::Client & getArvizClient()
The CMakePackageFinder class provides an interface to the CMake Package finder capabilities.
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
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
PluginT * addPlugin(const std::string prefix="", ParamsT &&... params)
std::string getName() const
Retrieve name of object.
The Pose class.
Definition Pose.h:243
void start()
Starts the thread.
bool isRunning() const
Retrieve running state of the thread.
void setDebugObserver(DebugObserverInterfacePrx observer)
Set an optional debug observer for timing measurements.
Definition MemoryBase.h:517
void onInitComponent() override
Pure virtual hook for the subclass.
void onDisconnectComponent() override
Hook for subclass.
armarx::PoseBasePtr getGlobalRobotPose(Ice::Long timestamp, const std::string &robotName, const ::Ice::Current &) override
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Creates the property definition container.
void onConnectComponent() override
Pure virtual hook for the subclass.
std::string getDefaultName() const override
Retrieve default name of component.
RobotUnitDataStreaming::DataStreamingDescription description
static Duration MicroSeconds(std::int64_t microSeconds)
Constructs a duration in microseconds.
Definition Duration.cpp:24
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#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_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
const simox::meta::EnumNames< DataEntryType > DataEntryNames
armarx::core::time::DateTime Time
::IceInternal::ProxyHandle<::IceProxy::armarx::RobotUnitInterface > RobotUnitInterfacePrx
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
SimpleRunningTask(Ts...) -> SimpleRunningTask< std::function< void(void)> >
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