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