HokuyoLaserUnit.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::HokuyoLaserUnit
17 * @author Fabian Paus ( fabian dot paus at kit dot edu )
18 * @date 2017
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
23#include "HokuyoLaserUnit.h"
24
25#include <SimoxUtility/algorithm/string/string_tools.h>
26
33
34#include <HokuyoLaserScannerDriver/urg_utils.h>
35
36using namespace armarx;
37
39
44
45void
47{
48 offeringTopicFromProperty("RobotHealthTopicName");
49 offeringTopicFromProperty("DebugObserverName");
50
51 laserScannerListenerProxyName = getProperty<std::string>("LaserScannerListenerName").getValue();
52 usingProxyFromProperty("LaserScannerListenerName");
53 ARMARX_INFO << "Going to report to listener " << laserScannerListenerProxyName;
54 updatePeriod = getProperty<int>("UpdatePeriod").getValue();
55 angleOffset = getProperty<float>("AngleOffset").getValue();
56
57 std::string deviceStrings = getProperty<std::string>("Devices").getValue();
58 std::vector<std::string> splitDeviceStrings = Split(deviceStrings, ";");
59 devices.clear();
60 devices.reserve(splitDeviceStrings.size());
61 for (std::string const& deviceString : splitDeviceStrings)
62 {
63 std::vector<std::string> deviceInfo = Split(deviceString, ",");
64 if (deviceInfo.size() != 3)
65 {
66 ARMARX_WARNING << "Unexpected format for laser scanner device: " << deviceString
67 << " (split size: " << deviceInfo.size() << ")";
68 continue;
69 }
70
71 try
72 {
73 int port = std::stoi(deviceInfo[1]);
74
75 HokuyoLaserScanDevice& device = devices.emplace_back();
76 device.ip = deviceInfo[0];
77 device.port = port;
78 device.frame = deviceInfo[2];
79 device.angleOffset = angleOffset;
80 device.connected = false;
81
82 device.componentName = getName();
83 }
84 catch (std::exception const& ex)
85 {
86 ARMARX_WARNING << "Could not convert port to integer for laser scanner device "
87 << deviceString << " (port is " << deviceInfo[1] << ") : " << ex.what();
88 continue;
89 }
90 }
91}
92
93void
95{
97
98 getProxyFromProperty(listenerPrx, "LaserScannerListenerName");
100 getProperty<std::string>("DebugObserverName").getValue());
101
102 connectedDevices.clear();
103 for (HokuyoLaserScanDevice& device : devices)
104 {
105 if (device.task)
106 {
107 device.task->stop();
108 device.task = nullptr;
109 }
110
111 // connecting devices for first time
112 if (!device.reconnect())
113 {
114 ARMARX_WARNING << "Not starting task for laser scanner with IP: " << device.ip
115 << ", Port: " << device.port;
116 continue;
117 }
118
119 ARMARX_CHECK_NOT_NULL(heartbeat);
120 heartbeat->signUp(device.ip,
123 {"LaserScanner", "Localization"},
124 "HokuyoLaserScanDevice");
125
126 LaserScannerInfo info;
127 info.device = device.ip;
128 info.frame = device.frame;
129 int minStep = 0, maxStep = 0;
130 urg_step_min_max(&device.urg, &minStep, &maxStep);
131 info.minAngle = urg_step2rad(&device.urg, minStep);
132 info.maxAngle = urg_step2rad(&device.urg, maxStep);
133
134
135 int lengthDataSize = urg_max_data_size(&device.urg);
136 info.stepSize = (info.maxAngle - info.minAngle) / (maxStep - minStep);
137 device.lengthData.resize(lengthDataSize);
138
139 device.listenerPrx = listenerPrx;
140 device.robotHealthPlugin = heartbeat;
141 device.debugObserver = debugObserver;
142
143 connectedDevices.push_back(info);
144
145 ARMARX_INFO << "Connected to " << device.ip << ", " << info.frame << ", " << info.minAngle
146 << ", " << info.maxAngle << ", " << info.stepSize;
147
148 device.task = new RunningTask<HokuyoLaserScanDevice>(
149 &device, &HokuyoLaserScanDevice::run, "HokuyoLaserScanUpdate_" + device.ip);
150 device.task->start();
151 }
152}
153
154void
156{
157 for (HokuyoLaserScanDevice& device : devices)
158 {
159 if (device.task)
160 {
161 device.task->stop();
162 device.task = nullptr;
163 }
164 if (device.connected)
165 {
166 urg_close(&device.urg);
167 device.connected = false;
168 }
169 }
170}
171
172void
176
183
184std::string
185HokuyoLaserUnit::getReportTopicName(const Ice::Current&) const
186{
187 return laserScannerListenerProxyName;
188}
189
190LaserScannerInfoSeq
191HokuyoLaserUnit::getConnectedDevices(const Ice::Current&) const
192{
193 return connectedDevices;
194}
195
196bool
198{
199 if (connected)
200 {
201 ARMARX_INFO << "Disconnecting from laser scanner with IP " << ip;
202 urg_close(&urg);
203 connected = false;
204 }
205 ARMARX_INFO << "Reconnecting to " << ip << ":" << port;
206 int ret = urg_open(&urg, URG_ETHERNET, ip.c_str(), port);
207 connected = (ret == 0);
208 if (!connected)
209 {
210 ARMARX_WARNING << "Could not connect to laser scanner device using URG driver (IP: " << ip
211 << ", Port: " << port << ", Error: " << ret << ")";
212 return false;
213 }
214 ret = urg_start_measurement(&urg, URG_DISTANCE, URG_SCAN_INFINITY, 0);
215 connected = (ret == 0);
216 if (connected)
217 {
218 ARMARX_IMPORTANT << "Reconnected succesffully to " << ip << ":" << port;
219 return true;
220 }
221 else
222 {
224 << "Could not start measurement for laser scanner device using URG driver (IP: " << ip
225 << ", Port: " << port << ", Error: " << ret << ")";
226 return false;
227 }
228}
229
230void
232{
234 while (!task->isStopped())
235 {
236 ARMARX_INFO << deactivateSpam() << "Running update loop for laserscanner device " << ip;
237
238 IceUtil::Time time_start = TimeUtil::GetTime();
239
240 if (errorCounter > 10)
241 {
242 ARMARX_ERROR << "Device " << ip << " has too many consecutive errors!";
243 // assume dead
244 reconnect();
245 }
246 if (connected)
247 {
248 int lengthDataSize = urg_get_distance(&urg, lengthData.data(), nullptr);
249 if (lengthDataSize < 0)
250 {
252 << "Could not get measurement for laser scanner (IP: " << ip
253 << ", Port: " << port << ", Error: " << lengthDataSize << ")";
254 errorCounter++;
255 continue;
256 }
257 IceUtil::Time time_measure = TimeUtil::GetTime();
258 TimestampVariantPtr now(new TimestampVariant(time_measure));
259
260 scan.clear();
261 scan.reserve(lengthDataSize);
262 for (int stepIndex = 0; stepIndex < lengthDataSize; ++stepIndex)
263 {
264 LaserScanStep step;
265 long distance = lengthData[stepIndex];
266 // TODO: Extract the min and max valid value for distance into parameters?
267 if (distance >= 21 && distance <= 30000)
268 {
269 step.angle =
270 angleOffset + (float)urg_index2rad(&urg, stepIndex); // Convert steps to rad
271 step.distance = (float)distance; // Data is already in mm
272 scan.push_back(step);
273 }
274 }
275
276 IceUtil::Time time_update = TimeUtil::GetTime();
277
278 errorCounter = 0;
279
280 if (listenerPrx)
281 {
282
283 listenerPrx->reportSensorValues(ip, frame, scan, now);
284 }
285 else
286 {
287 ARMARX_WARNING << "No proxy available: IP: " << ip << ", Port: " << port;
288 }
289 IceUtil::Time time_proxyReport = TimeUtil::GetTime();
290
291 if (robotHealthPlugin != nullptr)
292 {
293 robotHealthPlugin->heartbeatOnChannel(ip);
294 }
295 else
296 {
297 ARMARX_WARNING << "No robot health topic available: IP: " << ip
298 << ", Port: " << port;
299 }
300
301 IceUtil::Time time_topicHeartbeat = TimeUtil::GetTime();
302
303 IceUtil::Time duration = time_topicHeartbeat - time_start;
304
305 StringVariantBaseMap durations;
306 durations["total_ms"] = new Variant(duration.toMilliSecondsDouble());
307 durations["measure_ms"] =
308 new Variant((time_measure - time_start).toMilliSecondsDouble());
309 durations["update_ms"] =
310 new Variant((time_update - time_measure).toMilliSecondsDouble());
311 durations["proxy_report_ms"] =
312 new Variant((time_proxyReport - time_update).toMilliSecondsDouble());
313 durations["topic_health_ms"] =
314 new Variant((time_topicHeartbeat - time_proxyReport).toMilliSecondsDouble());
315 debugObserver->setDebugChannel(
316 "LaserScannerDuration_" + simox::alg::replace_all(ip, ".", "_"), durations);
317
318 if (duration.toSecondsDouble() > 0.1)
319 {
320 ARMARX_WARNING << "Laserscanner reports are slow"
321 << "Total time: " << duration.toMilliSecondsDouble() << "ms\n"
322 << "Measure: "
323 << (time_measure - time_start).toMilliSecondsDouble() << "ms \n"
324 << "Update: "
325 << (time_update - time_measure).toMilliSecondsDouble() << "ms\n"
326 << "TopicSensor: "
327 << (time_proxyReport - time_update).toMilliSecondsDouble() << "ms\n"
328 << "TopicHeart: "
329 << (time_topicHeartbeat - time_proxyReport).toMilliSecondsDouble()
330 << "ms\n";
331 }
332 }
333 }
334}
#define float
Definition 16_Level.h:22
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition Logging.cpp:75
bool usingProxyFromProperty(const std::string &propertyName, const std::string &endpoints="")
Use a proxy whose name is specified by the given property.
void offeringTopicFromProperty(const std::string &propertyName)
Offer a topic whose name is specified by the given property.
ProxyType getProxyFromProperty(const std::string &propertyName, bool addToDependencies=false, const std::string &endpoints="", bool throwOnProxyError=true)
Get a proxy whose name is specified by the given property.
Definition Component.h:242
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
Property< PropertyType > getProperty(const std::string &name)
Brief description of class HokuyoLaserUnit.
void onInitComponent() override
LaserScannerInfoSeq getConnectedDevices(const Ice::Current &c) const override
void onDisconnectComponent() override
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void onConnectComponent() override
std::string getReportTopicName(const Ice::Current &c) const override
void onExitComponent() override
TopicProxyType getTopic(const std::string &name)
Returns a proxy of the specified topic.
PluginT * addPlugin(const std::string prefix="", ParamsT &&... params)
std::string getName() const
Retrieve name of object.
static IceUtil::Time GetTime(TimeMode timeMode=TimeMode::VirtualTime)
Get the current time.
Definition TimeUtil.cpp:42
Implements a Variant type for timestamps.
The Variant class is described here: Variants.
Definition Variant.h:224
static Duration MilliSeconds(std::int64_t milliSeconds)
Constructs a duration in milliseconds.
Definition Duration.cpp:48
#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_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::map< std::string, VariantBasePtr > StringVariantBaseMap
std::vector< std::string > Split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
IceInternal::Handle< TimestampVariant > TimestampVariantPtr
double distance(const Point &a, const Point &b)
Definition point.hpp:95
LaserScannerUnitListenerPrx listenerPrx
std::vector< long > lengthData
RunningTask< HokuyoLaserScanDevice >::pointer_type task
DebugObserverInterfacePrx debugObserver
armarx::plugins::HeartbeatComponentPlugin * robotHealthPlugin
#define ARMARX_TRACE
Definition trace.h:77