rosaria/Legacy/Aria/src/ArLaser.cpp
2021-12-16 14:07:59 +00:00

1402 lines
40 KiB
C++

/*
Adept MobileRobots Robotics Interface for Applications (ARIA)
Copyright (C) 2004, 2005 ActivMedia Robotics LLC
Copyright (C) 2006, 2007, 2008, 2009, 2010 MobileRobots Inc.
Copyright (C) 2011, 2012, 2013 Adept Technology
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
If you wish to redistribute ARIA under different terms, contact
Adept MobileRobots for information about a commercial version of ARIA at
robots@mobilerobots.com or
Adept MobileRobots, 10 Columbia Drive, Amherst, NH 03031; +1-603-881-7960
*/
#include "ArExport.h"
#include "ariaOSDef.h"
#include "ArLaser.h"
#include "ArRobot.h"
#include "ArDeviceConnection.h"
bool ArLaser::ourUseSimpleNaming = false;
AREXPORT ArLaser::ArLaser(
int laserNumber, const char *name,
unsigned int absoluteMaxRange, bool locationDependent,
bool appendLaserNumberToName) :
ArRangeDeviceThreaded(
361, 200, name, absoluteMaxRange,
0, 0, 0, locationDependent)
{
myLaserNumber = laserNumber;
if (appendLaserNumberToName)
{
char buf[1024];
snprintf(buf, sizeof(buf) - 20, "%s", name);
sprintf(buf, "%s_%d", buf, myLaserNumber);
myName = buf;
}
else
{
if (laserNumber != 1)
ArLog::log(ArLog::Verbose, "ArLaser::%s: Laser created with number %d, but the number is not appended to the name which may break things (especially since this number is greater than 1_", name, laserNumber);
myName = name;
}
laserSetName(myName.c_str());
myAbsoluteMaxRange = absoluteMaxRange;
myMaxRangeSet = false;
setSensorPosition(0, 0, 0, 0);
myTimeoutSeconds = 8;
myHaveSensorPose = false;
myFlipped = false;
myFlippedSet = false;
myCanSetDegrees = false;
myStartDegreesMin = HUGE_VAL;
myStartDegreesMax = -HUGE_VAL;
myStartDegreesSet = false;
myStartDegrees = 0;
myEndDegreesMin = HUGE_VAL;
myEndDegreesMax = -HUGE_VAL;
myEndDegreesSet = false;
myEndDegrees = 0;
myCanChooseDegrees = false;
myDegreesChoiceDouble = -HUGE_VAL;
myCanSetIncrement = 0;
myIncrementMin = HUGE_VAL;
myIncrementMax = -HUGE_VAL;
myIncrementSet = false;
myIncrement = 0;
myCanChooseIncrement = false;
myIncrementChoiceDouble = -HUGE_VAL;
myCanChooseUnits = false;
myCanChooseReflectorBits = false;
myCanSetPowerControlled = false;
myPowerControlled = true;
myPowerControlledSet = false;
myCanChooseStartingBaud = false;
myCanChooseAutoBaud = false;
myDefaultTcpPort = 8102;
myInfoLogLevel = ArLog::Verbose;
myRobotRunningAndConnected = false;
}
AREXPORT ArLaser::~ArLaser()
{
}
/**
This can be used to set the name on mutexes and such to match the
laser's new name.
**/
AREXPORT void ArLaser::laserSetName(const char *name)
{
if (ourUseSimpleNaming)
{
myName = "Laser_";
char buf[1024];
sprintf(buf, "%d", myLaserNumber);
myName += buf;
}
else
{
myName = name;
}
myTask.setThreadName(myName.c_str());
myConnectCBList.setNameVar("%s::myConnectCBList", myName.c_str());
myFailedConnectCBList.setNameVar("%s::myFailedConnectCBList", myName.c_str());
myDisconnectOnErrorCBList.setNameVar(
"%s::myDisconnectOnErrorCBList", myName.c_str());
myDisconnectNormallyCBList.setNameVar(
"%s::myDisconnectNormallyCBList", myName.c_str());
myDataCBList.setNameVar("%s::myDataCBList", myName.c_str());
//myDataCBList.setLogging(false); // supress debug logging since it drowns out all other logging
}
AREXPORT void ArLaser::setMaxRange(unsigned int maxRange)
{
if (maxRange > myAbsoluteMaxRange)
{
ArLog::log(ArLog::Terse, "%s::setMaxRange: Tried to set the max range to %u which is above the absoluteMaxRange on the device of %d, capping it",
getName(), maxRange, getAbsoluteMaxRange());
ArRangeDevice::setMaxRange(myAbsoluteMaxRange);
}
else
ArRangeDevice::setMaxRange(maxRange);
myMaxRangeSet = true;
}
AREXPORT void ArLaser::setCumulativeBufferSize(size_t size)
{
ArRangeDevice::setCumulativeBufferSize(size);
myCumulativeBufferSizeSet = true;
}
AREXPORT void ArLaser::laserSetAbsoluteMaxRange(unsigned int absoluteMaxRange)
{
ArLog::log(myInfoLogLevel, "%s: Setting absolute max range to %u",
getName(), absoluteMaxRange);
myAbsoluteMaxRange = absoluteMaxRange;
setMaxRange(getMaxRange());
}
/**
Filter readings, moving them from the raw current buffer to
filtered current buffer (see ArRangeDevice), and then also to the
cumulative buffer.
This must be called for the laser subclass to work right.
This also calls the reading callbacks.
**/
void ArLaser::laserProcessReadings(void)
{
// if we have no readings... don't do anything
if (myRawReadings == NULL || myRawReadings->begin() == myRawReadings->end())
return;
std::list<ArSensorReading *>::iterator sensIt;
ArSensorReading *sReading;
double x, y;
double lastX = 0.0, lastY = 0.0;
//unsigned int i = 0;
ArTime len;
len.setToNow();
bool clean;
if (myCumulativeCleanInterval <= 0 ||
(myCumulativeLastClean.mSecSince() >
myCumulativeCleanInterval))
{
myCumulativeLastClean.setToNow();
clean = true;
}
else
{
clean = false;
}
myCurrentBuffer.setPoseTaken(myRawReadings->front()->getPoseTaken());
myCurrentBuffer.setEncoderPoseTaken(
myRawReadings->front()->getEncoderPoseTaken());
myCurrentBuffer.beginRedoBuffer();
// walk the buffer of all the readings and see if we want to add them
for (sensIt = myRawReadings->begin();
sensIt != myRawReadings->end();
++sensIt)
{
sReading = (*sensIt);
// if we have ignore readings then check them here
if (!myIgnoreReadings.empty() &&
(myIgnoreReadings.find(
(int) ceil(sReading->getSensorTh())) !=
myIgnoreReadings.end()) ||
myIgnoreReadings.find(
(int) floor(sReading->getSensorTh())) !=
myIgnoreReadings.end())
sReading->setIgnoreThisReading(true);
// see if the reading is valid
if (sReading->getIgnoreThisReading())
continue;
// if we have a max range then check it here...
if (myMaxRange != 0 &&
sReading->getRange() > myMaxRange)
{
sReading->setIgnoreThisReading(true);
}
// see if the reading is valid... this is set up this way so that
// max range readings can cancel out other readings, but will
// still be ignored other than that... ones ignored for other
// reasons were skipped above
if (sReading->getIgnoreThisReading())
{
internalProcessReading(sReading->getX(), sReading->getY(),
sReading->getRange(), clean, true);
continue;
}
// get our coords
x = sReading->getX();
y = sReading->getY();
// see if we're checking on the filter near dist... if we are
// and the reading is a good one we'll check the cumulative
// buffer
if (myMinDistBetweenCurrentSquared > 0.0000001)
{
// see where the last reading was
//squaredDist = (x-lastX)*(x-lastX) + (y-lastY)*(y-lastY);
// see if the reading is far enough from the last reading
if (ArMath::squaredDistanceBetween(x, y, lastX, lastY) >
myMinDistBetweenCurrentSquared)
{
lastX = x;
lastY = y;
// since it was a good reading, see if we should toss it in
// the cumulative buffer...
internalProcessReading(x, y, sReading->getRange(), clean, false);
/* we don't do this part anymore since it wound up leaving
// too many things not really tehre... if its outside of our
// sensor angle to use to filter then don't let this one
// clean (ArMath::fabs(sReading->getSensorTh()) > 50)
// filterAddAndCleanCumulative(x, y, false); else*/
}
// it wasn't far enough, skip this one and go to the next one
else
{
continue;
}
}
// we weren't filtering the readings, but see if it goes in the
// cumulative buffer anyways
else
{
internalProcessReading(x, y, sReading->getRange(), clean, false);
}
// now drop the reading into the current buffer
myCurrentBuffer.redoReading(x, y);
//i++;
}
myCurrentBuffer.endRedoBuffer();
/* Put this in to see how long the cumulative filtering is taking
if (clean)
printf("### %ld %d\n", len.mSecSince(), myCumulativeBuffer.getBuffer()->size());
*/
internalGotReading();
}
void ArLaser::internalProcessReading(double x, double y,
unsigned int range, bool clean,
bool onlyClean)
{
if (myCumulativeBuffer.getSize() == 0)
return;
// make sure we really want to clean
if (clean && myCumulativeCleanDistSquared < 1)
clean = false;
std::list<ArPoseWithTime *>::iterator cit;
bool addReading = true;
//double squaredDist;
ArLineSegment line;
double xTaken = myCurrentBuffer.getPoseTaken().getX();
double yTaken = myCurrentBuffer.getPoseTaken().getY();
ArPose intersection;
ArPoseWithTime reading(x, y);
// if we're not cleaning and its further than we're keeping track of
// readings ignore it... replaced with the part thats 'until here'
/*
if (!clean &&
myMaxInsertDistCumulative > 1 &&
range > myMaxInsertDistCumulative)
return;
*/
if (onlyClean)
addReading = false;
if (myMaxInsertDistCumulative > 1 &&
range > myMaxInsertDistCumulative)
addReading = false;
if (!clean && !addReading)
return;
// until here
// if we're cleaning we start our sweep
if (clean)
myCumulativeBuffer.beginInvalidationSweep();
// run through all the readings
for (cit = getCumulativeBuffer()->begin();
cit != getCumulativeBuffer()->end();
++cit)
{
// if its closer to a reading than the filter near dist, just return
if (addReading && myMinDistBetweenCumulativeSquared < .0000001 ||
(ArMath::squaredDistanceBetween(x, y, (*cit)->getX(), (*cit)->getY()) <
myMinDistBetweenCumulativeSquared))
{
// if we're not cleaning it and its too close just return,
// otherwise keep going (to clear out invalid readings)
if (!clean)
return;
addReading = false;
}
// see if this reading invalidates some other readings by coming too close
if (clean)
{
// set up our line
line.newEndPoints(x, y, xTaken, yTaken);
// see if the cumulative buffer reading perpindicular intersects
// this line segment, and then see if its too close if it does,
// but if the intersection is very near the endpoint then leave it
if (line.getPerpPoint((*cit), &intersection) &&
(intersection.squaredFindDistanceTo(*(*cit)) <
myCumulativeCleanDistSquared) &&
(intersection.squaredFindDistanceTo(reading) >
50 * 50))
{
//printf("Found one too close to the line\n");
myCumulativeBuffer.invalidateReading(cit);
}
}
}
// if we're cleaning finish the sweep
if (clean)
myCumulativeBuffer.endInvalidationSweep();
// toss the reading in
if (addReading)
myCumulativeBuffer.addReading(x, y);
}
AREXPORT bool ArLaser::laserPullUnsetParamsFromRobot(void)
{
if (myRobot == NULL)
{
ArLog::log(ArLog::Normal, "%s: Trying to connect, but have no robot, continuing under the assumption this is intentional", getName());
return true;
}
const ArRobotParams *params = myRobot->getRobotParams();
if (params == NULL)
{
ArLog::log(ArLog::Terse,
"%s: Robot has no params, cannot pull unset params from robot",
getName());
return false;
}
const char *paramStr;
char *endPtr;
double paramDouble;
int paramInt;
bool paramBool;
paramBool = params->getLaserFlipped(getLaserNumber());
if (!myFlippedSet)
{
if (paramBool)
{
ArLog::log(myInfoLogLevel,
"%s: Setting flipped to true from robot params",
getName());
setFlipped(true);
}
else if (!paramBool)
{
ArLog::log(myInfoLogLevel,
"%s: Setting flipped to false from robot params",
getName());
setFlipped(false);
}
}
paramInt = params->getLaserMaxRange(getLaserNumber());
if (!myMaxRangeSet)
{
if(paramInt < 0)
{
ArLog::log(ArLog::Terse, "%s: LaserMaxRange in robot param file was negative but shouldn't be (it was '%d'), failing", getName(), paramInt);
return false;
}
if (paramInt > 0)
{
ArLog::log(myInfoLogLevel,
"%s: Setting max range to %d from robot params",
getName(), paramInt);
setMaxRange(paramInt);
}
}
paramInt = params->getLaserCumulativeBufferSize(getLaserNumber());
if (!myCumulativeBufferSizeSet)
{
if(paramInt < 0)
{
ArLog::log(ArLog::Terse, "%s: LaserCumulativeBufferSize in robot param file was negative but shouldn't be (it was '%d'), failing", getName(), paramInt);
return false;
}
if (paramInt > 0)
{
ArLog::log(myInfoLogLevel,
"%s: Setting cumulative buffer size to %d from robot params",
getName(), paramInt);
setCumulativeBufferSize(paramInt);
}
}
paramStr = params->getLaserStartDegrees(getLaserNumber());
if (canSetDegrees() && !myStartDegreesSet &&
paramStr != NULL && paramStr[0] != '\0')
{
paramDouble = strtod(paramStr, &endPtr);
if(endPtr == paramStr)
{
ArLog::log(ArLog::Terse, "%s: LaserStartDegrees in robot param file was not a double (it was '%s'), failing", getName(), paramStr);
return false;
}
ArLog::log(myInfoLogLevel,
"%s: Setting start degrees to %g from robot params",
getName(), paramDouble);
setStartDegrees(paramDouble);
}
paramStr = params->getLaserEndDegrees(getLaserNumber());
if (canSetDegrees() && !myEndDegreesSet &&
paramStr != NULL && paramStr[0] != '\0')
{
paramDouble = strtod(paramStr, &endPtr);
if(endPtr == paramStr)
{
ArLog::log(ArLog::Terse,
"%s: LaserEndDegrees in robot param file was not a double (it was '%s'), failing",
getName(), paramStr);
return false;
}
ArLog::log(myInfoLogLevel,
"%s: Setting end degrees to %g from robot params",
getName(), paramDouble);
setEndDegrees(paramDouble);
}
paramStr = params->getLaserDegreesChoice(getLaserNumber());
if (canChooseDegrees() && !myDegreesChoiceSet &&
paramStr != NULL && paramStr[0] != '\0')
{
ArLog::log(myInfoLogLevel,
"%s: Setting degrees choice to %s from robot params",
getName(), paramStr);
chooseDegrees(paramStr);
}
paramStr = params->getLaserIncrement(getLaserNumber());
if (canSetDegrees() && !myIncrementSet &&
paramStr != NULL && paramStr[0] != '\0')
{
paramDouble = strtod(paramStr, &endPtr);
if(endPtr == paramStr)
{
ArLog::log(ArLog::Terse,
"%s: LaserIncrement in robot param file was not a double (it was '%s'), failing",
getName(), paramStr);
return false;
}
ArLog::log(myInfoLogLevel,
"%s: Setting increment to %g from robot params",
getName(), paramDouble);
setIncrement(paramDouble);
}
paramStr = params->getLaserIncrementChoice(getLaserNumber());
if (canChooseIncrement() && !myIncrementChoiceSet &&
paramStr != NULL && paramStr[0] != '\0')
{
ArLog::log(myInfoLogLevel,
"%s: Setting increment choice to %s from robot params",
getName(), paramStr);
chooseIncrement(paramStr);
}
paramStr = params->getLaserUnitsChoice(getLaserNumber());
if (canChooseUnits() && !myUnitsChoiceSet &&
paramStr != NULL && paramStr[0] != '\0')
{
ArLog::log(myInfoLogLevel,
"%s: Setting units choice to %s from robot params",
getName(), paramStr);
chooseUnits(paramStr);
}
paramStr = params->getLaserReflectorBitsChoice(getLaserNumber());
if (canChooseReflectorBits() && !myReflectorBitsChoiceSet &&
paramStr != NULL && paramStr[0] != '\0')
{
ArLog::log(myInfoLogLevel,
"%s: Setting reflectorBits choice to %s from robot params",
getName(), paramStr);
chooseReflectorBits(paramStr);
}
paramBool = params->getLaserPowerControlled(getLaserNumber());
if (canSetPowerControlled() && !myPowerControlledSet)
{
if (paramBool)
{
ArLog::log(myInfoLogLevel,
"%s: Setting powerControlled to true from robot params",
getName());
setPowerControlled(true);
}
else if (!paramBool)
{
ArLog::log(myInfoLogLevel,
"%s: Setting powerControlled to false from robot params",
getName());
setPowerControlled(false);
}
}
paramStr = params->getLaserStartingBaudChoice(getLaserNumber());
if (canChooseStartingBaud() && !myStartingBaudChoiceSet &&
paramStr != NULL && paramStr[0] != '\0')
{
ArLog::log(myInfoLogLevel,
"%s: Setting startingBaud choice to %s from robot params",
getName(), paramStr);
chooseStartingBaud(paramStr);
}
paramStr = params->getLaserAutoBaudChoice(getLaserNumber());
if (canChooseAutoBaud() && !myAutoBaudChoiceSet &&
paramStr != NULL && paramStr[0] != '\0')
{
ArLog::log(myInfoLogLevel,
"%s: Setting autoBaud choice to %s from robot params",
getName(), paramStr);
chooseAutoBaud(paramStr);
}
if (!addIgnoreReadings(params->getLaserIgnore(getLaserNumber())))
return false;
setSensorPosition(params->getLaserX(getLaserNumber()),
params->getLaserY(getLaserNumber()),
params->getLaserTh(getLaserNumber()),
params->getLaserZ(getLaserNumber()));
return true;
}
AREXPORT void ArLaser::setDeviceConnection(ArDeviceConnection *conn)
{
myConnMutex.lock();
myConn = conn;
myConn->setDeviceName(getName());
myConnMutex.unlock();
}
AREXPORT ArDeviceConnection *ArLaser::getDeviceConnection(void)
{
return myConn;
}
/**
Sets the time to go without a response from the laser
until it is assumed that the connection with the laser has been
broken and the disconnect on error events will happen.
If there is no robot then there is a straightforward check of last
reading time against this value. If there is a robot then it will
not start the check until the laser is running and connected.
@param seconds if 0 or less then the connection timeout feature
will be disabled, otherwise disconnect on error will be triggered
after this number of miliseconds...
**/
AREXPORT void ArLaser::setConnectionTimeoutSeconds(double seconds)
{
ArLog::log(ArLog::Normal,
"%s::setConnectionTimeoutSeconds: Setting timeout to %g secs",
getName(), seconds);
myLastReading.setToNow();
if (seconds > 0)
myTimeoutSeconds = seconds;
else
myTimeoutSeconds = 0;
}
/**
Gets the time (miliseconds) to go without response from the laser
until it is assumed that the connection with the laser has been
broken and the disconnect on error events will happen.
If 0, then the timeout is disabled.
If there is no robot then there is a straightforward check of last
reading time against this value. If there is a robot then it will
not start the check until the laser is running and connected.
**/
double ArLaser::getConnectionTimeoutSeconds(void)
{
return myTimeoutSeconds;
}
AREXPORT void ArLaser::laserConnect(void)
{
// figure out how many readings we can have and set the current
// buffer size to that
double degrees;
myLastReading.setToNow();
if (canSetDegrees())
{
//degrees = fabs(ArMath::subAngle(getStartDegrees(), getEndDegrees()));
degrees = fabs(getStartDegrees() - getEndDegrees());
ArLog::log(myInfoLogLevel,
"%s: Using degrees settings of %g to %g for %g degrees",
getName(), getStartDegrees(), getEndDegrees(),
degrees);
}
else if (canChooseDegrees())
{
degrees = getDegreesChoiceDouble();
ArLog::log(myInfoLogLevel, "%s: Using choice of %g degrees",
getName(), degrees);
}
else
{
degrees = 360;
ArLog::log(ArLog::Terse, "%s: Don't have any settings for degrees, arbitrarily using 360", getName());
}
double increment;
if (canSetIncrement())
{
increment = getIncrement();
ArLog::log(myInfoLogLevel, "%s: Using increment setting of %g degrees",
getName(), increment);
}
else if (canChooseIncrement())
{
increment = getIncrementChoiceDouble();
ArLog::log(myInfoLogLevel, "%s: Using increment setting of %g degrees",
getName(), increment);
}
else
{
// PS 10/20/11 - This was missing causing buffer size to be very large
// set this to the lowest, note both the SZ and S3 are setting the buffer
// size but it's being overriden by this procedure - do we want to fix
// this or just leave it at the max value 360/.25=1440???
increment = .25;
ArLog::log(ArLog::Terse, "%s: Don't have any settings for increment, arbitrarily using .25", getName());
}
int size = (int)ceil(degrees / increment) + 1;
ArLog::log(myInfoLogLevel, "%s: Setting current buffer size to %d",
getName(), size);
setCurrentBufferSize(size);
ArLog::log(myInfoLogLevel, "%s: Connected", getName());
myConnectCBList.invoke();
}
AREXPORT void ArLaser::laserFailedConnect(void)
{
ArLog::log(myInfoLogLevel, "%s: Failed to connect", getName());
myFailedConnectCBList.invoke();
}
AREXPORT void ArLaser::laserDisconnectNormally(void)
{
ArLog::log(myInfoLogLevel, "%s: Disconnected normally", getName());
myDisconnectNormallyCBList.invoke();
}
AREXPORT void ArLaser::laserDisconnectOnError(void)
{
ArLog::log(ArLog::Normal, "%s: Disconnected because of error", getName());
myDisconnectOnErrorCBList.invoke();
}
AREXPORT void ArLaser::internalGotReading(void)
{
if (myTimeLastReading != time(NULL))
{
myTimeLastReading = time(NULL);
myReadingCount = myReadingCurrentCount;
myReadingCurrentCount = 0;
}
myReadingCurrentCount++;
myLastReading.setToNow();
myDataCBList.invoke();
}
AREXPORT int ArLaser::getReadingCount()
{
if (myTimeLastReading == time(NULL))
return myReadingCount;
if (myTimeLastReading == time(NULL) - 1)
return myReadingCurrentCount;
return 0;
}
AREXPORT void ArLaser::setSensorPosition(
double x, double y, double th, double z)
{
setSensorPosition(ArPose(x, y, th), z);
}
AREXPORT void ArLaser::setSensorPosition(ArPose pose, double z)
{
myHaveSensorPose = true;
mySensorPose.setPose(pose);
mySensorZ = z;
}
bool ArLaser::internalCheckChoice(const char *check, const char *choice,
std::list<std::string> *choices,
const char *choicesStr)
{
if (check == NULL || choices == NULL || choice == NULL || choice[0] == '\0')
{
ArLog::log(ArLog::Terse, "%s::%s: Internal error in setup");
return false;
}
std::list<std::string>::iterator it;
std::string str;
for (it = choices->begin(); it != choices->end(); it++)
{
str = (*it);
if (ArUtil::strcasecmp(choice, str) == 0)
return true;
}
ArLog::log(ArLog::Terse, "%s::%s: Invalid choice, choices are <%s>.",
myName.c_str(), check, choicesStr);
return false;
}
bool ArLaser::internalCheckChoice(const char *check, const char *choice,
std::map<std::string, double> *choices,
const char *choicesStr,
double *choiceDouble)
{
if (check == NULL || choices == NULL || choice == NULL || choice[0] == '\0')
{
ArLog::log(ArLog::Terse, "%s::%s: Internal error in setup");
return false;
}
std::map<std::string, double>::iterator it;
std::string str;
for (it = choices->begin(); it != choices->end(); it++)
{
str = (*it).first;
if (ArUtil::strcasecmp(choice, str) == 0)
{
*choiceDouble = (*it).second;
return true;
}
}
ArLog::log(ArLog::Terse, "%s::%s: Invalid choice, choices are <%s>.",
myName.c_str(), check, choicesStr);
return false;
}
void ArLaser::internalBuildChoicesString(
std::list<std::string> *choices, std::string *str)
{
std::list<std::string>::iterator it;
bool first;
std::string choiceStr;
for (it = choices->begin(), first = true; it != choices->end(); it++)
{
choiceStr = (*it);
if (!first)
(*str) += "|";
first = false;
(*str) += choiceStr;
}
}
void ArLaser::internalBuildChoices(
std::map<std::string, double> *choices, std::string *str,
std::list<std::string> *choicesList)
{
std::map<std::string, double>::iterator it;
bool first;
std::string choiceStr;
for (it = choices->begin(), first = true; it != choices->end(); it++)
{
choiceStr = (*it).first;
choicesList->push_back(choiceStr);
if (!first)
(*str) += "|";
first = false;
(*str) += choiceStr;
}
}
/**
This allows the setting of the degrees the laser will use from a
range for both starting and ending degrees. Only one of this and
laserAllowDegreesChoices should be used.
@param defaultStartDegrees The default start degrees to use, this
default should probably be for the max range.
@param startDegreesMin The minimum value for start degrees
@param startDegreesMax The maximum value for start degrees
@param defaultEndDegrees The default end degrees to use, this
default should probably be for the max range.
@param endDegreesMin The minimum value for end degrees
@param endDegreesMax The maximum value for end degrees
**/
AREXPORT void ArLaser::laserAllowSetDegrees(double defaultStartDegrees, double startDegreesMin, double startDegreesMax, double defaultEndDegrees, double endDegreesMin, double endDegreesMax)
{
myCanSetDegrees = true;
myStartDegreesMin = startDegreesMin;
myStartDegreesMax = startDegreesMax;
setStartDegrees(defaultStartDegrees);
myStartDegreesSet = false;
myEndDegreesMin = endDegreesMin;
myEndDegreesMax = endDegreesMax;
setEndDegrees(defaultEndDegrees);
myEndDegreesSet = false;
}
AREXPORT bool ArLaser::setStartDegrees(double startDegrees)
{
if (!myCanSetDegrees)
{
ArLog::log(ArLog::Terse, "%s::setStartDegrees: Cannot set angles on this laser", myName.c_str());
return false;
}
if (startDegrees < myStartDegreesMin)
{
ArLog::log(ArLog::Terse, "%s::setStartDegrees: Start degrees (%g) tried to be set to less than the minimum (%g))", myName.c_str(), startDegrees, myStartDegreesMin);
return false;
}
if (startDegrees > myStartDegreesMax)
{
ArLog::log(ArLog::Terse, "%s::setStartDegrees: Start degrees (%g) tried to be set to greater than the minimum (%g))", myName.c_str(), startDegrees, myStartDegreesMax);
return false;
}
if (myEndDegreesSet && startDegrees >= myEndDegrees)
{
ArLog::log(ArLog::Terse, "%s::setStartDegrees: Start degrees (%g) tried to be set to greater than or equal to end degrees %g)", myName.c_str(), startDegrees, myEndDegrees);
return false;
}
myStartDegreesSet = true;
myStartDegrees = startDegrees;
return true;
}
AREXPORT bool ArLaser::setEndDegrees(double endDegrees)
{
if (!myCanSetDegrees)
{
ArLog::log(ArLog::Terse, "%s::setEndDegrees: Cannot set angles on this laser", myName.c_str());
return false;
}
if (endDegrees < myEndDegreesMin)
{
ArLog::log(ArLog::Terse, "%s::setEndDegrees: End degrees (%g) tried to be set to less than the minimum (%g))", myName.c_str(), endDegrees, myEndDegreesMin);
return false;
}
if (endDegrees > myEndDegreesMax)
{
ArLog::log(ArLog::Terse, "%s::setEndDegrees: End degrees (%g) tried to be set to greater than the minimum (%g))", myName.c_str(), endDegrees, myEndDegreesMax);
return false;
}
if (myStartDegreesSet && endDegrees <= myStartDegrees)
{
ArLog::log(ArLog::Terse, "%s::setEndDegrees: End degrees (%g) tried to be set to less than or equal to end degrees %g)", myName.c_str(), endDegrees, myStartDegrees);
return false;
}
myEndDegreesSet = true;
myEndDegrees = endDegrees;
return true;
}
/**
Allows the choice of the laser degrees from one of a number of
choices, only one of this and laserAllowSetDegrees should be used.
@param defaultDegreesChoice The default degrees, this should be
the largest value.
@param degreesChoices this is a mapping of std::strings to
doubles, the strings should be the actual available choices, and
the doubles should be the numerical representation... this is so
the simulated laser can behave more easily like the real
lasers... and because the original sick driver used words typed out
(to make problems more obvious).
**/
AREXPORT void ArLaser::laserAllowDegreesChoices(
const char *defaultDegreesChoice,
std::map<std::string, double> degreesChoices)
{
myCanChooseDegrees = true;
myDegreesChoices = degreesChoices;
internalBuildChoices(&myDegreesChoices, &myDegreesChoicesString, &myDegreesChoicesList);
chooseDegrees(defaultDegreesChoice);
myDegreesChoiceSet = false;
}
AREXPORT bool ArLaser::chooseDegrees(
const char *degreesChoice)
{
if (!myCanChooseDegrees)
{
ArLog::log(ArLog::Terse, "%s::chooseDegrees: Cannot choose degrees on this laser", myName.c_str());
return false;
}
double degreesChoiceDouble;
if (!internalCheckChoice("chooseDegrees", degreesChoice, &myDegreesChoices,
myDegreesChoicesString.c_str(), &degreesChoiceDouble))
return false;
myDegreesChoice = degreesChoice;
myDegreesChoiceDouble = degreesChoiceDouble;
return true;
}
/**
Allows the choice of increment from a range, only one of this and
laserAllowIncrementChoices should be used.
@param defaultIncrement The default increment to use, this
default should be a reasonable value.
@param incrementMin The minimum value for the increment
@param incrementMax The maximum value for the increment
**/
AREXPORT void ArLaser::laserAllowSetIncrement(
double defaultIncrement, double incrementMin, double incrementMax)
{
myCanSetIncrement = true;
myIncrementMin = incrementMin;
myIncrementMax = incrementMax;
setIncrement(defaultIncrement);
myIncrementSet = false;
}
AREXPORT bool ArLaser::setIncrement(double increment)
{
if (!myCanSetIncrement)
{
ArLog::log(ArLog::Terse, "%s::setIncrement: Cannot set increment on this laser", myName.c_str());
return false;
}
if (increment < myIncrementMin)
{
ArLog::log(ArLog::Terse, "%s::setIncrement: Increment (%g) tried to be set to less than the minimum (%g))", myName.c_str(), increment, myIncrementMin);
return false;
}
if (increment > myIncrementMax)
{
ArLog::log(ArLog::Terse, "%s::setIncrement: End degrees (%g) tried to be set to greater than the maximum (%g))", myName.c_str(), increment, myIncrementMax);
return false;
}
myIncrementSet = true;
myIncrement = increment;
return true;
}
/**
Allows the choice of increment from a limited set of values, only
one of this and laserAllowSetIncrement should be used.
@param defaultIncrementChoice The default increment, this should be
a reasonable value.
@param incrementChoices this is a mapping of std::strings to
doubles, the strings should be the actual available choices, and
the doubles should be the numerical representation... this is so
the simulated laser can behave more easily like the real
lasers... and because the original sick driver used words typed out
(to make problems more obvious).
**/
AREXPORT void ArLaser::laserAllowIncrementChoices(
const char *defaultIncrementChoice,
std::map<std::string, double> incrementChoices)
{
myCanChooseIncrement = true;
myIncrementChoices = incrementChoices;
internalBuildChoices(&myIncrementChoices, &myIncrementChoicesString, &myIncrementChoicesList);
chooseIncrement(defaultIncrementChoice);
myIncrementChoiceSet = false;
}
AREXPORT bool ArLaser::chooseIncrement(const char *incrementChoice)
{
if (!myCanChooseIncrement)
{
ArLog::log(ArLog::Terse, "%s::chooseIncrement: Cannot choose increment on this laser", myName.c_str());
return false;
}
double incrementChoiceDouble;
if (!internalCheckChoice("chooseIncrement", incrementChoice,
&myIncrementChoices, myIncrementChoicesString.c_str(),
&incrementChoiceDouble))
return false;
myIncrementChoice = incrementChoice;
myIncrementChoiceDouble = incrementChoiceDouble;
return true;
}
/**
@param defaultUnitsChoice This is the default units choice, it
should be a reasonable value.
@param unitsChoices The possible choices for units.
**/
AREXPORT void ArLaser::laserAllowUnitsChoices(
const char *defaultUnitsChoice,
std::list<std::string> unitsChoices)
{
myCanChooseUnits = true;
myUnitsChoices = unitsChoices;
internalBuildChoicesString(&myUnitsChoices, &myUnitsChoicesString);
chooseUnits(defaultUnitsChoice);
myUnitsChoiceSet = false;
}
AREXPORT bool ArLaser::chooseUnits(const char *unitsChoice)
{
if (!myCanChooseUnits)
{
ArLog::log(ArLog::Terse, "%s::chooseUnits: Cannot choose units on this laser", myName.c_str());
return false;
}
if (!internalCheckChoice("chooseUnits", unitsChoice, &myUnitsChoices,
myUnitsChoicesString.c_str()))
return false;
myUnitsChoice = unitsChoice;
return true;
}
/**
@param defaultReflectorBitsChoice The default choice for reflector
bits, should be a reasonable value.
@param reflectorBitsChoices The possible choices for reflector bits
**/
AREXPORT void ArLaser::laserAllowReflectorBitsChoices(
const char *defaultReflectorBitsChoice,
std::list<std::string> reflectorBitsChoices)
{
myCanChooseReflectorBits = true;
myReflectorBitsChoices = reflectorBitsChoices;
internalBuildChoicesString(&myReflectorBitsChoices, &myReflectorBitsChoicesString);
chooseReflectorBits(defaultReflectorBitsChoice);
myReflectorBitsChoiceSet = false;
}
AREXPORT bool ArLaser::chooseReflectorBits(const char *reflectorBitsChoice)
{
if (!myCanChooseReflectorBits)
{
ArLog::log(ArLog::Terse, "%s::chooseReflectorBits: Cannot choose reflectorBits on this laser", myName.c_str());
return false;
}
if (!internalCheckChoice("chooseReflectorBits", reflectorBitsChoice,
&myReflectorBitsChoices,
myReflectorBitsChoicesString.c_str()))
return false;
myReflectorBitsChoice = reflectorBitsChoice;
return true;
}
/**
Allows settings of whether the power can be controlled or not.
This is mostly for devices that respond differently at power up
than they do if they are already on (ie the lms2xx where it doesn't
respond at all while powering up). If the communication is the
same either way, you can just not set this.
@param defaultPowerControlled The default value for power
controlled.
**/
AREXPORT void ArLaser::laserAllowSetPowerControlled(bool defaultPowerControlled)
{
myCanSetPowerControlled = true;
setPowerControlled(defaultPowerControlled);
myPowerControlledSet = false;
}
AREXPORT bool ArLaser::setPowerControlled(
bool powerControlled)
{
if (!myCanSetPowerControlled)
{
ArLog::log(ArLog::Terse, "%s::setPowerControlled: Cannot set if the laser power is controlled on this laser", myName.c_str());
return false;
}
myPowerControlledSet = true;
myPowerControlled = powerControlled;
return true;
}
/**
@param defaultStartingBaudChoice Default starting baud choice.
This should probably stay the same as what the sensor ships with.
@param startingBaudChoices The available choices for starting baud
**/
AREXPORT void ArLaser::laserAllowStartingBaudChoices(
const char *defaultStartingBaudChoice,
std::list<std::string> startingBaudChoices)
{
myCanChooseStartingBaud = true;
myStartingBaudChoices = startingBaudChoices;
internalBuildChoicesString(&myStartingBaudChoices, &myStartingBaudChoicesString);
chooseStartingBaud(defaultStartingBaudChoice);
myStartingBaudChoiceSet = false;
}
AREXPORT bool ArLaser::chooseStartingBaud(const char *startingBaudChoice)
{
if (!myCanChooseStartingBaud)
{
ArLog::log(ArLog::Terse, "%s::chooseStartingBaud: Cannot choose startingBaud on this laser", myName.c_str());
return false;
}
if (!internalCheckChoice("chooseStartingBaud", startingBaudChoice,
&myStartingBaudChoices,
myStartingBaudChoicesString.c_str()))
return false;
myStartingBaudChoice = startingBaudChoice;
return true;
}
/**
@param defaultAutoBaudChoice Default auto baud choice. This should
probably be the maximum reasonable reliable robust rate that the
laser supports. The laser should autobaud up to this choice after
it connects.
@param autoBaudChoices The available choices for auto baud
**/
AREXPORT void ArLaser::laserAllowAutoBaudChoices(
const char *defaultAutoBaudChoice,
std::list<std::string> autoBaudChoices)
{
myCanChooseAutoBaud = true;
myAutoBaudChoices = autoBaudChoices;
internalBuildChoicesString(&myAutoBaudChoices, &myAutoBaudChoicesString);
chooseAutoBaud(defaultAutoBaudChoice);
myAutoBaudChoiceSet = false;
}
AREXPORT bool ArLaser::chooseAutoBaud(const char *autoBaudChoice)
{
if (!myCanChooseAutoBaud)
{
ArLog::log(ArLog::Terse, "%s::chooseAutoBaud: Cannot choose autoBaud on this laser", myName.c_str());
return false;
}
if (!internalCheckChoice("chooseAutoBaud", autoBaudChoice,
&myAutoBaudChoices, myAutoBaudChoicesString.c_str()))
return false;
myAutoBaudChoice = autoBaudChoice;
return true;
}
AREXPORT void ArLaser::laserSetDefaultTcpPort(int defaultTcpPort)
{
myDefaultTcpPort = defaultTcpPort;
}
AREXPORT void ArLaser::laserSetDefaultPortType(const char *defaultPortType)
{
myDefaultPortType = defaultPortType;
}
AREXPORT bool ArLaser::addIgnoreReadings(const char *ignoreReadings)
{
// if we have , then use it as the separator, otherwise use space
// like normal
char separator = ' ';
if (strstr(ignoreReadings, ",") != NULL)
separator = ',';
ArArgumentBuilder args(1024, separator);
args.add(ignoreReadings);
if (args.getArgc() == 0)
return true;
size_t i;
const char *str;
float begin, end;
float ignore;
for (i = 0; i < args.getArgc(); i++)
{
if (args.isArgDouble(i))
{
ignore = args.getArgDouble(i);
addIgnoreReading(ignore);
ArLog::log(ArLog::Verbose, "%s: Added ignore reading %g",
getName(), ignore);
}
else
{
str = args.getArg(i);
if (sscanf(str, "%f:%f", &begin, &end) == 2 ||
sscanf(str, "%f-%f", &begin, &end) == 2)
{
ArLog::log(ArLog::Verbose, "%s: Adding ignore reading from %g to %g",
getName(), begin, end);
// reorder them for easier looping
if (begin > end)
{
ignore = begin;
begin = end;
end = ignore;
}
ArLog::log(ArLog::Verbose, "%s: Added ignore reading (beginning) %g",
getName(), begin);
addIgnoreReading(begin);
for (ignore = begin; ignore <= end; ignore += 1.0)
{
ArLog::log(ArLog::Verbose, "%s: Added ignore reading %g",
getName(), ignore);
addIgnoreReading(ignore);
}
ArLog::log(ArLog::Verbose, "%s: Added ignore reading (ending) %g",
getName(), end);
addIgnoreReading(end);
}
else
{
ArLog::log(ArLog::Terse, "%s: Bad syntax for ignore readings, had string '%s' as one of the arguments (the values need to either be individual doubles, or begin:end (75:77) or begin-end (75-77))", getName(), str);
return false;
}
}
}
return true;
}
/**
Applies a transform to the buffers. this is mostly useful for translating
to/from local/global coordinates, but may have other uses.
This is different from
the class because it also transforms the raw readings.
@param trans the transform to apply to the data
@param doCumulative whether to transform the cumulative buffer or not
*/
AREXPORT void ArLaser::applyTransform(ArTransform trans,
bool doCumulative)
{
myCurrentBuffer.applyTransform(trans);
std::list<ArSensorReading *>::iterator it;
for (it = myRawReadings->begin(); it != myRawReadings->end(); ++it)
(*it)->applyTransform(trans);
if (doCumulative)
myCumulativeBuffer.applyTransform(trans);
}
/**
This will check if the laser has lost connection. If there is no
robot it is a straightforward check of last reading time against
getConnectionTimeoutSeconds. If there is a robot then it will not
start the check until the laser is running and connected.
**/
AREXPORT bool ArLaser::laserCheckLostConnection(void)
{
if ((myRobot == NULL || myRobotRunningAndConnected) &&
getConnectionTimeoutSeconds() > 0 &&
myLastReading.mSecSince() > getConnectionTimeoutSeconds() * 1000)
return true;
if (!myRobotRunningAndConnected && myRobot != NULL &&
myRobot->isRunning() && myRobot->isConnected())
{
myRobotRunningAndConnected = true;
myLastReading.setToNow();
}
return false;
}
AREXPORT void ArLaser::copyReadingCount(const ArLaser* laser)
{
myTimeLastReading = laser->myTimeLastReading;
myReadingCurrentCount = laser->myReadingCurrentCount;
myReadingCount = laser->myReadingCount;
}
AREXPORT void ArLaser::useSimpleNamingForAllLasers(void)
{
ArLog::log(ArLog::Normal, "ArLaser: Will use simple naming for all lasers");
ourUseSimpleNaming = true;
}