Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

SkewlZoneDriver.cc

Go to the documentation of this file.
00001 #ifdef HAVE_SKEWLZONE
00002 
00003 #include "SkewlZoneDriver.h"
00004 #include "Shared/MarkScope.h"
00005 #include "Shared/get_time.h"
00006 #include "Shared/debuget.h"
00007 
00008 using namespace std; 
00009 
00010 const unsigned int SkewlZoneDriver::NUM_SERVO;
00011 const int SkewlZoneDriver::UNUSED;
00012 const std::string SkewlZoneDriver::autoRegisterSkewlZoneDriver = DeviceDriver::getRegistry().registerType<SkewlZoneDriver>("SkewlZone");
00013 
00014 void SkewlZoneDriver::motionStarting() {
00015   ASSERTRET(!motionActive,"SkewlZoneDriver::motionStarting, but motionActive is true");
00016   MotionHook::motionStarting();
00017   CommPort * comm = CommPort::getRegistry().getInstance(commName);
00018   if(comm==NULL)
00019     std::cerr << "SkewlZoneDriver \"" << instanceName << "\": could not find CommPort \"" << commName << "\"" << std::endl;
00020   else
00021     comm->open();
00022   motionActive=true;
00023   commName.addPrimitiveListener(this);
00024 }
00025 
00026 bool SkewlZoneDriver::isConnected() {
00027   CommPort * comm = CommPort::getRegistry().getInstance(commName);
00028   return (comm!=NULL && comm->isWriteable());
00029 }
00030 
00031 void SkewlZoneDriver::motionStopping() {
00032   ASSERTRET(motionActive,"SkewlZoneDriver::motionStopping, but motionActive is false");
00033   motionActive=false;
00034   if(!sensorsActive) // listener count is not recursive, so only remove if we're the last one
00035     commName.removePrimitiveListener(this);
00036   CommPort * comm = CommPort::getRegistry().getInstance(commName);
00037   if(comm!=NULL)
00038     comm->close(); // this *is* recursive, so we always close it to match our open() in motionStarting()
00039   MotionHook::motionStopping();
00040 }
00041 
00042 void SkewlZoneDriver::motionCheck(const float outputs[][NumOutputs]) {
00043 
00044   bool  something_changed = false;  // variable to flag changes
00045   int RCB3_index = 0;     // RCB3 index for writing to the move vector
00046 
00047   for(unsigned int i=0; i<NUM_SERVO; i++) {
00048     int idx=servos[i];
00049     if(idx<0 || static_cast<unsigned int>(idx)>=NumOutputs) {
00050       if(idx!=UNUSED)
00051         std::cerr << "Warning: SkewlZone driver mapping servo " << i << " to invalid output index " << idx << std::endl;
00052       continue; // invalid/unused servo
00053     }
00054     
00055     if(isFirstCheck || lastOutputs[idx]!=outputs[NumFrames-1][idx] ) {
00056       // for debugging purposes, the servo position is printed out to the command line
00057       std::cout << "Servo:" << i << "=" << outputs[NumFrames-1][idx] << std::endl;
00058 
00059       something_changed = true;
00060 
00061       // convert the Tekkotsu index to the RCB3 index for writing to the move vector
00062       RCB3_index = convertIndex(i);
00063 
00064       // if you are not going to hit an undesired ("special") position,
00065       // and it's not the first check, go ahead with the operation
00066       // otherwise, do nothing
00067       if(!bsMotorPosIsSpecial(vecMotorPos.m_fBuf[RCB3_index]) && !isFirstCheck)
00068       {
00069         // the speed is hard-coded to 100% to allow Tekkotsu to do all interpolation
00070         vecMotorMove.m_fBufSpeed[RCB3_index] = 100.0;
00071 
00072         // if the angle for this index should be inverted, invert it
00073         // otherwise, pass it through
00074         if(indexInverted(RCB3_index))
00075         {
00076           vecMotorMove.m_fBufPos[RCB3_index] = (-1)*(outputs[NumFrames-1][idx]);
00077         }
00078         else
00079         {
00080           vecMotorMove.m_fBufPos[RCB3_index] = outputs[NumFrames-1][idx];
00081         }
00082       }
00083       else
00084       {
00085         // set speed to zero and make the index a NOOP
00086         vecMotorMove.m_fBufSpeed[RCB3_index] = 0.0;
00087         vecMotorMove.m_fBufPos[RCB3_index] = BSBOT_MOTOR_POS_NOOP;
00088       }
00089     }
00090   }
00091 
00092   if(!isFirstCheck) { //All the motors go to zero on startup anyway...so don't move on the first check
00093     if(something_changed) // if a position has changed, send the update
00094     {
00095       rc = bsBotMoveToPosAllMotors(pClient, &vecMotorMove);
00096     }
00097   }
00098 
00099   MotionHook::motionCheck(outputs); // updates lastOutputs and isFirstCheck, we ignore its motionUpdated() call
00100 }
00101 
00102 
00103 unsigned int SkewlZoneDriver::nextTimestamp() {
00104   CommPort * comm = CommPort::getRegistry().getInstance(commName);
00105   if(comm==NULL || !comm->isReadable())
00106     return -1U;
00107   return static_cast<unsigned int>(lastSensorTime + 1000.f/(*sensorFramerate) + .5f);
00108 }
00109 
00110 void SkewlZoneDriver::registerSource() {
00111   ASSERTRET(!sensorsActive,"SkewlZoneDriver::registerSource, but sensorsActive is true");
00112   CommPort * comm = CommPort::getRegistry().getInstance(commName);
00113   if(comm!=NULL)
00114     comm->open();
00115   queryServos.addPrimitiveListener(this);
00116   if(queryServos) {
00117     for(unsigned int i=0; i<NUM_SERVO; i++) {
00118       provideOutput(servos[i]);
00119       servos[i].addPrimitiveListener(this);
00120     }
00121   }
00122   sensorsActive=true;
00123   commName.addPrimitiveListener(this);
00124 }
00125 void SkewlZoneDriver::deregisterSource() {
00126   ASSERTRET(sensorsActive,"SkewlZoneDriver::deregisterSource, but sensorsActive is false");
00127   CommPort * comm = CommPort::getRegistry().getInstance(commName);
00128   if(comm!=NULL)
00129     comm->close();
00130   if(queryServos) {
00131     for(unsigned int i=0; i<NUM_SERVO; ++i) {
00132       servos[i].removePrimitiveListener(this);
00133       ignoreOutput(servos[i]);
00134     }
00135   }
00136   queryServos.removePrimitiveListener(this);
00137   sensorsActive=false;
00138   if(!motionActive) // listener count is not recursive, so only remove if we're the last one
00139     commName.removePrimitiveListener(this);
00140 }
00141 void SkewlZoneDriver::doUnfreeze() {
00142   MarkScope sl(poller.getStartLock());
00143   if(!poller.isStarted()) {
00144     poller.resetPeriod(1.0/(*sensorFramerate));
00145     poller.start();
00146   }
00147   sensorFramerate->addPrimitiveListener(this);
00148 }
00149 void SkewlZoneDriver::doFreeze() {
00150   MarkScope sl(poller.getStartLock());
00151   if(poller.isStarted())
00152     poller.stop().join();
00153   sensorFramerate->removePrimitiveListener(this);
00154 }
00155 
00156 
00157 bool SkewlZoneDriver::advance() {
00158   CommPort * comm = CommPort::getRegistry().getInstance(commName);
00159   if(comm==NULL || !comm->isReadable() || !comm->isWriteable())
00160     return false;
00161 
00162   MarkScope commlock(comm->getLock());
00163   std::ostream os(&comm->getWriteStreambuf());
00164   std::istream is(&comm->getReadStreambuf());
00165   
00166   // request exception on badbit so we can thread_cancel read under linux
00167   // (otherwise we get FATAL: exception not rethrown / Abort error)
00168   os.exceptions(ios_base::badbit);
00169   is.exceptions(ios_base::badbit);
00170   
00171   MarkScope writelock(getSensorWriteLock());
00172   // generally, bad idea to request servo position before position has been sent
00173   // But if the arm is still powered, it'll know it's position.  If the user has set WaitForSensors,
00174   // then DataSource::requiresFirstSensor will be set, otherwise we skip queries until we've sent
00175   // a servo position (and thus set the isFirstCheck flag
00176   if((!isFirstCheck || DataSource::requiresFirstSensor) && queryServos) {
00177     // check joint positions
00178     stringstream q;
00179     for(unsigned int i=0; i<NUM_SERVO; i++) {
00180       int idx=servos[i];
00181       if(idx<0 || static_cast<unsigned int>(idx)>=NumOutputs)
00182         continue; // invalid/unused servo
00183       q << "QP " << i << ' ';
00184     }
00185     try {
00186       os << q.rdbuf() << '\r' << flush;
00187     } catch(const std::exception&) {}
00188     if(!os) {
00189       std::cerr << "SkewlZone position query failed (bad write)" << std::endl;
00190       return false;
00191     }
00192     for(unsigned int i=0; i<NUM_SERVO; i++) {
00193       int idx=servos[i];
00194       if(idx<0 || static_cast<unsigned int>(idx)>=NumOutputs)
00195         continue; // invalid/unused servo
00196       int check=is.get();
00197       if(check==-1) {
00198         cerr << "SkewlZoneDriver: bad read!" << endl;
00199         return false;
00200       }
00201       unsigned int v=(unsigned char)check;
00202       setOutputValue(idx, getServo(i,v*10));
00203     }
00204   }
00205   stringstream aq,dq;
00206   bool acnt=0,dcnt=0;
00207   for(unsigned int i=0; i<NUM_INPUT; i++) {
00208     int idx=inputs[i];
00209     if(!buttonMode[i]) {
00210       if(idx<0 || static_cast<unsigned int>(idx)>=NumSensors) {
00211         if(idx!=UNUSED)
00212           std::cerr << "Warning: SkewlZone driver mapping input " << i << " to invalid sensor index " << idx << std::endl;
00213         continue; // invalid/unused servo
00214       }
00215       ++acnt;
00216       aq << 'V' << char('A'+i) << ' ';
00217     } else {
00218       if(idx<0 || static_cast<unsigned int>(idx)>=NumButtons) {
00219         if(idx!=UNUSED)
00220           std::cerr << "Warning: SkewlZone driver mapping input " << i << " to invalid button index " << idx << std::endl;
00221         continue; // invalid/unused servo
00222       }
00223       ++dcnt;
00224       dq << char('A'+i) << ' ' << char('A'+i) << "L ";
00225     }
00226   }
00227   
00228   // send both queries now, we can process first response while SSC is processing second query
00229   try {
00230     if(dcnt>0)
00231       os << dq.str() << '\r';
00232     if(acnt>0)
00233       os << aq.str() << '\r';
00234     if(dcnt>0 || acnt>0)
00235       os << flush;
00236   } catch(const std::exception&) {}
00237   if(!os) {
00238     std::cerr << "SkewlZone sensor query failed (bad write)" << std::endl;
00239     return false;
00240   }
00241   
00242   // store responses
00243   if(dcnt>0) {
00244     for(unsigned int i=0; i<NUM_INPUT; i++) {
00245       int idx=inputs[i];
00246       if(idx>=0 && static_cast<unsigned int>(idx)<NumButtons && buttonMode[i]) {
00247         int check=is.get();
00248         if(check==-1) {
00249           cerr << "SkewlZoneDriver: bad read!" << endl;
00250           return false;
00251         }
00252         unsigned char cur=check;
00253         check=is.get();
00254         if(check==-1) {
00255           cerr << "SkewlZoneDriver: bad read!" << endl;
00256           return false;
00257         }
00258         unsigned char latch=check;
00259         setButtonValue(idx, getDigital(i,cur,latch));
00260       }
00261     }
00262   }
00263   if(acnt>0) {
00264     for(unsigned int i=0; i<NUM_INPUT; i++) {
00265       int idx=inputs[i];
00266       if(idx>=0 && static_cast<unsigned int>(idx)<NumSensors && !buttonMode[i]) {
00267         int check=is.get();
00268         if(check==-1) {
00269           cerr << "SkewlZoneDriver: bad read!" << endl;
00270           return false;
00271         }
00272         setSensorValue(idx, getAnalog(i,check));
00273       }
00274     }
00275   }
00276   lastSensorTime=get_time();
00277   ++frameNumber;
00278   return true;
00279 }
00280 
00281 void SkewlZoneDriver::plistValueChanged(const plist::PrimitiveBase& pl) {
00282   if(&pl==&commName) {
00283     // if here, then motionStarted or setDataSourceThread has been called, thus when commName changes,
00284     // need to close old one and reopen new one
00285     if(poller.isStarted())
00286       poller.stop().join();
00287     
00288     CommPort * comm = CommPort::getRegistry().getInstance(commName.getPreviousValue());
00289     if(comm!=NULL) {
00290       // close each of our old references
00291       if(sensorsActive)
00292         comm->close();
00293       if(motionActive)
00294         comm->close();
00295     }
00296     comm = CommPort::getRegistry().getInstance(commName);
00297     if(comm==NULL) {
00298       std::cerr << "SkewlZoneDriver \"" << instanceName << "\": could not find CommPort \"" << commName << "\"" << std::endl;
00299     } else {
00300       // open each of our new references
00301       if(sensorsActive)
00302         comm->open();
00303       if(motionActive)
00304         comm->open();
00305     }
00306     
00307     if(getTimeScale()>0) {
00308       poller.resetPeriod(1.0/(*sensorFramerate));
00309       poller.start();
00310     }     
00311   } else if(&pl==&queryServos) {
00312     // if here, LoadDataThread has been assigned, need to update providing/ignoring outputs
00313     // (and maintain listeners for individual servos while providing)
00314     if(queryServos) {
00315       for(unsigned int i=0; i<NUM_SERVO; i++) {
00316         provideOutput(servos[i]);
00317         servos[i].addPrimitiveListener(this);
00318       }
00319     } else {
00320       for(unsigned int i=0; i<NUM_SERVO; ++i) {
00321         servos[i].removePrimitiveListener(this);
00322         ignoreOutput(servos[i]);
00323       }
00324     }
00325   } else if(&pl==sensorFramerate) {
00326     poller.resetPeriod(1.0/(*sensorFramerate));
00327   } else {
00328     // check if it's one of the individual servos... if it is, means we're providing servo feedback,
00329     // need to call providingOutput/ignoringOutput as appropriate
00330     for(unsigned int i=0; i<NUM_SERVO; ++i) {
00331       if(&pl==&servos[i]) {
00332         ignoreOutput(servos[i].getPreviousValue());
00333         provideOutput(servos[i]);
00334         return; // found it, DON'T fall through to error message below...
00335       }
00336     }
00337     std::cerr << "Unhandled value change in " << getClassName() << ": " << pl.get() << std::endl;
00338   }
00339 }
00340 
00341 void SkewlZoneDriver::setServo(std::ostream& ss, unsigned int servoIdx, float v) {
00342   
00343     unsigned int outputIdx = servos[servoIdx];
00344   // get output's range in radians
00345   float outRange = outputRanges[outputIdx][MaxRange]-outputRanges[outputIdx][MinRange];
00346   // get servo's range in pulse width (ms)
00347   unsigned int servoRange = maxPW[servoIdx]-minPW[servoIdx];
00348   // get commanded position as percent of range of motion
00349   float cmd = (v-outputRanges[outputIdx][MinRange])/outRange;
00350   // flip commanded position -- map positive (high) rotation to low pulse width
00351   // this is so if you mount a servo "up", coordinate system will work correctly
00352 #ifdef TGT_HAS_LEDS
00353   if(outputIdx<LEDOffset || outputIdx>=LEDOffset+NumLEDs) // only flip non-LEDs though
00354     cmd=1-cmd;
00355 #endif
00356   // do conversion from radians (output domain) to pulse width (servo domain)
00357   float pw = cmd*servoRange+minPW[servoIdx];
00358   if(pw<0)
00359     pw=0;
00360   // round to int
00361   unsigned int bpw = static_cast<unsigned int>(pw+0.5);
00362   // check bounds
00363   if(bpw<minPW[servoIdx])
00364     bpw=minPW[servoIdx];
00365   if(bpw>maxPW[servoIdx])
00366     bpw=maxPW[servoIdx];
00367   // send to output buffer
00368   ss << '#' << servoIdx << " P" << bpw << ' ';
00369 }
00370 
00371 float SkewlZoneDriver::getServo(unsigned int servoIdx, unsigned int pw) {
00372   unsigned int outputIdx = servos[servoIdx];
00373   // get output's range in radians
00374   float outRange = outputRanges[outputIdx][MaxRange]-outputRanges[outputIdx][MinRange];
00375   // get servo's range in pulse width (ms)
00376   unsigned int servoRange = maxPW[servoIdx]-minPW[servoIdx];
00377   // do conversion from pulse width (servo domain) to radians (output domain)
00378   return (pw-minPW[servoIdx])*outRange/servoRange + outputRanges[outputIdx][MinRange];
00379 }
00380 
00381 float SkewlZoneDriver::getAnalog(unsigned int /*inputIdx*/, unsigned char s) {
00382   return s*5.f/256;
00383 }
00384 
00385 float SkewlZoneDriver::getDigital(unsigned int /*inputIdx*/, unsigned char cur, unsigned char latch) {
00386   // The SSC-32 only  latches on 1->0 transitions, so switches should be wired with NC to +5V and NO to GND.
00387   if (cur=='1')  // switch hasn't been pressed since last query
00388     return 0;
00389   // switch has been pressed, so...
00390   return (latch=='1') ? 0.5f : 1;
00391 }
00392 
00393 void SkewlZoneDriver::simplePrintVec(const char *sPrefix, double fBuf[], size_t uCount)
00394 {
00395     size_t  i;
00396     //BsBotVec_T  *pVec = (BsBotVec_T *)p;
00397 
00398     printf("%s:", sPrefix);
00399     for(i=0; i<uCount; ++i)
00400     {
00401         if( (i % 8) == 0 )
00402         {
00403             printf("\n");
00404         }
00405     printf("  %7.3f", fBuf[i]);
00406     }
00407   printf("\n");
00408 }
00409 
00410 int SkewlZoneDriver::createSZclient()
00411 {
00412   if( (pClient = bsClientNew(BsBotTypeKHR2, "GeorgeOfThe_eJungle")) == NULL )
00413     {
00414         LOGERROR("Failed to create client\n");
00415         return SK_EC_EXEC;
00416     }
00417   else
00418   {
00419     return SK_EC_OK;
00420   }
00421 }
00422 
00423 int SkewlZoneDriver::connectSZserver()
00424 {
00425   if((rc = bsServerConnect(pClient, SKProxyIPAddr, SKProxyIPPort)) < 0)
00426     {
00427         LOGERROR("bsProxy @%s:%d: %s\n",
00428           SKProxyIPAddr, SKProxyIPPort, bsStrError(rc));
00429         return SK_EC_EXEC;
00430     }
00431         else
00432         {
00433                 return SK_EC_OK;
00434         }
00435 
00436 }
00437 
00438 int SkewlZoneDriver::openRCB3()
00439 {
00440   if((h=bsReqProxDevOpenRCB3(pClient, serialPath.c_str(), 115200, RCB3ModelJ)) < 0)
00441     {
00442         LOGERROR("%s: %s\n", serialPath.c_str(), bsStrError(h));
00443         return SK_EC_EXEC;
00444     }
00445   else
00446         {
00447                 return SK_EC_OK;
00448         }
00449 
00450 }
00451 
00452 int SkewlZoneDriver::RCB3EStop()
00453 {
00454   rc = bsBotEStop(pClient);
00455 
00456   return rc;
00457 }
00458 
00459 int SkewlZoneDriver::convertIndex(int i)
00460 {
00461   switch(i)
00462   {
00463     case 0:
00464       return 1;
00465     case 1:
00466       return 2;
00467     case 2:
00468       return 3;
00469     case 3:
00470       return 5;
00471     case 4:
00472       return 6;
00473     case 5:
00474       return 7;
00475     case 6:
00476       return 10;
00477     case 7:
00478       return 11;
00479     case 8:
00480       return 12;
00481     case 9:
00482       return 13;
00483     case 10:
00484       return 14;
00485     case 11:
00486       return 16;
00487     case 12:
00488       return 17;
00489     case 13:
00490       return 18;
00491     case 14:
00492       return 19;
00493     case 15:
00494       return 20;
00495     case 16:
00496       return 0;
00497   }
00498 }
00499 
00500 int SkewlZoneDriver::indexInverted(int i)
00501 {
00502   switch(i)
00503   {
00504     case 0:
00505       return 0;
00506     case 1:
00507       return 0;
00508     case 2:
00509       return 0;
00510     case 3:
00511       return 0;
00512     case 4:
00513       return 0;
00514     case 5:
00515       return 1;
00516     case 6:
00517       return 1;
00518     case 7:
00519       return 1;
00520     case 8:
00521       return 0;
00522     case 9:
00523       return 0;
00524     case 10:
00525       return 1;
00526     case 11:
00527       return 0;
00528     case 12:
00529       return 0;
00530     case 13:
00531       return 1;
00532     case 14:
00533       return 1;
00534     case 15:
00535       return 0;
00536     case 16:
00537       return 0;
00538     case 17:
00539       return 1;
00540     case 18:
00541       return 1;
00542     case 19:
00543       return 0;
00544     case 20:
00545       return 0;
00546   }
00547 }
00548 
00549 void SkewlZoneDriver::attachRCB3()
00550 {
00551   bsAttrSetInterface(pClient, BsAttrKeyIFRobotController, h);
00552 }
00553 
00554 void SkewlZoneDriver::setSZunits()
00555 {
00556   bsAttrSetUnits(pClient, BsAttrKeyUnitsMotorPos, units_radians);
00557     bsAttrSetUnits(pClient, BsAttrKeyUnitsMotorSpeed, units_percent);
00558     //bsAttrSetUnits(pClient, BsAttrKeyUnitsFootPressure, units_kgf);
00559     //bsAttrSetUnits(pClient, BsAttrKeyUnitsIMUAccel, units_m_per_s2);
00560 }
00561 
00562 void SkewlZoneDriver::printServos()
00563 {
00564   rc = bsBotReadAllMotorPos(pClient, &vecMotorPos);
00565     if( rc == BS_OK )
00566     {
00567         simplePrintVec("Current servo positions (radians)",
00568                 vecMotorPos.m_fBuf,
00569           vecMotorPos.m_uCount);
00570   }
00571 }
00572 
00573 void SkewlZoneDriver::initMotorVec()
00574 {
00575   vecMotorMove.m_uCount = vecMotorPos.m_uCount;
00576 
00577   //std::cout << "Motor Move Count = " << vecMotorMove.m_uCount << std::endl;
00578 
00579   for(int i=0; i<vecMotorMove.m_uCount; i++) {
00580     vecMotorMove.m_fBufSpeed[i] = 0.0;
00581     vecMotorMove.m_fBufPos[i]   = BSBOT_MOTOR_POS_NOOP;
00582   }
00583 }
00584 
00585 /*! @file
00586  * @brief Implements SkewlZoneDriver, based on SSC32Driver, ported to support SkewlZone interface by Jason Tennyson and Aaron Parker
00587  */
00588 
00589 #endif

Tekkotsu Hardware Abstraction Layer 5.1CVS
Generated Mon May 9 05:01:39 2016 by Doxygen 1.6.3