From c15de133622d123025204537b1b2dec691a28468 Mon Sep 17 00:00:00 2001
From: Johann Mantel <j-mantel@gmx.net>
Date: Fri, 2 Jul 2021 12:46:10 +0200
Subject: [PATCH] Add SickScanAdapter which inherits from SickScanCommon in
 order to override the data handling and publishing method with an
 ArmarX-compatible implementation

---
 .../drivers/SickLaserUnit/CMakeLists.txt      |    6 +-
 .../drivers/SickLaserUnit/SickScanAdapter.cpp | 2211 +++++++++++++++++
 .../drivers/SickLaserUnit/SickScanAdapter.h   |  172 ++
 3 files changed, 2386 insertions(+), 3 deletions(-)
 create mode 100644 source/RobotAPI/drivers/SickLaserUnit/SickScanAdapter.cpp
 create mode 100644 source/RobotAPI/drivers/SickLaserUnit/SickScanAdapter.h

diff --git a/source/RobotAPI/drivers/SickLaserUnit/CMakeLists.txt b/source/RobotAPI/drivers/SickLaserUnit/CMakeLists.txt
index 24f8769f4..4f22c48fb 100644
--- a/source/RobotAPI/drivers/SickLaserUnit/CMakeLists.txt
+++ b/source/RobotAPI/drivers/SickLaserUnit/CMakeLists.txt
@@ -41,15 +41,15 @@ armarx_add_component(
 	## SickLaserUnitInterfaces  # If you defined a component ice interface above.
 
 	${sick_scan_base_LIBRARIES}
-	${Boost_LIBRARY_DIRS}
+	${Boost_LIBRARIES}
 
     SOURCES
         SickLaserUnit.cpp
-		#SickScanAdapter.cpp
+	        #SickScanAdapter.cpp
 
     HEADERS
         SickLaserUnit.h
-		#SickScanAdapter.h
+	        #SickScanAdapter.h
 )
 
 
diff --git a/source/RobotAPI/drivers/SickLaserUnit/SickScanAdapter.cpp b/source/RobotAPI/drivers/SickLaserUnit/SickScanAdapter.cpp
new file mode 100644
index 000000000..7bdb37dec
--- /dev/null
+++ b/source/RobotAPI/drivers/SickLaserUnit/SickScanAdapter.cpp
@@ -0,0 +1,2211 @@
+/**
+* \file
+* \brief Laser Scanner communication (TCP Helper Class)
+* Copyright (C) 2013, Osnabrück University
+* Copyright (C) 2017, Ing.-Buero Dr. Michael Lehning, Hildesheim
+* Copyright (C) 2017, SICK AG, Waldkirch
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of Osnabrück University nor the names of its
+*       contributors may be used to endorse or promote products derived from
+*       this software without specific prior written permission.
+*     * Neither the name of SICK AG nor the names of its
+*       contributors may be used to endorse or promote products derived from
+*       this software without specific prior written permission
+*     * Neither the name of Ing.-Buero Dr. Michael Lehning nor the names of its
+*       contributors may be used to endorse or promote products derived from
+*       this software without specific prior written permission
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+* POSSIBILITY OF SUCH DAMAGE.
+*
+*  Last modified: 12th Dec 2017
+*
+*      Authors:
+*         Michael Lehning <michael.lehning@lehning.de>
+*         Jochen Sprickerhof <jochen@sprickerhof.de>
+*         Martin Günther <mguenthe@uos.de>
+*
+* Based on the TiM communication example by SICK AG.
+*
+*/
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4996)
+#pragma warning(disable: 4267)
+#pragma warning(disable: 4101)   // C4101: "e" : Unreferenzierte lokale Variable
+#define _WIN32_WINNT 0x0501
+
+#endif
+
+#include "SickScanAdapter.h"
+#include <sick_scan/tcp/colaa.hpp>
+#include <sick_scan/tcp/colab.hpp>
+#include <sick_scan/sick_generic_radar.h>
+
+#include <boost/asio.hpp>
+#include <boost/lambda/lambda.hpp>
+#include <algorithm>
+#include <iterator>
+#include <boost/lexical_cast.hpp>
+#include <vector>
+
+#ifdef ROSSIMU
+
+#include <sick_scan/rosconsole_simu.hpp>
+
+#endif
+
+std::vector<unsigned char> exampleData(65536);
+std::vector<unsigned char> receivedData(65536);
+static long receivedDataLen = 0;
+
+static int getDiagnosticErrorCode()
+{
+#ifdef _MSC_VER
+#undef ERROR
+    return (2);
+#else
+    return (diagnostic_msgs::DiagnosticStatus::ERROR);
+#endif
+}
+
+namespace armarx
+{
+    bool emulateReply(UINT8* requestData, int requestLen, std::vector<unsigned char>* replyVector)
+    {
+        std::string request;
+        std::string reply;
+        std::vector<std::string> keyWordList;
+        std::vector<std::string> answerList;
+
+        std::string scannerType = "???";
+        ros::NodeHandle nhPriv;
+
+        enum
+        {
+            SIMU_RADAR = 0, SIMU_MRS_1XXX = 1, SIMU_NUM
+        };
+        int enumType = SIMU_RADAR; // Default simulation
+        if (true == nhPriv.getParam("scanner_type", scannerType))
+        {
+            if (scannerType.compare("sick_mrs_1xxx") == 0)
+            {
+                ROS_INFO("Simulate MRS1xxx");
+                enumType = SIMU_MRS_1XXX;
+            }
+        }
+
+        switch (enumType)
+        {
+            case SIMU_RADAR:
+
+                // XXX
+                keyWordList.push_back("sMN SetAccessMode");
+                answerList.push_back("sAN SetAccessMode 1");
+
+                keyWordList.push_back("sWN EIHstCola");
+                answerList.push_back("sWA EIHstCola");
+
+                keyWordList.push_back("sRN FirmwareVersion");
+                answerList.push_back("sRA FirmwareVersion 8 1.0.0.0R");
+
+                keyWordList.push_back("sRN OrdNum");
+                answerList.push_back("sRA OrdNum 7 1234567");
+
+                keyWordList.push_back("sWN TransmitTargets 1");
+                answerList.push_back("sWA TransmitTargets");
+
+                keyWordList.push_back("sWN TransmitObjects 1");
+                answerList.push_back("sWA TransmitObjects");
+
+                keyWordList.push_back("sWN TCTrackingMode 0");
+                answerList.push_back("sWA TCTrackingMode");
+
+                keyWordList.push_back("sRN SCdevicestate");
+                answerList.push_back("sRA SCdevicestate 1");
+
+                keyWordList.push_back("sRN DItype");
+                answerList.push_back("sRA DItype D RMS3xx-xxxxxx");
+
+                keyWordList.push_back("sRN ODoprh");
+                answerList.push_back("sRA ODoprh 451");
+
+                keyWordList.push_back("sMN mSCloadappdef");
+                answerList.push_back("sAN mSCloadappdef");
+
+
+                keyWordList.push_back("sRN SerialNumber");
+                answerList.push_back("sRA SerialNumber 8 18020073");
+
+                keyWordList.push_back("sMN Run");
+                answerList.push_back("sAN Run 1s");
+
+                keyWordList.push_back("sRN ODpwrc");
+                answerList.push_back("sRA ODpwrc 20");
+
+                keyWordList.push_back("sRN LocationName");
+                answerList.push_back("sRA LocationName B not defined");
+
+                keyWordList.push_back("sEN LMDradardata 1");
+                answerList.push_back("sEA LMDradardata 1");
+
+                for (int i = 0; i < requestLen; i++)
+                {
+                    request += (char) requestData[i];
+                }
+                for (size_t i = 0; i < keyWordList.size(); i++)
+                {
+                    if (request.find(keyWordList[i]) != std::string::npos)
+                    {
+                        reply = (char) 0x02;
+                        reply += answerList[i];
+                        reply += (char) 0x03;
+                    }
+                }
+
+                replyVector->clear();
+                for (size_t i = 0; i < reply.length(); i++)
+                {
+                    replyVector->push_back((unsigned char) reply[i]);
+                }
+                break;
+            case SIMU_MRS_1XXX:
+                ROS_INFO("Emulation of MRS_1xxx is not implemented.\n");
+                assert(0);
+                // XXX
+                break;
+        }
+
+
+        return (true);
+    }
+
+
+    SickScanAdapter::SickScanAdapter(const std::string& hostname, const std::string& port, int& timelimit,
+                                     sick_scan::SickGenericParser* parser, char cola_dialect_id)
+        :
+        sick_scan::SickScanCommon(parser),
+        socket_(io_service_),
+        deadline_(io_service_),
+        hostname_(hostname),
+        port_(port),
+        timelimit_(timelimit)
+    {
+
+        setEmulSensor(false);
+        if ((cola_dialect_id == 'a') || (cola_dialect_id == 'A'))
+        {
+            this->setProtocolType(CoLa_A);
+        }
+
+        if ((cola_dialect_id == 'b') || (cola_dialect_id == 'B'))
+        {
+            this->setProtocolType(CoLa_B);
+        }
+
+        assert(this->getProtocolType() != CoLa_Unknown);
+
+        m_numberOfBytesInReceiveBuffer = 0;
+        m_alreadyReceivedBytes = 0;
+        this->setReplyMode(0);
+        // io_service_.setReadCallbackFunction(boost::bind(&SopasDevice::readCallbackFunction, this, _1, _2));
+
+        // Set up the deadline actor to implement timeouts.
+        // Based on blocking TCP example on:
+        // http://www.boost.org/doc/libs/1_46_0/doc/html/boost_asio/example/timeouts/blocking_tcp_client.cpp
+        deadline_.expires_at(boost::posix_time::pos_infin);
+        checkDeadline();
+
+    }
+
+    SickScanAdapter::~SickScanAdapter()
+    {
+        // stop_scanner();
+        close_device();
+    }
+
+    using boost::asio::ip::tcp;
+    using boost::lambda::var;
+    using boost::lambda::_1;
+
+
+    /*!
+    \brief parsing datagram and publishing ros messages
+    \return error code
+    */
+    int SickScanAdapter::loopOnce()
+    {
+        static int cnt = 0;
+        diagnostics_.update();
+
+        unsigned char receiveBuffer[65536];
+        int actual_length = 0;
+        static unsigned int iteration_count = 0;
+        bool useBinaryProtocol = this->parser_->getCurrentParamPtr()->getUseBinaryProtocol();
+
+        ros::Time recvTimeStamp = ros::Time::now();  // timestamp incoming package, will be overwritten by get_datagram
+        // tcp packet
+        ros::Time recvTimeStampPush = ros::Time::now();  // timestamp
+
+        /*
+         * Try to get datagram
+         *
+         *
+         */
+
+
+        int packetsInLoop = 0;
+
+        const int maxNumAllowedPacketsToProcess = 25; // maximum number of packets, which will be processed in this loop.
+
+        int numPacketsProcessed = 0; // count number of processed datagrams
+
+        static bool firstTimeCalled = true;
+        static bool dumpData = false;
+        static int verboseLevel = 0;
+        static bool slamBundle = false;
+        float timeIncrement;
+        static std::string echoForSlam = "";
+        if (firstTimeCalled == true)
+        {
+
+            /* Dump Binary Protocol */
+            ros::NodeHandle tmpParam("~");
+            tmpParam.getParam("slam_echo", echoForSlam);
+            tmpParam.getParam("slam_bundle", slamBundle);
+            tmpParam.getParam("verboseLevel", verboseLevel);
+            firstTimeCalled = false;
+        }
+        do
+        {
+
+            int result = get_datagram(recvTimeStamp, receiveBuffer, 65536, &actual_length, useBinaryProtocol, &packetsInLoop);
+            numPacketsProcessed++;
+
+            ros::Duration dur = recvTimeStampPush - recvTimeStamp;
+
+            if (result != 0)
+            {
+                ROS_ERROR("Read Error when getting datagram: %i.", result);
+                diagnostics_.broadcast(getDiagnosticErrorCode(), "Read Error when getting datagram.");
+                return sick_scan::ExitError; // return failure to exit node
+            }
+            if (actual_length <= 0)
+            {
+                return sick_scan::ExitSuccess;
+            } // return success to continue looping
+
+            // ----- if requested, skip frames
+            if (iteration_count++ % (config_.skip + 1) != 0)
+            {
+                return sick_scan::ExitSuccess;
+            }
+
+            if (publish_datagram_)
+            {
+                std_msgs::String datagram_msg;
+                datagram_msg.data = std::string(reinterpret_cast<char*>(receiveBuffer));
+                datagram_pub_.publish(datagram_msg);
+            }
+
+
+            if (verboseLevel > 0)
+            {
+                dumpDatagramForDebugging(receiveBuffer, actual_length);
+            }
+
+
+            bool deviceIsRadar = false;
+
+            if (this->parser_->getCurrentParamPtr()->getDeviceIsRadar() == true)
+            {
+                deviceIsRadar = true;
+            }
+
+            if (true == deviceIsRadar)
+            {
+                int errorCode = sick_scan::ExitSuccess;
+#ifndef ROSSIMU
+                SickScanRadarSingleton* radar = SickScanRadarSingleton::getInstance();
+                // parse radar telegram and send pointcloud2-debug messages
+                errorCode = radar->parseDatagram(recvTimeStamp, (unsigned char*) receiveBuffer, actual_length,
+                                                 useBinaryProtocol);
+#endif
+                return errorCode; // return success to continue looping
+            }
+
+            static SickScanImu scanImu(this); // todo remove static
+            if (scanImu.isImuDatagram((char*) receiveBuffer, actual_length))
+            {
+                int errorCode = sick_scan::ExitSuccess;
+                if (scanImu.isImuAckDatagram((char*) receiveBuffer, actual_length))
+                {
+
+                }
+                else
+                {
+                    // parse radar telegram and send pointcloud2-debug messages
+                    errorCode = scanImu.parseDatagram(recvTimeStamp, (unsigned char*) receiveBuffer, actual_length,
+                                                      useBinaryProtocol);
+
+                }
+                return errorCode; // return success to continue looping
+
+
+            }
+            else
+            {
+
+                sensor_msgs::LaserScan msg;
+#ifndef _MSC_VER
+                sick_scan::Encoder EncoderMsg;
+                EncoderMsg.header.stamp = recvTimeStamp + ros::Duration(config_.time_offset);
+                //TODO remove this hardcoded variable
+                bool FireEncoder = false;
+                EncoderMsg.header.frame_id = "Encoder";
+                EncoderMsg.header.seq = numPacketsProcessed;
+#endif
+                msg.header.stamp = recvTimeStamp + ros::Duration(config_.time_offset);
+                double elevationAngleInRad = 0.0;
+                /*
+                 * datagrams are enclosed in <STX> (0x02), <ETX> (0x03) pairs
+                 */
+                char* buffer_pos = (char*) receiveBuffer;
+                char* dstart, *dend;
+                bool dumpDbg = false;
+                bool dataToProcess = true;
+                std::vector<float> vang_vec;
+                vang_vec.clear();
+                dstart = NULL;
+                dend = NULL;
+
+                while (dataToProcess)
+                {
+                    const int maxAllowedEchos = 5;
+
+                    int numValidEchos = 0;
+                    int aiValidEchoIdx[maxAllowedEchos] = {0};
+                    size_t dlength;
+                    int success = -1;
+                    int numEchos = 0;
+                    int echoMask = 0;
+                    bool publishPointCloud = true;
+
+                    if (useBinaryProtocol)
+                    {
+                        // if binary protocol used then parse binary message
+                        std::vector<unsigned char> receiveBufferVec = std::vector<unsigned char>(receiveBuffer,
+                                receiveBuffer + actual_length);
+#ifdef DEBUG_DUMP_TO_CONSOLE_ENABLED
+                        if (actual_length > 1000)
+                        {
+                            DataDumper::instance().dumpUcharBufferToConsole(receiveBuffer, actual_length);
+
+                        }
+
+                        DataDumper::instance().dumpUcharBufferToConsole(receiveBuffer, actual_length);
+#endif
+                        if (receiveBufferVec.size() > 8)
+                        {
+                            long idVal = 0;
+                            long lenVal = 0;
+                            memcpy(&idVal, receiveBuffer + 0, 4);  // read identifier
+                            memcpy(&lenVal, receiveBuffer + 4, 4);  // read length indicator
+                            swap_endian((unsigned char*) &lenVal, 4);
+
+                            if (idVal == 0x02020202)  // id for binary message
+                            {
+#if  0
+                                {
+                                    static int cnt = 0;
+                                    char szFileName[255];
+
+#ifdef _MSC_VER
+                                    sprintf(szFileName, "c:\\temp\\dump%05d.bin", cnt);
+#else
+                                    sprintf(szFileName, "/tmp/dump%05d.txt", cnt);
+#endif
+                                    FILE* fout;
+                                    fout = fopen(szFileName, "wb");
+                                    fwrite(receiveBuffer, actual_length, 1, fout);
+                                    fclose(fout);
+                                    cnt++;
+                                }
+#endif
+                                // binary message
+                                if (lenVal < actual_length)
+                                {
+                                    short elevAngleX200 = 0;  // signed short (F5 B2  -> Layer 24
+                                    // F5B2h -> -2638/200= -13.19°
+                                    int scanFrequencyX100 = 0;
+                                    double elevAngle = 0.00;
+                                    double scanFrequency = 0.0;
+                                    long measurementFrequencyDiv100 = 0; // multiply with 100
+                                    int numOfEncoders = 0;
+                                    int numberOf16BitChannels = 0;
+                                    int numberOf8BitChannels = 0;
+                                    uint32_t SystemCountScan = 0;
+                                    static uint32_t lastSystemCountScan = 0;// this variable is used to ensure that only the first time stamp of an multi layer scann is used for PLL updating
+                                    uint32_t SystemCountTransmit = 0;
+
+                                    memcpy(&elevAngleX200, receiveBuffer + 50, 2);
+                                    swap_endian((unsigned char*) &elevAngleX200, 2);
+
+                                    elevationAngleInRad = -elevAngleX200 / 200.0 * deg2rad_const;
+                                    //TODO check this ??
+                                    msg.header.seq = elevAngleX200; // should be multiple of 0.625° starting with -2638 (corresponding to 13.19°)
+
+                                    memcpy(&SystemCountScan, receiveBuffer + 0x26, 4);
+                                    swap_endian((unsigned char*) &SystemCountScan, 4);
+
+                                    memcpy(&SystemCountTransmit, receiveBuffer + 0x2A, 4);
+                                    swap_endian((unsigned char*) &SystemCountTransmit, 4);
+                                    double timestampfloat = recvTimeStamp.sec + recvTimeStamp.nsec * 1e-9;
+                                    bool bRet;
+                                    if (SystemCountScan !=
+                                        lastSystemCountScan)//MRS 6000 sends 6 packets with same  SystemCountScan we should only update the pll once with this time stamp since the SystemCountTransmit are different and this will only increase jitter of the pll
+                                    {
+                                        bRet = SoftwarePLL::instance().updatePLL(recvTimeStamp.sec, recvTimeStamp.nsec,
+                                                SystemCountTransmit);
+                                        lastSystemCountScan = SystemCountScan;
+                                    }
+                                    ros::Time tmp_time = recvTimeStamp;
+                                    bRet = SoftwarePLL::instance().getCorrectedTimeStamp(recvTimeStamp.sec, recvTimeStamp.nsec,
+                                            SystemCountScan);
+                                    double timestampfloat_coor = recvTimeStamp.sec + recvTimeStamp.nsec * 1e-9;
+                                    double DeltaTime = timestampfloat - timestampfloat_coor;
+                                    //ROS_INFO("%F,%F,%u,%u,%F",timestampfloat,timestampfloat_coor,SystemCountTransmit,SystemCountScan,DeltaTime);
+                                    //TODO Handle return values
+                                    if (config_.sw_pll_only_publish == true && bRet == false)
+                                    {
+                                        int packets_expected_to_drop = SoftwarePLL::instance().fifoSize - 1;
+                                        SoftwarePLL::instance().packeds_droped++;
+                                        ROS_INFO("%i / %i Packet dropped Software PLL not yet locked.",
+                                                 SoftwarePLL::instance().packeds_droped, packets_expected_to_drop);
+                                        if (SoftwarePLL::instance().packeds_droped == packets_expected_to_drop)
+                                        {
+                                            ROS_INFO("Software PLL is expected to be ready now!");
+                                        }
+                                        if (SoftwarePLL::instance().packeds_droped > packets_expected_to_drop)
+                                        {
+                                            ROS_WARN("More packages than expected were dropped!!\n"
+                                                     "Check the network connection.\n"
+                                                     "Check if the system time has been changed in a leap.\n"
+                                                     "If the problems can persist, disable the software PLL with the option sw_pll_only_publish=False  !");
+                                        }
+                                        dataToProcess = false;
+                                        break;
+                                    }
+
+#ifdef DEBUG_DUMP_ENABLED
+                                    double elevationAngleInDeg = elevationAngleInRad = -elevAngleX200 / 200.0;
+                                    // DataDumper::instance().pushData((double)SystemCountScan, "LAYER", elevationAngleInDeg);
+                                    //DataDumper::instance().pushData((double)SystemCountScan, "LASESCANTIME", SystemCountScan);
+                                    //DataDumper::instance().pushData((double)SystemCountTransmit, "LASERTRANSMITTIME", SystemCountTransmit);
+                                    //DataDumper::instance().pushData((double)SystemCountScan, "LASERTRANSMITDELAY", debug_duration.toSec());
+#endif
+
+                                    memcpy(&scanFrequencyX100, receiveBuffer + 52, 4);
+                                    swap_endian((unsigned char*) &scanFrequencyX100, 4);
+
+                                    memcpy(&measurementFrequencyDiv100, receiveBuffer + 56, 4);
+                                    swap_endian((unsigned char*) &measurementFrequencyDiv100, 4);
+
+
+                                    msg.scan_time = 1.0 / (scanFrequencyX100 / 100.0);
+
+                                    //due firmware inconsistency
+                                    if (measurementFrequencyDiv100 > 10000)
+                                    {
+                                        measurementFrequencyDiv100 /= 100;
+                                    }
+                                    msg.time_increment = 1.0 / (measurementFrequencyDiv100 * 100.0);
+                                    timeIncrement = msg.time_increment;
+                                    msg.range_min = parser_->get_range_min();
+                                    msg.range_max = parser_->get_range_max();
+
+                                    memcpy(&numOfEncoders, receiveBuffer + 60, 2);
+                                    swap_endian((unsigned char*) &numOfEncoders, 2);
+                                    int encoderDataOffset = 6 * numOfEncoders;
+                                    int32_t EncoderPosTicks[4] = {0};
+                                    int16_t EncoderSpeed[4] = {0};
+
+#ifndef _MSC_VER
+                                    if (numOfEncoders > 0 && numOfEncoders < 5)
+                                    {
+                                        FireEncoder = true;
+                                        for (int EncoderNum = 0; EncoderNum < numOfEncoders; EncoderNum++)
+                                        {
+                                            memcpy(&EncoderPosTicks[EncoderNum], receiveBuffer + 62 + EncoderNum * 6, 4);
+                                            swap_endian((unsigned char*) &EncoderPosTicks[EncoderNum], 4);
+                                            memcpy(&EncoderSpeed[EncoderNum], receiveBuffer + 66 + EncoderNum * 6, 2);
+                                            swap_endian((unsigned char*) &EncoderSpeed[EncoderNum], 2);
+                                        }
+                                    }
+                                    //TODO handle multi encoder with multiple encode msg or different encoder msg definition now using only first encoder
+                                    EncoderMsg.enc_position = EncoderPosTicks[0];
+                                    EncoderMsg.enc_speed = EncoderSpeed[0];
+#endif
+                                    memcpy(&numberOf16BitChannels, receiveBuffer + 62 + encoderDataOffset, 2);
+                                    swap_endian((unsigned char*) &numberOf16BitChannels, 2);
+
+                                    int parseOff = 64 + encoderDataOffset;
+
+
+                                    char szChannel[255] = {0};
+                                    float scaleFactor = 1.0;
+                                    float scaleFactorOffset = 0.0;
+                                    int32_t startAngleDiv10000 = 1;
+                                    int32_t sizeOfSingleAngularStepDiv10000 = 1;
+                                    double startAngle = 0.0;
+                                    double sizeOfSingleAngularStep = 0.0;
+                                    short numberOfItems = 0;
+
+                                    static int cnt = 0;
+                                    cnt++;
+                                    // get number of 8 bit channels
+                                    // we must jump of the 16 bit data blocks including header ...
+                                    for (int i = 0; i < numberOf16BitChannels; i++)
+                                    {
+                                        int numberOfItems = 0x00;
+                                        memcpy(&numberOfItems, receiveBuffer + parseOff + 19, 2);
+                                        swap_endian((unsigned char*) &numberOfItems, 2);
+                                        parseOff += 21; // 21 Byte header followed by data entries
+                                        parseOff += numberOfItems * 2;
+                                    }
+
+                                    // now we can read the number of 8-Bit-Channels
+                                    memcpy(&numberOf8BitChannels, receiveBuffer + parseOff, 2);
+                                    swap_endian((unsigned char*) &numberOf8BitChannels, 2);
+
+                                    parseOff = 64 + encoderDataOffset;
+                                    enum datagram_parse_task
+                                    {
+                                        process_dist,
+                                        process_vang,
+                                        process_rssi,
+                                        process_idle
+                                    };
+                                    int rssiCnt = 0;
+                                    int vangleCnt = 0;
+                                    int distChannelCnt = 0;
+
+                                    for (int processLoop = 0; processLoop < 2; processLoop++)
+                                    {
+                                        int totalChannelCnt = 0;
+
+
+                                        bool bCont = true;
+
+                                        datagram_parse_task task = process_idle;
+                                        bool parsePacket = true;
+                                        parseOff = 64 + encoderDataOffset;
+                                        bool processData = false;
+
+                                        if (processLoop == 0)
+                                        {
+                                            distChannelCnt = 0;
+                                            rssiCnt = 0;
+                                            vangleCnt = 0;
+                                        }
+
+                                        if (processLoop == 1)
+                                        {
+                                            processData = true;
+                                            numEchos = distChannelCnt;
+                                            msg.ranges.resize(numberOfItems * numEchos);
+                                            if (rssiCnt > 0)
+                                            {
+                                                msg.intensities.resize(numberOfItems * rssiCnt);
+                                            }
+                                            else
+                                            {
+                                            }
+                                            if (vangleCnt > 0) // should be 0 or 1
+                                            {
+                                                vang_vec.resize(numberOfItems * vangleCnt);
+                                            }
+                                            else
+                                            {
+                                                vang_vec.clear();
+                                            }
+                                            echoMask = (1 << numEchos) - 1;
+
+                                            // reset count. We will use the counter for index calculation now.
+                                            distChannelCnt = 0;
+                                            rssiCnt = 0;
+                                            vangleCnt = 0;
+
+                                        }
+
+                                        szChannel[6] = '\0';
+                                        scaleFactor = 1.0;
+                                        scaleFactorOffset = 0.0;
+                                        startAngleDiv10000 = 1;
+                                        sizeOfSingleAngularStepDiv10000 = 1;
+                                        startAngle = 0.0;
+                                        sizeOfSingleAngularStep = 0.0;
+                                        numberOfItems = 0;
+
+
+#if 1 // prepared for multiecho parsing
+
+                                        bCont = true;
+                                        bool doVangVecProc = false;
+                                        // try to get number of DIST and RSSI from binary data
+                                        task = process_idle;
+                                        do
+                                        {
+                                            task = process_idle;
+                                            doVangVecProc = false;
+                                            int processDataLenValuesInBytes = 2;
+
+                                            if (totalChannelCnt == numberOf16BitChannels)
+                                            {
+                                                parseOff += 2; // jump of number of 8 bit channels- already parsed above
+                                            }
+
+                                            if (totalChannelCnt >= numberOf16BitChannels)
+                                            {
+                                                processDataLenValuesInBytes = 1; // then process 8 bit values ...
+                                            }
+                                            bCont = false;
+                                            strcpy(szChannel, "");
+
+                                            if (totalChannelCnt < (numberOf16BitChannels + numberOf8BitChannels))
+                                            {
+                                                szChannel[5] = '\0';
+                                                strncpy(szChannel, (const char*) receiveBuffer + parseOff, 5);
+                                            }
+                                            else
+                                            {
+                                                // all channels processed (16 bit and 8 bit channels)
+                                            }
+
+                                            if (strstr(szChannel, "DIST") == szChannel)
+                                            {
+                                                task = process_dist;
+                                                distChannelCnt++;
+                                                bCont = true;
+                                                numberOfItems = 0;
+                                                memcpy(&numberOfItems, receiveBuffer + parseOff + 19, 2);
+                                                swap_endian((unsigned char*) &numberOfItems, 2);
+
+                                            }
+                                            if (strstr(szChannel, "VANG") == szChannel)
+                                            {
+                                                vangleCnt++;
+                                                task = process_vang;
+                                                bCont = true;
+                                                numberOfItems = 0;
+                                                memcpy(&numberOfItems, receiveBuffer + parseOff + 19, 2);
+                                                swap_endian((unsigned char*) &numberOfItems, 2);
+
+                                                vang_vec.resize(numberOfItems);
+
+                                            }
+                                            if (strstr(szChannel, "RSSI") == szChannel)
+                                            {
+                                                task = process_rssi;
+                                                rssiCnt++;
+                                                bCont = true;
+                                                numberOfItems = 0;
+                                                // copy two byte value (unsigned short to  numberOfItems
+                                                memcpy(&numberOfItems, receiveBuffer + parseOff + 19, 2);
+                                                swap_endian((unsigned char*) &numberOfItems, 2);  // swap
+
+                                            }
+                                            if (bCont)
+                                            {
+                                                scaleFactor = 0.0;
+                                                scaleFactorOffset = 0.0;
+                                                startAngleDiv10000 = 0;
+                                                sizeOfSingleAngularStepDiv10000 = 0;
+                                                numberOfItems = 0;
+
+                                                memcpy(&scaleFactor, receiveBuffer + parseOff + 5, 4);
+                                                memcpy(&scaleFactorOffset, receiveBuffer + parseOff + 9, 4);
+                                                memcpy(&startAngleDiv10000, receiveBuffer + parseOff + 13, 4);
+                                                memcpy(&sizeOfSingleAngularStepDiv10000, receiveBuffer + parseOff + 17, 2);
+                                                memcpy(&numberOfItems, receiveBuffer + parseOff + 19, 2);
+
+
+                                                swap_endian((unsigned char*) &scaleFactor, 4);
+                                                swap_endian((unsigned char*) &scaleFactorOffset, 4);
+                                                swap_endian((unsigned char*) &startAngleDiv10000, 4);
+                                                swap_endian((unsigned char*) &sizeOfSingleAngularStepDiv10000, 2);
+                                                swap_endian((unsigned char*) &numberOfItems, 2);
+
+                                                if (processData)
+                                                {
+                                                    unsigned short* data = (unsigned short*)(receiveBuffer + parseOff + 21);
+
+                                                    unsigned char* swapPtr = (unsigned char*) data;
+                                                    // copy RSSI-Values +2 for 16-bit values +1 for 8-bit value
+                                                    for (int i = 0;
+                                                         i < numberOfItems * processDataLenValuesInBytes; i += processDataLenValuesInBytes)
+                                                    {
+                                                        if (processDataLenValuesInBytes == 1)
+                                                        {
+                                                        }
+                                                        else
+                                                        {
+                                                            unsigned char tmp;
+                                                            tmp = swapPtr[i + 1];
+                                                            swapPtr[i + 1] = swapPtr[i];
+                                                            swapPtr[i] = tmp;
+                                                        }
+                                                    }
+                                                    int idx = 0;
+
+                                                    switch (task)
+                                                    {
+
+                                                        case process_dist:
+                                                        {
+                                                            startAngle = startAngleDiv10000 / 10000.00;
+                                                            sizeOfSingleAngularStep = sizeOfSingleAngularStepDiv10000 / 10000.0;
+                                                            sizeOfSingleAngularStep *= (M_PI / 180.0);
+
+                                                            msg.angle_min = startAngle / 180.0 * M_PI - M_PI / 2;
+                                                            msg.angle_increment = sizeOfSingleAngularStep;
+                                                            msg.angle_max = msg.angle_min + (numberOfItems - 1) * msg.angle_increment;
+
+                                                            if (this->parser_->getCurrentParamPtr()->getScanMirroredAndShifted())
+                                                            {
+                                                                msg.angle_min -= M_PI / 2;
+                                                                msg.angle_max -= M_PI / 2;
+
+                                                                msg.angle_min *= -1.0;
+                                                                msg.angle_increment *= -1.0;
+                                                                msg.angle_max *= -1.0;
+
+                                                            }
+                                                            float* rangePtr = NULL;
+
+                                                            if (numberOfItems > 0)
+                                                            {
+                                                                rangePtr = &msg.ranges[0];
+                                                            }
+                                                            float scaleFactor_001 = 0.001F * scaleFactor;// to avoid repeated multiplication
+                                                            for (int i = 0; i < numberOfItems; i++)
+                                                            {
+                                                                idx = i + numberOfItems * (distChannelCnt - 1);
+                                                                rangePtr[idx] = (float) data[i] * scaleFactor_001 + scaleFactorOffset;
+#ifdef DEBUG_DUMP_ENABLED
+                                                                if (distChannelCnt == 1)
+                                                                {
+                                                                    if (i == floor(numberOfItems / 2))
+                                                                    {
+                                                                        double curTimeStamp = SystemCountScan + i * msg.time_increment * 1E6;
+                                                                        //DataDumper::instance().pushData(curTimeStamp, "DIST", rangePtr[idx]);
+                                                                    }
+                                                                }
+#endif
+                                                                //XXX
+                                                            }
+
+                                                        }
+                                                        break;
+                                                        case process_rssi:
+                                                        {
+                                                            // Das muss vom Protokoll abgeleitet werden. !!!
+
+                                                            float* intensityPtr = NULL;
+
+                                                            if (numberOfItems > 0)
+                                                            {
+                                                                intensityPtr = &msg.intensities[0];
+
+                                                            }
+                                                            for (int i = 0; i < numberOfItems; i++)
+                                                            {
+                                                                idx = i + numberOfItems * (rssiCnt - 1);
+                                                                // we must select between 16 bit and 8 bit values
+                                                                float rssiVal = 0.0;
+                                                                if (processDataLenValuesInBytes == 2)
+                                                                {
+                                                                    rssiVal = (float) data[i];
+                                                                }
+                                                                else
+                                                                {
+                                                                    unsigned char* data8Ptr = (unsigned char*) data;
+                                                                    rssiVal = (float) data8Ptr[i];
+                                                                }
+                                                                intensityPtr[idx] = rssiVal * scaleFactor + scaleFactorOffset;
+                                                            }
+                                                        }
+                                                        break;
+
+                                                        case process_vang:
+                                                            float* vangPtr = NULL;
+                                                            if (numberOfItems > 0)
+                                                            {
+                                                                vangPtr = &vang_vec[0]; // much faster, with vang_vec[i] each time the size will be checked
+                                                            }
+                                                            for (int i = 0; i < numberOfItems; i++)
+                                                            {
+                                                                vangPtr[i] = (float) data[i] * scaleFactor + scaleFactorOffset;
+                                                            }
+                                                            break;
+                                                    }
+                                                }
+                                                parseOff += 21 + processDataLenValuesInBytes * numberOfItems;
+
+
+                                            }
+                                            totalChannelCnt++;
+                                        }
+                                        while (bCont);
+                                    }
+#endif
+
+                                    elevAngle = elevAngleX200 / 200.0;
+                                    scanFrequency = scanFrequencyX100 / 100.0;
+
+
+                                }
+                            }
+                        }
+
+
+                        parser_->checkScanTiming(msg.time_increment, msg.scan_time, msg.angle_increment, 0.00001f);
+
+                        success = sick_scan::ExitSuccess;
+                        // change Parsing Mode
+                        dataToProcess = false; // only one package allowed - no chaining
+                    }
+                    else // Parsing of Ascii-Encoding of datagram, xxx
+                    {
+                        dstart = strchr(buffer_pos, 0x02);
+                        if (dstart != NULL)
+                        {
+                            dend = strchr(dstart + 1, 0x03);
+                        }
+                        if ((dstart != NULL) && (dend != NULL))
+                        {
+                            dataToProcess = true; // continue parasing
+                            dlength = dend - dstart;
+                            *dend = '\0';
+                            dstart++;
+                        }
+                        else
+                        {
+                            dataToProcess = false;
+                            break; //
+                        }
+
+                        if (dumpDbg)
+                        {
+                            {
+                                static int cnt = 0;
+                                char szFileName[255];
+
+#ifdef _MSC_VER
+                                sprintf(szFileName, "c:\\temp\\dump%05d.txt", cnt);
+#else
+                                sprintf(szFileName, "/tmp/dump%05d.txt", cnt);
+#endif
+#if 0
+                                FILE* fout;
+                                fout = fopen(szFileName, "wb");
+                                fwrite(dstart, dlength, 1, fout);
+                                fclose(fout);
+#endif
+                                cnt++;
+                            }
+                        }
+
+                        // HEADER of data followed by DIST1 ... DIST2 ... DIST3 .... RSSI1 ... RSSI2.... RSSI3...
+
+                        // <frameid>_<sign>00500_DIST[1|2|3]
+                        numEchos = 1;
+                        // numEchos contains number of echos (1..5)
+                        // _msg holds ALL data of all echos
+                        success = parser_->parse_datagram(dstart, dlength, config_, msg, numEchos, echoMask);
+                        publishPointCloud = true; // for MRS1000
+
+                        numValidEchos = 0;
+                        for (int i = 0; i < maxAllowedEchos; i++)
+                        {
+                            aiValidEchoIdx[i] = 0;
+                        }
+                    }
+
+
+                    int numOfLayers = parser_->getCurrentParamPtr()->getNumberOfLayers();
+
+                    if (success == sick_scan::ExitSuccess)
+                    {
+                        bool elevationPreCalculated = false;
+                        double elevationAngleDegree = 0.0;
+
+
+                        std::vector<float> rangeTmp = msg.ranges;  // copy all range value
+                        std::vector<float> intensityTmp = msg.intensities; // copy all intensity value
+
+                        int intensityTmpNum = intensityTmp.size();
+                        float* intensityTmpPtr = NULL;
+                        if (intensityTmpNum > 0)
+                        {
+                            intensityTmpPtr = &intensityTmp[0];
+                        }
+
+                        // Helpful: https://answers.ros.org/question/191265/pointcloud2-access-data/
+                        // https://gist.github.com/yuma-m/b5dcce1b515335c93ce8
+                        // Copy to pointcloud
+                        int layer = 0;
+                        int baseLayer = 0;
+                        bool useGivenElevationAngle = false;
+                        switch (numOfLayers)
+                        {
+                            case 1: // TIM571 etc.
+                                baseLayer = 0;
+                                break;
+                            case 4:
+
+                                baseLayer = -1;
+                                if (msg.header.seq == 250)
+                                {
+                                    layer = -1;
+                                }
+                                else if (msg.header.seq == 0)
+                                {
+                                    layer = 0;
+                                }
+                                else if (msg.header.seq == -250)
+                                {
+                                    layer = 1;
+                                }
+                                else if (msg.header.seq == -500)
+                                {
+                                    layer = 2;
+                                }
+                                elevationAngleDegree = this->parser_->getCurrentParamPtr()->getElevationDegreeResolution();
+                                elevationAngleDegree = elevationAngleDegree / 180.0 * M_PI;
+                                // 0.0436332 /*2.5 degrees*/;
+                                break;
+                            case 24: // Preparation for MRS6000
+                                baseLayer = -1;
+                                layer = (msg.header.seq - (-2638)) / 125;
+                                layer = (23 - layer) - 1;
+#if 0
+                                elevationAngleDegree = this->parser_->getCurrentParamPtr()->getElevationDegreeResolution();
+                                elevationAngleDegree = elevationAngleDegree / 180.0 * M_PI;
+#endif
+
+                                elevationPreCalculated = true;
+                                if (vang_vec.size() > 0)
+                                {
+                                    useGivenElevationAngle = true;
+                                }
+                                break;
+                            default:
+                                assert(0);
+                                break; // unsupported
+
+                        }
+
+
+
+
+
+                        // XXX  - HIER MEHRERE SCANS publish, falls Mehrzielszenario läuft
+                        if (numEchos > 5)
+                        {
+                            ROS_WARN("Too much echos");
+                        }
+                        else
+                        {
+
+                            size_t startOffset = 0;
+                            size_t endOffset = 0;
+                            int echoPartNum = msg.ranges.size() / numEchos;
+                            for (int i = 0; i < numEchos; i++)
+                            {
+
+                                bool sendMsg = false;
+                                if ((echoMask & (1 << i)) & outputChannelFlagId)
+                                {
+                                    aiValidEchoIdx[numValidEchos] = i; // save index
+                                    numValidEchos++;
+                                    sendMsg = true;
+                                }
+                                startOffset = i * echoPartNum;
+                                endOffset = (i + 1) * echoPartNum;
+
+                                msg.ranges.clear();
+                                msg.intensities.clear();
+                                msg.ranges = std::vector<float>(
+                                                 rangeTmp.begin() + startOffset,
+                                                 rangeTmp.begin() + endOffset);
+                                // check also for MRS1104
+                                if (endOffset <= intensityTmp.size() && (intensityTmp.size() > 0))
+                                {
+                                    msg.intensities = std::vector<float>(
+                                                          intensityTmp.begin() + startOffset,
+                                                          intensityTmp.begin() + endOffset);
+                                }
+                                else
+                                {
+                                    msg.intensities.resize(echoPartNum); // fill with zeros
+                                }
+                                {
+                                    // numEchos
+                                    char szTmp[255] = {0};
+                                    if (this->parser_->getCurrentParamPtr()->getNumberOfLayers() > 1)
+                                    {
+                                        const char* cpFrameId = config_.frame_id.c_str();
+#if 0
+                                        sprintf(szTmp, "%s_%+04d_DIST%d", cpFrameId, msg.header.seq, i + 1);
+#else // experimental
+                                        char szSignName[10] = {0};
+                                        if ((int) msg.header.seq < 0)
+                                        {
+                                            strcpy(szSignName, "NEG");
+                                        }
+                                        else
+                                        {
+                                            strcpy(szSignName, "POS");
+
+                                        }
+
+                                        sprintf(szTmp, "%s_%s_%03d_DIST%d", cpFrameId, szSignName, abs((int) msg.header.seq), i + 1);
+#endif
+                                    }
+                                    else
+                                    {
+                                        strcpy(szTmp, config_.frame_id.c_str());
+                                    }
+
+                                    msg.header.frame_id = std::string(szTmp);
+                                    // Hector slam can only process ONE valid frame id.
+                                    if (echoForSlam.length() > 0)
+                                    {
+                                        if (slamBundle)
+                                        {
+                                            // try to map first echos to horizontal layers.
+                                            if (i == 0)
+                                            {
+                                                // first echo
+                                                msg.header.frame_id = echoForSlam;
+                                                strcpy(szTmp, echoForSlam.c_str());  //
+                                                if (elevationAngleInRad != 0.0)
+                                                {
+                                                    float cosVal = cos(elevationAngleInRad);
+                                                    int rangeNum = msg.ranges.size();
+                                                    for (int j = 0; j < rangeNum; j++)
+                                                    {
+                                                        msg.ranges[j] *= cosVal;
+                                                    }
+                                                }
+                                            }
+                                        }
+
+                                        if (echoForSlam.compare(szTmp) == 0)
+                                        {
+                                            sendMsg = true;
+                                        }
+                                        else
+                                        {
+                                            sendMsg = false;
+                                        }
+                                    }
+
+                                }
+#ifndef _MSC_VER
+                                if (parser_->getCurrentParamPtr()->getEncoderMode() >= 0 && FireEncoder == true)//
+                                {
+                                    Encoder_pub.publish(EncoderMsg);
+                                }
+                                if (numOfLayers > 4)
+                                {
+                                    sendMsg = false; // too many layers for publish as scan message. Only pointcloud messages will be pub.
+                                }
+                                if (sendMsg&
+                                    outputChannelFlagId)  // publish only configured channels - workaround for cfg-bug MRS1104
+                                {
+
+                                    pub_.publish(msg);
+                                }
+#else
+                                printf("MSG received...");
+#endif
+                            }
+                        }
+
+
+                        if (publishPointCloud == true)
+                        {
+
+
+                            const int numChannels = 4; // x y z i (for intensity)
+
+                            int numTmpLayer = numOfLayers;
+
+
+                            cloud_.header.stamp = recvTimeStamp + ros::Duration(config_.time_offset);
+
+
+                            cloud_.header.frame_id = config_.frame_id;
+                            cloud_.header.seq = 0;
+                            cloud_.height = numTmpLayer * numValidEchos; // due to multi echo multiplied by num. of layers
+                            cloud_.width = msg.ranges.size();
+                            cloud_.is_bigendian = false;
+                            cloud_.is_dense = true;
+                            cloud_.point_step = numChannels * sizeof(float);
+                            cloud_.row_step = cloud_.point_step * cloud_.width;
+                            cloud_.fields.resize(numChannels);
+                            for (int i = 0; i < numChannels; i++)
+                            {
+                                std::string channelId[] = {"x", "y", "z", "intensity"};
+                                cloud_.fields[i].name = channelId[i];
+                                cloud_.fields[i].offset = i * sizeof(float);
+                                cloud_.fields[i].count = 1;
+                                cloud_.fields[i].datatype = sensor_msgs::PointField::FLOAT32;
+                            }
+
+                            cloud_.data.resize(cloud_.row_step * cloud_.height);
+
+                            unsigned char* cloudDataPtr = &(cloud_.data[0]);
+
+
+                            // prepare lookup for elevation angle table
+
+                            std::vector<float> cosAlphaTable; // Lookup table for cos
+                            std::vector<float> sinAlphaTable; // Lookup table for sin
+                            size_t rangeNum = rangeTmp.size() / numValidEchos;
+                            cosAlphaTable.resize(rangeNum);
+                            sinAlphaTable.resize(rangeNum);
+                            float mirror_factor = 1.0;
+                            float angleShift = 0;
+                            if (this->parser_->getCurrentParamPtr()->getScanMirroredAndShifted())
+                            {
+                                //                mirror_factor = -1.0;
+                                //                angleShift = +M_PI/2.0; // add 90 deg for NAV3xx-series
+                            }
+
+                            for (size_t iEcho = 0; iEcho < numValidEchos; iEcho++)
+                            {
+
+                                float angle = config_.min_ang;
+
+
+                                float* cosAlphaTablePtr = &cosAlphaTable[0];
+                                float* sinAlphaTablePtr = &sinAlphaTable[0];
+
+                                float* vangPtr = NULL;
+                                float* rangeTmpPtr = &rangeTmp[0];
+                                if (vang_vec.size() > 0)
+                                {
+                                    vangPtr = &vang_vec[0];
+                                }
+                                for (size_t i = 0; i < rangeNum; i++)
+                                {
+                                    enum enum_index_descr
+                                    {
+                                        idx_x,
+                                        idx_y,
+                                        idx_z,
+                                        idx_intensity,
+                                        idx_num
+                                    };
+                                    long adroff = i * (numChannels * (int) sizeof(float));
+
+                                    adroff += (layer - baseLayer) * cloud_.row_step;
+
+                                    adroff += iEcho * cloud_.row_step * numTmpLayer;
+
+                                    unsigned char* ptr = cloudDataPtr + adroff;
+                                    float* fptr = (float*)(cloudDataPtr + adroff);
+
+                                    geometry_msgs::Point32 point;
+                                    float range_meter = rangeTmpPtr[iEcho * rangeNum + i];
+                                    float phi = angle; // azimuth angle
+                                    float alpha = 0.0;  // elevation angle
+
+                                    if (useGivenElevationAngle) // FOR MRS6124
+                                    {
+                                        alpha = -vangPtr[i] * deg2rad_const;
+                                    }
+                                    else
+                                    {
+                                        if (elevationPreCalculated) // FOR MRS6124 without VANGL
+                                        {
+                                            alpha = elevationAngleInRad;
+                                        }
+                                        else
+                                        {
+                                            alpha = layer * elevationAngleDegree; // for MRS1104
+                                        }
+                                    }
+
+                                    if (iEcho == 0)
+                                    {
+                                        cosAlphaTablePtr[i] = cos(alpha); // for z-value (elevation)
+                                        sinAlphaTablePtr[i] = sin(alpha);
+                                    }
+                                    else
+                                    {
+                                        // Just for Debugging: printf("%3d %8.3lf %8.3lf\n", (int)i, cosAlphaTablePtr[i], sinAlphaTablePtr[i]);
+                                    }
+                                    // Thanks to Sebastian Pütz <spuetz@uos.de> for his hint
+                                    float rangeCos = range_meter * cosAlphaTablePtr[i];
+
+                                    double phi_used = phi  + angleShift;
+                                    if (this->angleCompensator != NULL)
+                                    {
+                                        phi_used = angleCompensator->compensateAngleInRadFromRos(phi_used);
+                                    }
+                                    fptr[idx_x] = rangeCos * cos(phi_used);  // copy x value in pointcloud
+                                    fptr[idx_y] = rangeCos * sin(phi_used);  // copy y value in pointcloud
+                                    fptr[idx_z] = range_meter * sinAlphaTablePtr[i] * mirror_factor;// copy z value in pointcloud
+
+                                    fptr[idx_intensity] = 0.0;
+                                    if (config_.intensity)
+                                    {
+                                        int intensityIndex = aiValidEchoIdx[iEcho] * rangeNum + i;
+                                        // intensity values available??
+                                        if (intensityIndex < intensityTmpNum)
+                                        {
+                                            fptr[idx_intensity] = intensityTmpPtr[intensityIndex]; // copy intensity value in pointcloud
+                                        }
+                                    }
+                                    angle += msg.angle_increment;
+                                }
+                                // Publish
+                                static int cnt = 0;
+                                int layerOff = (layer - baseLayer);
+
+                            }
+                            // if ( (msg.header.seq == 0) || (layerOff == 0)) // FIXEN!!!!
+                            bool shallIFire = false;
+                            if ((msg.header.seq == 0) || (msg.header.seq == 237))
+                            {
+                                shallIFire = true;
+                            }
+
+
+                            static int layerCnt = 0;
+                            static int layerSeq[4];
+
+                            if (config_.cloud_output_mode > 0)
+                            {
+
+                                layerSeq[layerCnt % 4] = layer;
+                                if (layerCnt >= 4)  // mind. erst einmal vier Layer zusammensuchen
+                                {
+                                    shallIFire = true; // here are at least 4 layers available
+                                }
+                                else
+                                {
+                                    shallIFire = false;
+                                }
+
+                                layerCnt++;
+                            }
+
+                            if (shallIFire) // shall i fire the signal???
+                            {
+#ifdef ROSSIMU
+                                static int cnt = 0;
+                                cnt++;
+
+                                printf("PUBLISH_DATA:\n");
+
+                                unsigned char* cloudDataPtr = &(cloud_.data[0]);
+                                int w = cloud_.width;
+                                int h = cloud_.height;
+
+                                int numShots = w * h;
+
+                                float* ptr = (float*) cloudDataPtr;
+
+                                if (cnt == 25)
+                                {
+                                    char jpgFileName_tmp[255] = { 0 };
+#if linux
+                                    strcpy(jpgFileName_tmp, "./demo/scan.jpg_tmp");
+#else
+                                    strcpy(jpgFileName_tmp, "..\\demo\\scan.jpg_tmp");
+#endif
+                                    int xic = 400;
+                                    int yic = 400;
+                                    int w2i = 400;
+                                    int h2i = 400;
+                                    int hi = h2i * 2 + 1;
+                                    int wi = w2i * 2 + 1;
+                                    int pixNum = hi * wi;
+                                    int numColorChannel = 3;
+                                    unsigned char* pixel = (unsigned char*) malloc(numColorChannel * hi * wi);
+                                    memset(pixel, 0, numColorChannel * pixNum);
+                                    double scaleFac = 50.0;
+
+                                    for (int i = 0; i < hi; i++)
+                                    {
+                                        int pixAdr = numColorChannel * (i * wi + xic);
+                                        pixel[pixAdr] = 0x40;
+                                        pixel[pixAdr + 1] = 0x40;
+                                        pixel[pixAdr + 2] = 0x40;
+                                    }
+                                    for (int i = 0; i < wi; i++)
+                                    {
+                                        int pixAdr = numColorChannel * (yic * wi + i);
+                                        pixel[pixAdr] = 0x40;
+                                        pixel[pixAdr + 1] = 0x40;
+                                        pixel[pixAdr + 2] = 0x40;
+                                    }
+
+                                    scaleFac *= -1.0;
+                                    for (int i = 0; i < numShots; i++)
+                                    {
+                                        double x, y, z, intensity;
+                                        x = ptr[0];
+                                        y = ptr[1];
+                                        z = ptr[2];
+                                        intensity = ptr[3];
+                                        ptr += 4;
+                                        int xi = (x * scaleFac) + xic;
+                                        int yi = (y * scaleFac) + yic;
+                                        if ((xi >= 0) && (xi < wi))
+                                        {
+                                            if ((yi >= 0) && (xi < hi))
+                                            {
+                                                // yi shows left (due to neg. scaleFac)
+                                                // xi shows up (due to neg. scaleFac)
+                                                int pixAdr = numColorChannel * (xi * wi + yi);
+                                                int layer = i / w;
+                                                unsigned char color[3] = {0x00};
+                                                switch (layer)
+                                                {
+                                                    case 0:
+                                                        color[0] = 0xFF;
+                                                        break;
+                                                    case 1:
+                                                        color[1] = 0xFF;
+                                                        break;
+                                                    case 2:
+                                                        color[2] = 0xFF;
+                                                        break;
+                                                    case 3:
+                                                        color[0] = 0xFF;
+                                                        color[1] = 0xFF;
+                                                        break;
+                                                }
+
+                                                for (int kk = 0; kk < 3; kk++)
+                                                {
+                                                    pixel[pixAdr + kk] = color[kk];
+
+                                                }
+                                            }
+                                        }
+
+                                    }
+
+
+
+                                    // Write JPEG Scan Top View
+                                    foutJpg = fopen(jpgFileName_tmp, "wb");
+                                    if (foutJpg == NULL)
+                                    {
+                                        ROS_INFO("PANIC: Can not open %s for writing. Check existience of demo dir. or patch  code.\n", jpgFileName_tmp);
+                                    }
+                                    else
+                                    {
+                                        TooJpeg::writeJpeg(jpegOutputCallback, pixel, wi, hi, true, 99);
+                                        fclose(foutJpg);
+
+                                        free(pixel);
+
+#if linux
+                                        rename(jpgFileName_tmp, "./demo/scan.jpg");
+#else
+                                        _unlink("..\\demo\\scan.jpg");
+                                        rename(jpgFileName_tmp, "..\\demo\\scan.jpg");
+#endif
+
+                                    }
+                                    // Write CSV-File
+                                    char csvFileNameTmp[255];
+#if linux
+                                    strcpy(csvFileNameTmp, "./demo/scan.csv_tmp");
+#else
+                                    strcpy(csvFileNameTmp, "..\\demo\\scan.csv_tmp");
+#endif
+                                    FILE* foutCsv = fopen(csvFileNameTmp, "w");
+                                    if (foutCsv)
+                                    {
+                                        // ZIEL: fprintf(foutCsv,"timestamp;range;elevation;azimuth;x;y;z;intensity\n");
+                                        fprintf(foutCsv, "timestamp_sec;timestamp_nanosec;range;azimuth_deg;elevation_deg;x;y;z;intensity\n");
+                                        unsigned char* cloudDataPtr = &(cloud_.data[0]);
+
+                                        int numShots = w * h;
+
+                                        float* ptr = (float*) cloudDataPtr;
+
+
+                                        long timestamp_sec = cloud_.header.stamp.sec;
+                                        long timestamp_nanosec = cloud_.header.stamp.nsec;
+                                        for (int i = 0; i < numShots; i++)
+                                        {
+                                            double x, y, z, intensity;
+                                            x = ptr[0];
+                                            y = ptr[1];
+                                            z = ptr[2];
+                                            float range_xy = sqrt(x * x + y * y);
+                                            float range_xyz = sqrt(x * x + y * y + z * z);
+                                            float elevation = atan2(z, range_xy);
+                                            float azimuth = atan2(y, x);
+                                            float elevationDeg = elevation * 180.0 / M_PI;
+                                            float azimuthDeg = azimuth * 180.0 / M_PI;
+
+                                            intensity = ptr[3];
+                                            ptr += 4;
+                                            fprintf(foutCsv, "%12ld;%12ld;%8.3lf;%8.3lf;%8.3lf;%8.3f;%8.3f;%8.3f;%8.0f\n", timestamp_sec, timestamp_nanosec, range_xyz, azimuthDeg, elevationDeg, x, y, z, intensity);
+                                        }
+                                        fclose(foutCsv);
+#ifdef linux
+                                        rename(csvFileNameTmp, "./demo/scan.csv");
+#else
+                                        _unlink("..\\demo\\scan.csv");
+                                        rename(csvFileNameTmp, "..\\demo\\scan.csv");
+#endif
+                                    }
+                                    else
+                                    {
+                                        ROS_INFO("PANIC: Can not open %s for writing. Check existience of demo dir. or patch  code.\n", csvFileNameTmp);
+                                    }
+                                    cnt = 0;
+                                }
+
+#else
+                                if (config_.cloud_output_mode == 0)
+                                {
+                                    // standard handling of scans
+                                    cloud_pub_.publish(cloud_);
+
+                                }
+
+                                //                cloud_pub_.publish(cloud_);
+#endif
+                            }
+                        }
+                    }
+                    // Start Point
+                    if (dend != NULL)
+                    {
+                        buffer_pos = dend + 1;
+                    }
+                } // end of while loop
+            }
+
+            // shall we process more data? I.e. are there more packets to process in the input queue???
+
+        }
+        while ((packetsInLoop > 0) && (numPacketsProcessed < maxNumAllowedPacketsToProcess));
+        return sick_scan::ExitSuccess; // return success to continue looping
+    }
+
+    void SickScanAdapter::disconnectFunction()
+    {
+
+    }
+
+    void SickScanAdapter::disconnectFunctionS(void* obj)
+    {
+        if (obj != NULL)
+        {
+            ((SickScanAdapter*)(obj))->disconnectFunction();
+        }
+    }
+
+    void SickScanAdapter::readCallbackFunctionS(void* obj, UINT8* buffer, UINT32& numOfBytes)
+    {
+        ((SickScanAdapter*) obj)->readCallbackFunction(buffer, numOfBytes);
+    }
+
+
+    void SickScanAdapter::setReplyMode(int _mode)
+    {
+        m_replyMode = _mode;
+    }
+
+    int SickScanAdapter::getReplyMode()
+    {
+        return (m_replyMode);
+    }
+
+#if 0
+    void SickScanAdapter::setProtocolType(char cola_dialect_id)
+    {
+        if ((cola_dialect_id == 'a') || (cola_dialect_id == 'A'))
+        {
+            this->m_protocol = CoLa_A;
+        }
+        else
+        {
+            this->m_protocol = CoLa_B;
+        }
+    }
+#endif
+
+    /*!
+    \brief Set emulation flag (using emulation instead of "real" scanner - currently implemented for radar
+    \param _emulFlag: Flag to switch emulation on or off
+    \return
+    */
+    void SickScanAdapter::setEmulSensor(bool _emulFlag)
+    {
+        m_emulSensor = _emulFlag;
+    }
+
+    /*!
+    \brief get emulation flag (using emulation instead of "real" scanner - currently implemented for radar
+    \param
+    \return bool: Flag to switch emulation on or off
+    */
+    bool SickScanAdapter::getEmulSensor()
+    {
+        return (m_emulSensor);
+    }
+
+    //
+    // Look for 23-frame (STX/ETX) in receive buffer.
+    // Move frame to start of buffer
+    //
+    // Return: 0 : No (complete) frame found
+    //        >0 : Frame length
+    //
+    SopasEventMessage SickScanAdapter::findFrameInReceiveBuffer()
+    {
+        UINT32 frameLen = 0;
+        UINT32 i;
+
+        // Depends on protocol...
+        if (getProtocolType() == CoLa_A)
+        {
+            //
+            // COLA-A
+            //
+            // Must start with STX (0x02)
+            if (m_receiveBuffer[0] != 0x02)
+            {
+                // Look for starting STX (0x02)
+                for (i = 1; i < m_numberOfBytesInReceiveBuffer; i++)
+                {
+                    if (m_receiveBuffer[i] == 0x02)
+                    {
+                        break;
+                    }
+                }
+
+                // Found beginning of frame?
+                if (i >= m_numberOfBytesInReceiveBuffer)
+                {
+                    // No start found, everything can be discarded
+                    m_numberOfBytesInReceiveBuffer = 0; // Invalidate buffer
+                    return SopasEventMessage(); // No frame found
+                }
+
+                // Move frame start to index 0
+                UINT32 newLen = m_numberOfBytesInReceiveBuffer - i;
+                memmove(&(m_receiveBuffer[0]), &(m_receiveBuffer[i]), newLen);
+                m_numberOfBytesInReceiveBuffer = newLen;
+            }
+
+            // Look for ending ETX (0x03)
+            for (i = 1; i < m_numberOfBytesInReceiveBuffer; i++)
+            {
+                if (m_receiveBuffer[i] == 0x03)
+                {
+                    break;
+                }
+            }
+
+            // Found end?
+            if (i >= m_numberOfBytesInReceiveBuffer)
+            {
+                // No end marker found, so it's not a complete frame (yet)
+                return SopasEventMessage(); // No frame found
+            }
+
+            // Calculate frame length in byte
+            frameLen = i + 1;
+
+            return SopasEventMessage(m_receiveBuffer, CoLa_A, frameLen);
+        }
+        else if (getProtocolType() == CoLa_B)
+        {
+            UINT32 magicWord;
+            UINT32 payloadlength;
+
+            if (m_numberOfBytesInReceiveBuffer < 4)
+            {
+                return SopasEventMessage();
+            }
+            UINT16 pos = 0;
+            magicWord = colab::getIntegerFromBuffer<UINT32>(m_receiveBuffer, pos);
+            if (magicWord != 0x02020202)
+            {
+                // Look for starting STX (0x02020202)
+                for (i = 1; i <= m_numberOfBytesInReceiveBuffer - 4; i++)
+                {
+                    pos = i; // this is needed, as the position value is updated by getIntegerFromBuffer
+                    magicWord = colab::getIntegerFromBuffer<UINT32>(m_receiveBuffer, pos);
+                    if (magicWord == 0x02020202)
+                    {
+                        // found magic word
+                        break;
+                    }
+                }
+
+                // Found beginning of frame?
+                if (i > m_numberOfBytesInReceiveBuffer - 4)
+                {
+                    // No start found, everything can be discarded
+                    m_numberOfBytesInReceiveBuffer = 0; // Invalidate buffer
+                    return SopasEventMessage(); // No frame found
+                }
+                else
+                {
+                    // Move frame start to index
+                    UINT32 bytesToMove = m_numberOfBytesInReceiveBuffer - i;
+                    memmove(&(m_receiveBuffer[0]), &(m_receiveBuffer[i]), bytesToMove); // payload+magic+length+s+checksum
+                    m_numberOfBytesInReceiveBuffer = bytesToMove;
+                }
+            }
+
+            // Pruefe Laenge des Pufferinhalts
+            if (m_numberOfBytesInReceiveBuffer < 9)
+            {
+                // Es sind nicht genug Daten fuer einen Frame
+                printInfoMessage("SickScanCommonNw::findFrameInReceiveBuffer: Frame cannot be decoded yet, only " +
+                                 ::toString(m_numberOfBytesInReceiveBuffer) + " bytes in the buffer.", m_beVerbose);
+                return SopasEventMessage();
+            }
+
+            // Read length of payload
+            pos = 4;
+            payloadlength = colab::getIntegerFromBuffer<UINT32>(m_receiveBuffer, pos);
+            printInfoMessage(
+                "SickScanCommonNw::findFrameInReceiveBuffer: Decoded payload length is " + ::toString(payloadlength) +
+                " bytes.", m_beVerbose);
+
+            // Ist die Datenlaenge plausibel und wuede in den Puffer passen?
+            if (payloadlength > (sizeof(m_receiveBuffer) - 9))
+            {
+                // magic word + length + checksum = 9
+                printWarning(
+                    "SickScanCommonNw::findFrameInReceiveBuffer: Frame too big for receive buffer. Frame discarded with length:"
+                    + ::toString(payloadlength) + ".");
+                m_numberOfBytesInReceiveBuffer = 0;
+                return SopasEventMessage();
+            }
+            if ((payloadlength + 9) > m_numberOfBytesInReceiveBuffer)
+            {
+                // magic word + length + s + checksum = 10
+                printInfoMessage(
+                    "SickScanCommonNw::findFrameInReceiveBuffer: Frame not complete yet. Waiting for the rest of it (" +
+                    ::toString(payloadlength + 9 - m_numberOfBytesInReceiveBuffer) + " bytes missing).", m_beVerbose);
+                return SopasEventMessage(); // frame not complete
+            }
+
+            // Calculate the total frame length in bytes: Len = Frame (9 bytes) + Payload
+            frameLen = payloadlength + 9;
+
+            //
+            // test checksum of payload
+            //
+            UINT8 temp = 0;
+            UINT8 temp_xor = 0;
+            UINT8 checkSum;
+
+            // Read original checksum
+            pos = frameLen - 1;
+            checkSum = colab::getIntegerFromBuffer<UINT8>(m_receiveBuffer, pos);
+
+            // Erzeuge die Pruefsumme zum Vergleich
+            for (UINT16 i = 8; i < (frameLen - 1); i++)
+            {
+                pos = i;
+                temp = colab::getIntegerFromBuffer<UINT8>(m_receiveBuffer, pos);
+                temp_xor = temp_xor ^ temp;
+            }
+
+            // Vergleiche die Pruefsummen
+            if (temp_xor != checkSum)
+            {
+                printWarning("SickScanCommonNw::findFrameInReceiveBuffer: Wrong checksum, Frame discarded.");
+                m_numberOfBytesInReceiveBuffer = 0;
+                return SopasEventMessage();
+            }
+
+            return SopasEventMessage(m_receiveBuffer, CoLa_B, frameLen);
+        }
+
+        // Return empty frame
+        return SopasEventMessage();
+    }
+
+
+    /**
+    * Read callback. Diese Funktion wird aufgerufen, sobald Daten auf der Schnittstelle
+    * hereingekommen sind.
+    */
+
+    void SickScanAdapter::processFrame(ros::Time timeStamp, SopasEventMessage& frame)
+    {
+
+        if (getProtocolType() == CoLa_A)
+        {
+            printInfoMessage(
+                "SickScanCommonNw::processFrame: Calling processFrame_CoLa_A() with " + ::toString(frame.size()) + " bytes.",
+                m_beVerbose);
+            // processFrame_CoLa_A(frame);
+        }
+        else if (getProtocolType() == CoLa_B)
+        {
+            printInfoMessage(
+                "SickScanCommonNw::processFrame: Calling processFrame_CoLa_B() with " + ::toString(frame.size()) + " bytes.",
+                m_beVerbose);
+            // processFrame_CoLa_B(frame);
+        }
+
+        // Push frame to recvQueue
+
+        DatagramWithTimeStamp dataGramWidthTimeStamp(timeStamp, std::vector<unsigned char>(frame.getRawData(),
+                frame.getRawData() +
+                frame.size()));
+        // recvQueue.push(std::vector<unsigned char>(frame.getRawData(), frame.getRawData() + frame.size()));
+        recvQueue.push(dataGramWidthTimeStamp);
+    }
+
+    void SickScanAdapter::readCallbackFunction(UINT8* buffer, UINT32& numOfBytes)
+    {
+        ros::Time rcvTimeStamp = ros::Time::now(); // stamp received datagram
+        bool beVerboseHere = false;
+        printInfoMessage(
+            "SickScanCommonNw::readCallbackFunction(): Called with " + toString(numOfBytes) + " available bytes.",
+            beVerboseHere);
+
+        ScopedLock lock(&m_receiveDataMutex); // Mutex for access to the input buffer
+        UINT32 remainingSpace = sizeof(m_receiveBuffer) - m_numberOfBytesInReceiveBuffer;
+        UINT32 bytesToBeTransferred = numOfBytes;
+        if (remainingSpace < numOfBytes)
+        {
+            bytesToBeTransferred = remainingSpace;
+            // printWarning("SickScanCommonNw::readCallbackFunction(): Input buffer space is to small, transferring only " +
+            //              ::toString(bytesToBeTransferred) + " of " + ::toString(numOfBytes) + " bytes.");
+        }
+        else
+        {
+            // printInfoMessage("SickScanCommonNw::readCallbackFunction(): Transferring " + ::toString(bytesToBeTransferred) +
+            //                   " bytes from TCP to input buffer.", beVerboseHere);
+        }
+
+        if (bytesToBeTransferred > 0)
+        {
+            // Data can be transferred into our input buffer
+            memcpy(&(m_receiveBuffer[m_numberOfBytesInReceiveBuffer]), buffer, bytesToBeTransferred);
+            m_numberOfBytesInReceiveBuffer += bytesToBeTransferred;
+
+            UINT32 size = 0;
+
+            while (1)
+            {
+                // Now work on the input buffer until all received datasets are processed
+                SopasEventMessage frame = findFrameInReceiveBuffer();
+
+                size = frame.size();
+                if (size == 0)
+                {
+                    // Framesize = 0: There is no valid frame in the buffer. The buffer is either empty or the frame
+                    // is incomplete, so leave the loop
+                    printInfoMessage("SickScanCommonNw::readCallbackFunction(): No complete frame in input buffer, we are done.",
+                                     beVerboseHere);
+
+                    // Leave the loop
+                    break;
+                }
+                else
+                {
+                    // A frame was found in the buffer, so process it now.
+                    printInfoMessage(
+                        "SickScanCommonNw::readCallbackFunction(): Processing a frame of length " + ::toString(frame.size()) +
+                        " bytes.", beVerboseHere);
+                    processFrame(rcvTimeStamp, frame);
+                    UINT32 bytesToMove = m_numberOfBytesInReceiveBuffer - size;
+                    memmove(&(m_receiveBuffer[0]), &(m_receiveBuffer[size]), bytesToMove); // payload+magic+length+s+checksum
+                    m_numberOfBytesInReceiveBuffer = bytesToMove;
+
+                }
+            }
+        }
+        else
+        {
+            // There was input data from the TCP interface, but our input buffer was unable to hold a single byte.
+            // Either we have not read data from our buffer for a long time, or something has gone wrong. To re-sync,
+            // we clear the input buffer here.
+            m_numberOfBytesInReceiveBuffer = 0;
+        }
+    }
+
+
+    int SickScanAdapter::init_device()
+    {
+        int portInt;
+        sscanf(port_.c_str(), "%d", &portInt);
+        m_nw.init(hostname_, portInt, disconnectFunctionS, (void*) this);
+        m_nw.setReadCallbackFunction(readCallbackFunctionS, (void*) this);
+        if (this->getEmulSensor())
+        {
+            ROS_INFO("Sensor emulation is switched on - network traffic is switched off.");
+        }
+        else
+        {
+            m_nw.connect();
+        }
+        return sick_scan::ExitSuccess;
+    }
+
+    int SickScanAdapter::close_device()
+    {
+        ROS_WARN("Disconnecting TCP-Connection.");
+        m_nw.disconnect();
+        return 0;
+    }
+
+
+    bool SickScanAdapter::stopScanData()
+    {
+        stop_scanner();
+        return (true);
+    }
+
+    void SickScanAdapter::handleRead(boost::system::error_code error, size_t bytes_transfered)
+    {
+        ec_ = error;
+        bytes_transfered_ += bytes_transfered;
+    }
+
+
+    void SickScanAdapter::checkDeadline()
+    {
+        if (deadline_.expires_at() <= boost::asio::deadline_timer::traits_type::now())
+        {
+            // The reason the function is called is that the deadline expired. Close
+            // the socket to return all IO operations and reset the deadline
+            socket_.close();
+            deadline_.expires_at(boost::posix_time::pos_infin);
+        }
+
+        // Nothing bad happened, go back to sleep
+        deadline_.async_wait(boost::bind(&SickScanAdapter::checkDeadline, this));
+    }
+
+
+    int SickScanAdapter::numberOfDatagramInInputFifo()
+    {
+        int numVal = this->recvQueue.getNumberOfEntriesInQueue();
+        return (numVal);
+    }
+
+    int SickScanAdapter::readWithTimeout(size_t timeout_ms, char* buffer, int buffer_size, int* bytes_read,
+                                         bool* exception_occured, bool isBinary)
+    {
+        // Set up the deadline to the proper timeout, error and delimiters
+        deadline_.expires_from_now(boost::posix_time::milliseconds(timeout_ms));
+        const char end_delim = static_cast<char>(0x03);
+        int dataLen = 0;
+        ec_ = boost::asio::error::would_block;
+        bytes_transfered_ = 0;
+
+        size_t to_read;
+
+        int numBytes = 0;
+        // Polling - should be changed to condition variable in the future
+        int waitingTimeInMs = 1; // try to lookup for new incoming packages
+        size_t i;
+        for (i = 0; i < timeout_ms; i += waitingTimeInMs)
+        {
+            if (false == this->recvQueue.isQueueEmpty())
+            {
+                break;
+            }
+            boost::this_thread::sleep(boost::posix_time::milliseconds(waitingTimeInMs));
+        }
+        if (i >= timeout_ms)
+        {
+            ROS_ERROR("no answer received after %zu ms. Maybe sopas mode is wrong.\n", timeout_ms);
+            return (sick_scan::ExitError);
+        }
+        boost::condition_variable cond_;
+        DatagramWithTimeStamp datagramWithTimeStamp = this->recvQueue.pop();
+
+        *bytes_read = datagramWithTimeStamp.datagram.size();
+        memcpy(buffer, &(datagramWithTimeStamp.datagram[0]), datagramWithTimeStamp.datagram.size());
+        return (sick_scan::ExitSuccess);
+    }
+
+    /**
+     * Send a SOPAS command to the device and print out the response to the console.
+     */
+    int SickScanAdapter::sendSOPASCommand(const char* request, std::vector<unsigned char>* reply, int cmdLen)
+    {
+#if 0
+        if (!socket_.is_open())
+        {
+            ROS_ERROR("sendSOPASCommand: socket not open");
+            diagnostics_.broadcast(getDiagnosticErrorCode(), "sendSOPASCommand: socket not open.");
+            return sick_scan::ExitError;
+        }
+#endif
+        int sLen = 0;
+        int preambelCnt = 0;
+        bool cmdIsBinary = false;
+
+        if (request != NULL)
+        {
+            sLen = cmdLen;
+            preambelCnt = 0; // count 0x02 bytes to decide between ascii and binary command
+            if (sLen >= 4)
+            {
+                for (int i = 0; i < 4; i++)
+                {
+                    if (request[i] == 0x02)
+                    {
+                        preambelCnt++;
+                    }
+                }
+            }
+
+            if (preambelCnt < 4)
+            {
+                cmdIsBinary = false;
+            }
+            else
+            {
+                cmdIsBinary = true;
+            }
+            int msgLen = 0;
+            if (cmdIsBinary == false)
+            {
+                msgLen = strlen(request);
+            }
+            else
+            {
+                int dataLen = 0;
+                for (int i = 4; i < 8; i++)
+                {
+                    dataLen |= ((unsigned char) request[i] << (7 - i) * 8);
+                }
+                msgLen = 8 + dataLen + 1; // 8 Msg. Header + Packet +
+            }
+#if 1
+            if (getEmulSensor())
+            {
+                emulateReply((UINT8*) request, msgLen, reply);
+            }
+            else
+            {
+                bool debugBinCmd = false;
+                if (debugBinCmd)
+                {
+                    printf("=== START HEX DUMP ===\n");
+                    for (int i = 0; i < msgLen; i++)
+                    {
+                        unsigned char* ptr = (UINT8*) request;
+                        printf("%02x ", ptr[i]);
+                    }
+                    printf("\n=== END HEX DUMP ===\n");
+                }
+                m_nw.sendCommandBuffer((UINT8*) request, msgLen);
+            }
+#else
+
+            /*
+             * Write a SOPAS variable read request to the device.
+             */
+            try
+            {
+                boost::asio::write(socket_, boost::asio::buffer(request, msgLen));
+            }
+            catch (boost::system::system_error& e)
+            {
+                ROS_ERROR("write error for command: %s", request);
+                diagnostics_.broadcast(getDiagnosticErrorCode(), "Write error for sendSOPASCommand.");
+                return sick_scan::ExitError;
+            }
+#endif
+        }
+
+        // Set timeout in 5 seconds
+        const int BUF_SIZE = 65536;
+        char buffer[BUF_SIZE];
+        int bytes_read;
+        // !!!
+        if (getEmulSensor())
+        {
+
+        }
+        else
+        {
+            if (readWithTimeout(getReadTimeOutInMs(), buffer, BUF_SIZE, &bytes_read, 0, cmdIsBinary) == sick_scan::ExitError)
+            {
+                ROS_INFO_THROTTLE(1.0, "sendSOPASCommand: no full reply available for read after %d ms", getReadTimeOutInMs());
+                diagnostics_.broadcast(getDiagnosticErrorCode(),
+                                       "sendSOPASCommand: no full reply available for read after timeout.");
+                return sick_scan::ExitError;
+            }
+
+
+            if (reply)
+            {
+                reply->resize(bytes_read);
+
+                std::copy(buffer, buffer + bytes_read, &(*reply)[0]);
+            }
+        }
+        return sick_scan::ExitSuccess;
+    }
+
+
+    int SickScanAdapter::get_datagram(ros::Time& recvTimeStamp, unsigned char* receiveBuffer, int bufferSize,
+                                      int* actual_length,
+                                      bool isBinaryProtocol, int* numberOfRemainingFifoEntries)
+    {
+        if (NULL != numberOfRemainingFifoEntries)
+        {
+            *numberOfRemainingFifoEntries = 0;
+        }
+        this->setReplyMode(1);
+
+        if (this->getEmulSensor())
+        {
+#ifndef ROSSIMU
+            // boost::this_thread::sleep(boost::posix_time::milliseconds(waitingTimeInMs));
+            ros::Time timeStamp = ros::Time::now();
+            uint32_t nanoSec = timeStamp.nsec;
+            double waitTime10Hz = 10.0 * (double) nanoSec / 1E9;  // 10th of sec. [0..10[
+
+            uint32_t waitTime = (int) waitTime10Hz; // round down
+
+            double waitTimeUntilNextTime10Hz = 1 / 10.0 * (1.0 - (waitTime10Hz - waitTime));
+
+            ros::Duration(waitTimeUntilNextTime10Hz).sleep();
+            SickScanRadar radar(this);
+            radar.setEmulation(true);
+            radar.simulateAsciiDatagram(receiveBuffer, actual_length);
+            recvTimeStamp = ros::Time::now();
+#endif
+        }
+        else
+        {
+            const int maxWaitInMs = getReadTimeOutInMs();
+            std::vector<unsigned char> dataBuffer;
+#if 1 // prepared for reconnect
+            bool retVal = this->recvQueue.waitForIncomingObject(maxWaitInMs);
+            if (retVal == false)
+            {
+                ROS_WARN("Timeout during waiting for new datagram");
+                return sick_scan::ExitError;
+            }
+            else
+            {
+                // Look into receiving queue for new Datagrams
+                //
+                //
+                DatagramWithTimeStamp datagramWithTimeStamp = this->recvQueue.pop();
+                if (NULL != numberOfRemainingFifoEntries)
+                {
+                    *numberOfRemainingFifoEntries = this->recvQueue.getNumberOfEntriesInQueue();
+                }
+                recvTimeStamp = datagramWithTimeStamp.timeStamp;
+                dataBuffer = datagramWithTimeStamp.datagram;
+
+            }
+#endif
+            // dataBuffer = this->recvQueue.pop();
+            long size = dataBuffer.size();
+            memcpy(receiveBuffer, &(dataBuffer[0]), size);
+            *actual_length = size;
+        }
+
+#if 0
+        static int cnt = 0;
+        char szFileName[255];
+        sprintf(szFileName, "/tmp/dg%06d.bin", cnt++);
+
+        FILE* fout;
+
+        fout = fopen(szFileName, "wb");
+        if (fout != NULL)
+        {
+            fwrite(receiveBuffer, size, 1, fout);
+            fclose(fout);
+        }
+#endif
+        return sick_scan::ExitSuccess;
+
+        if (!socket_.is_open())
+        {
+            ROS_ERROR("get_datagram: socket not open");
+            diagnostics_.broadcast(getDiagnosticErrorCode(), "get_datagram: socket not open.");
+            return sick_scan::ExitError;
+        }
+
+        /*
+         * Write a SOPAS variable read request to the device.
+         */
+        std::vector<unsigned char> reply;
+
+        // Wait at most 5000ms for a new scan
+        size_t timeout = 30000;
+        bool exception_occured = false;
+
+        char* buffer = reinterpret_cast<char*>(receiveBuffer);
+
+        if (readWithTimeout(timeout, buffer, bufferSize, actual_length, &exception_occured, isBinaryProtocol) !=
+            sick_scan::ExitSuccess)
+        {
+            ROS_ERROR_THROTTLE(1.0, "get_datagram: no data available for read after %zu ms", timeout);
+            diagnostics_.broadcast(getDiagnosticErrorCode(), "get_datagram: no data available for read after timeout.");
+
+            // Attempt to reconnect when the connection was terminated
+            if (!socket_.is_open())
+            {
+#ifdef _MSC_VER
+                Sleep(1000);
+#else
+                sleep(1);
+#endif
+                ROS_INFO("Failure - attempting to reconnect");
+                return init();
+            }
+
+            return exception_occured ? sick_scan::ExitError : sick_scan::ExitSuccess;    // keep on trying
+        }
+
+        return sick_scan::ExitSuccess;
+    }
+
+}
diff --git a/source/RobotAPI/drivers/SickLaserUnit/SickScanAdapter.h b/source/RobotAPI/drivers/SickLaserUnit/SickScanAdapter.h
new file mode 100644
index 000000000..08daf8276
--- /dev/null
+++ b/source/RobotAPI/drivers/SickLaserUnit/SickScanAdapter.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2013, Freiburg University
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Osnabrück University nor the names of its
+ *       contributors may be used to endorse or promote products derived from
+ *       this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  Created on: 15.11.2013
+ *
+ *      Authors:
+ *         Christian Dornhege <c.dornhege@googlemail.com>
+ */
+
+#ifndef SICK_TIM3XX_COMMON_TCP_H
+#define SICK_TIM3XX_COMMON_TCP_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <boost/asio.hpp>
+
+#undef NOMINMAX // to get rid off warning C4005: "NOMINMAX": Makro-Neudefinition
+
+#include <sick_scan/sick_scan_common.h>
+#include <sick_scan/sick_generic_parser.h>
+#include <sick_scan/template_queue.h>
+
+namespace armarx
+{
+    /* class prepared for optimized time stamping */
+
+    class DatagramWithTimeStamp
+    {
+    public:
+        DatagramWithTimeStamp(ros::Time timeStamp_, std::vector<unsigned char> datagram_)
+        {
+            timeStamp = timeStamp_;
+            datagram = datagram_;
+        }
+
+        // private:
+        ros::Time timeStamp;
+        std::vector<unsigned char> datagram;
+    };
+
+
+    class SickScanAdapter : public sick_scan::SickScanCommon
+    {
+    public:
+        int loopOnce();
+
+        static void disconnectFunctionS(void* obj);
+
+        SickScanAdapter(const std::string& hostname, const std::string& port, int& timelimit, sick_scan::SickGenericParser* parser,
+                        char cola_dialect_id);
+
+        virtual ~SickScanAdapter();
+
+        static void readCallbackFunctionS(void* obj, UINT8* buffer, UINT32& numOfBytes);
+
+        void readCallbackFunction(UINT8* buffer, UINT32& numOfBytes);
+
+        void setReplyMode(int _mode);
+
+        int getReplyMode();
+
+        void setEmulSensor(bool _emulFlag);
+
+        bool getEmulSensor();
+
+        bool stopScanData();
+
+        int numberOfDatagramInInputFifo();
+
+        SopasEventMessage findFrameInReceiveBuffer();
+
+        void processFrame(ros::Time timeStamp, SopasEventMessage& frame);
+
+        // Queue<std::vector<unsigned char> > recvQueue;
+        Queue<DatagramWithTimeStamp> recvQueue;
+        UINT32 m_alreadyReceivedBytes;
+        UINT32 m_lastPacketSize;
+        UINT8 m_packetBuffer[480000];
+        /**
+         * Read callback. Diese Funktion wird aufgerufen, sobald Daten auf der Schnittstelle
+         * hereingekommen sind.
+         */
+
+    protected:
+        void disconnectFunction();
+
+        void readCallbackFunctionOld(UINT8* buffer, UINT32& numOfBytes);
+
+        virtual int init_device();
+
+        virtual int close_device();
+
+        /// Send a SOPAS command to the device and print out the response to the console.
+        virtual int sendSOPASCommand(const char* request, std::vector<unsigned char>* reply, int cmdLen);
+
+        /// Read a datagram from the device.
+        /**
+         * \param [out] recvTimeStamp timestamp of received datagram
+         * \param [in] receiveBuffer data buffer to fill
+         * \param [in] bufferSize max data size to write to buffer (result should be 0 terminated)
+         * \param [out] actual_length the actual amount of data written
+         * \param [in] isBinaryProtocol true=binary False=ASCII
+         */
+        virtual int
+        get_datagram(ros::Time& recvTimeStamp, unsigned char* receiveBuffer, int bufferSize, int* actual_length,
+                     bool isBinaryProtocol, int* numberOfRemainingFifoEntries);
+
+        // Helpers for boost asio
+        int readWithTimeout(size_t timeout_ms, char* buffer, int buffer_size, int* bytes_read = 0,
+                            bool* exception_occured = 0, bool isBinary = false);
+
+        void handleRead(boost::system::error_code error, size_t bytes_transfered);
+
+        void checkDeadline();
+
+    private:
+
+
+        // Response buffer
+        UINT32 m_numberOfBytesInResponseBuffer; ///< Number of bytes in buffer
+        UINT8 m_responseBuffer[1024]; ///< Receive buffer for everything except scan data and eval case data.
+        Mutex m_receiveDataMutex; ///< Access mutex for buffer
+
+        // Receive buffer
+        UINT32 m_numberOfBytesInReceiveBuffer; ///< Number of bytes in buffer
+        UINT8 m_receiveBuffer[480000]; ///< Low-Level receive buffer for all data
+
+        bool m_beVerbose;
+        bool m_emulSensor;
+
+        boost::asio::io_service io_service_;
+        boost::asio::ip::tcp::socket socket_;
+        boost::asio::deadline_timer deadline_;
+        boost::asio::streambuf input_buffer_;
+        boost::system::error_code ec_;
+        size_t bytes_transfered_;
+
+        std::string hostname_;
+        std::string port_;
+        int timelimit_;
+        int m_replyMode;
+    };
+
+}
+#endif /* SICK_TIM3XX_COMMON_TCP_H */
+
-- 
GitLab