00001 #include "MotionSequenceEngine.h"
00002 #include "DynamicMotionSequence.h"
00003 #include "Shared/get_time.h"
00004 #include "Shared/WorldState.h"
00005 #include "Shared/Config.h"
00006 #include <iostream>
00007
00008 using std::cout;
00009 using std::endl;
00010
00011 MotionSequenceEngine::Move_idx_t MotionSequenceEngine::invalid_move=(MotionSequenceEngine::Move_idx_t)-1;
00012
00013 MotionSequenceEngine::MotionSequenceEngine()
00014 : LoadSave(), playtime(1), lasttime(0), endtime(0), playspeed(1.0f),
00015 playing(true), hold(true), loadSaveMode(1)
00016 {
00017 for(unsigned int i=0; i<NumOutputs; i++)
00018 curstamps[i]=-1U;
00019 }
00020
00021
00022 int MotionSequenceEngine::updateOutputs() {
00023 if(isPlaying()) {
00024 if(lasttime==0)
00025 play();
00026 unsigned int curtime=get_time();
00027 float diff=(curtime-lasttime)*playspeed;
00028 if(playtime<-diff)
00029 setTime(0);
00030 else
00031 setTime(static_cast<unsigned int>(diff+playtime));
00032 lasttime=curtime;
00033 return 1;
00034 } else {
00035 lasttime=get_time();
00036 return 0;
00037 }
00038 }
00039
00040 const OutputCmd& MotionSequenceEngine::getOutputCmd(unsigned int i) {
00041 if(curstamps[i]!=playtime) {
00042 if(nexts[i]!=invalid_move)
00043 calcOutput(curs[i],playtime,getKeyFrame(prevs[i]),getKeyFrame(nexts[i]));
00044 else if(hold)
00045 curs[i]=getKeyFrame(prevs[i]).cmd;
00046 else
00047 curs[i].unset();
00048 curstamps[i]=playtime;
00049 }
00050 return curs[i];
00051 }
00052
00053 unsigned int MotionSequenceEngine::getBinSize() const {
00054 char buf[128];
00055 unsigned int len=128;
00056 unsigned int used=strlen("#MSq\n");
00057 used+=snprintf(buf,len,isSaveRadians()?"radians\n":"degrees\n");
00058 unsigned int t=0;
00059 Move_idx_t tprevs[NumOutputs];
00060 Move_idx_t tnexts[NumOutputs];
00061 bool hasInitialFrame=false;
00062 for(unsigned int i=0;i<NumOutputs;i++) {
00063 tnexts[i]=getKeyFrame(tprevs[i]=starts[i]).next;
00064 if(getKeyFrame(starts[i]).cmd.weight!=0)
00065 hasInitialFrame=true;
00066 }
00067 if(hasInitialFrame)
00068 used+=snprintf(buf,len,"setTime\t0\n");
00069 while(t!=-1U) {
00070 for(unsigned int i=0; i<NumOutputs; i++) {
00071 if((t!=0 || getKeyFrame(tprevs[i]).cmd.weight!=0) && getKeyFrame(tprevs[i]).starttime==t) {
00072 if(getKeyFrame(tprevs[i]).cmd.weight==1)
00073 used+=snprintf(buf,len,"%s\t%g\n",outputNames[i],getKeyFrame(tprevs[i]).cmd.value/loadSaveMode);
00074 else
00075 used+=snprintf(buf,len,"%s\t%g\t%g\n",outputNames[i],getKeyFrame(tprevs[i]).cmd.value/loadSaveMode,getKeyFrame(tprevs[i]).cmd.weight);
00076 }
00077 }
00078 unsigned int last=t;
00079 t=setNextFrameTime(tprevs,tnexts);
00080 if(t!=-1U)
00081 used+=snprintf(buf,len,"advanceTime\t%d\n",t-last);
00082 }
00083 used+=strlen("#END\n");
00084 return used+1;
00085 }
00086
00087 unsigned int MotionSequenceEngine::loadBuffer(const char buf[], unsigned int len, const char* filename) {
00088 unsigned int origlen=len;
00089 if(strncmp("#POS",buf,4)==0) {
00090
00091 PostureEngine pose;
00092 unsigned int used=pose.loadBuffer(buf,len);
00093 if(used!=0)
00094 setPose(pose);
00095 return used;
00096 }
00097 if(strncmp("#MSq",buf,4)!=0) {
00098
00099
00100
00101
00102 return 0;
00103 }
00104 unsigned int linenum=1;
00105 unsigned int lastOutputIdx=0;
00106 while(len<=origlen && len>0) {
00107 int written;
00108
00109 if(buf[0]=='\r') {
00110 buf++; len--;
00111 if(buf[0]=='\n') {
00112 buf++; len--;
00113 }
00114 linenum++;
00115 continue;
00116 }
00117 if(buf[0]=='\n') {
00118 buf++; len--;
00119 linenum++;
00120 continue;
00121 }
00122 if(buf[0]=='#') {
00123 if(strncmp("#END\n",buf,5)==0 || strncmp("#END\r",buf,5)==0) {
00124 return origlen-len+5;
00125 } else if(strncmp("#END\r\n",buf,6)==0) {
00126 return origlen-len+6;
00127 } else {
00128 while(len>0 && *buf!='\n' && *buf!='\r') {len--;buf++;}
00129 if(*buf=='\n') {
00130 buf++;
00131 len--;
00132 }
00133 linenum++;
00134 continue;
00135 }
00136 }
00137 written=-1;
00138 const unsigned int cmdlen=64, arglen=32;
00139 char command[cmdlen];
00140 char arg1[arglen];
00141 char arg2[arglen];
00142 written=readWord(buf,&buf[len],command,cmdlen);
00143 if(!checkInc(written,buf,len,"*** ERROR MotionSequenceEngine load corrupted - line %d\n",linenum)) return 0;
00144 written=readWord(buf,&buf[len],arg1,arglen);
00145 if(written>0)
00146 if(!checkInc(written,buf,len,"*** ERROR MotionSequenceEngine load corrupted - line %d\n",linenum)) return 0;
00147 written=readWord(buf,&buf[len],arg2,arglen);
00148 if(written!=0)
00149 if(!checkInc(written,buf,len,"*** ERROR MotionSequenceEngine load corrupted - line %d\n",linenum)) return 0;
00150 for(;len>0 && *buf!='\n' && *buf!='\r';buf++,len--) {}
00151 if(*buf=='\n') {
00152 buf++;
00153 len--;
00154 }
00155
00156 if(strcasecmp(command,"delay")==0 || strcasecmp(command,"advanceTime")==0) {
00157 char* used;
00158 int delay = strtol(arg1,&used,0);
00159 if(*used!='\0') {
00160 cout << "*** WARNING illegal delay argument: " << arg1 << " - line " << linenum << endl;
00161 } else {
00162 setTime(playtime+delay);
00163 }
00164 } else if(strcasecmp(command,"settime")==0) {
00165 char* used;
00166 int newtime = strtol(arg1,&used,0);
00167 if(*used!='\0') {
00168 cout << "*** WARNING illegal settime argument: " << arg1 << " - line " << linenum << endl;
00169 } else {
00170 setTime(newtime);
00171 }
00172 } else if(strcasecmp(command,"load")==0 || strcasecmp(command,"overlay")==0) {
00173 PostureEngine pose;
00174 if(pose.loadFile(arg1)!=0) {
00175
00176 setPose(pose);
00177 } else if(overlayMotion(arg1)!=0) {
00178
00179 } else {
00180
00181 cout << "*** WARNING could not read file " << arg1 << " for overlay - line " << linenum << endl;
00182 }
00183 } else if(strcasecmp(command,"loadExplicit")==0) {
00184 PostureEngine pose;
00185 if(pose.loadFile(arg1)!=0) {
00186 setExplicitPose(pose);
00187 } else
00188 cout << "*** WARNING could not read file " << arg1 << " for load (loadExplicit only accepts posture files) - line " << linenum << endl;
00189 } else if(strcasecmp(command,"degrees")==0) {
00190 cout << "*** WARNING 'degrees' is deprecated, please specify all angles as radians" << endl;
00191 loadSaveMode=(float)M_PI/180;
00192 } else if(strcasecmp(command,"radians")==0) {
00193 setSaveRadians();
00194 } else {
00195 lastOutputIdx=getOutputIndex(command,lastOutputIdx+1);
00196 bool found = (lastOutputIdx!=NumOutputs);
00197 unsigned int lidx=lastOutputIdx,ridx=NumOutputs;
00198 if(!found) {
00199
00200 char tname[cmdlen+1];
00201 strncpy(tname+1,command,cmdlen);
00202 tname[0]='L';
00203 lidx=getOutputIndex(tname,lastOutputIdx+1);
00204 if(lidx!=NumOutputs) {
00205 tname[0]='R';
00206 ridx=getOutputIndex(tname,lidx+1);
00207 if(ridx!=NumOutputs)
00208 found=true;
00209 }
00210 }
00211 if (!found) {
00212 cout << "*** WARNING " << command << " is not a valid joint on this model." << endl;
00213 } else {
00214 char* used;
00215 float value=(float)strtod(arg1,&used), weight=1;
00216 if(*used!='\0')
00217 cout << "*** WARNING illegal value argument: " << arg1 << " - line " << linenum << endl;
00218 else {
00219 if(arg2[0]!='\0') {
00220 weight=(float)strtod(arg2,&used);
00221 if(*used!='\0') {
00222 cout << "*** WARNING illegal weight argument: " << arg2 << " - line " << linenum << endl;
00223 weight=1;
00224 }
00225 }
00226 if(lastOutputIdx!=NumOutputs) {
00227
00228 setOutputCmd(lastOutputIdx,OutputCmd(value*loadSaveMode,weight));
00229 } else {
00230
00231 setOutputCmd(lidx,OutputCmd(value*loadSaveMode,weight));
00232 setOutputCmd(ridx,OutputCmd(value*loadSaveMode,weight));
00233 lastOutputIdx=ridx;
00234 }
00235 }
00236 }
00237 }
00238
00239 linenum++;
00240
00241 }
00242 cout << "*** WARNING MotionSequenceEngine load missing #END" << endl;
00243 return origlen-len;
00244 }
00245
00246 unsigned int MotionSequenceEngine::saveBuffer(char buf[], unsigned int len) const {
00247
00248 unsigned int origlen=len;
00249 int written=snprintf(buf,len,"#MSq\n");
00250 if(!checkInc(written,buf,len,"*** ERROR MotionSequenceEngine save failed on header\n")) return 0; if(len==0 || len>origlen) {
00251 cout << "*** ERROR MotionSequenceEngine save overflow on header" << endl;
00252 return 0;
00253 }
00254 written=snprintf(buf,len,isSaveRadians()?"radians\n":"degrees\n");
00255 if(!checkInc(written,buf,len,"*** ERROR MotionSequenceEngine save failed on mode\n")) return 0; if(len==0 || len>origlen) {
00256 cout << "*** ERROR MotionSequenceEngine save overflow" << endl;
00257 return 0;
00258 }
00259 unsigned int t=0;
00260 Move_idx_t tprevs[NumOutputs];
00261 Move_idx_t tnexts[NumOutputs];
00262 bool hasInitialFrame=false;
00263 for(unsigned int i=0;i<NumOutputs;i++) {
00264 tnexts[i]=getKeyFrame(tprevs[i]=starts[i]).next;
00265 if(getKeyFrame(starts[i]).cmd.weight!=0)
00266 hasInitialFrame=true;
00267 }
00268 if(hasInitialFrame) {
00269 written=snprintf(buf,len,"setTime\t0\n");
00270 if(!checkInc(written,buf,len,"*** ERROR MotionSequenceEngine save failed on initial frame spec\n")) return 0;
00271 }
00272 while(t!=-1U) {
00273
00274 for(unsigned int i=0; i<NumOutputs; i++) {
00275
00276
00277 if((t!=0 || getKeyFrame(tprevs[i]).cmd.weight!=0) && getKeyFrame(tprevs[i]).starttime==t) {
00278 if(getKeyFrame(tprevs[i]).cmd.weight==1) {
00279 written=snprintf(buf,len,"%s\t%g\n",outputNames[i],getKeyFrame(tprevs[i]).cmd.value/loadSaveMode);
00280 if(!checkInc(written,buf,len,"*** ERROR MotionSequenceEngine save failed\n")) return 0;
00281 } else {
00282 written=snprintf(buf,len,"%s\t%g\t%g\n",outputNames[i],getKeyFrame(tprevs[i]).cmd.value/loadSaveMode,getKeyFrame(tprevs[i]).cmd.weight);
00283 if(!checkInc(written,buf,len,"*** ERROR MotionSequenceEngine save failed\n")) return 0;
00284 }
00285 if(len==0 || len>origlen) {
00286 cout << "*** ERROR MotionSequenceEngine save overflow" << endl;
00287 return 0;
00288 }
00289 }
00290 }
00291 unsigned int last=t;
00292 t=setNextFrameTime(tprevs,tnexts);
00293 if(t!=-1U) {
00294 written=snprintf(buf,len,"delay\t%d\n",t-last);
00295 if(!checkInc(written,buf,len,"*** ERROR MotionSequenceEngine save failed\n")) return 0;
00296 if(len==0 || len>origlen) {
00297 cout << "*** ERROR MotionSequenceEngine save overflow" << endl;
00298 return 0;
00299 }
00300 }
00301 }
00302 written=snprintf(buf,len,"#END\n");
00303 if(!checkInc(written,buf,len,"*** ERROR MotionSequenceEngine save failed on #END\n")) return 0;
00304 if(len==0 || len>origlen) {
00305 cout << "*** ERROR MotionSequenceEngine save overflow on #END" << endl;
00306 return 0;
00307 }
00308 return origlen-len;
00309 cout << "SAVE-done!" << endl;
00310 }
00311
00312 unsigned int MotionSequenceEngine::loadFile(const char filename[]) {
00313 return LoadSave::loadFile(config->motion.makePath(filename).c_str());
00314 }
00315 unsigned int MotionSequenceEngine::saveFile(const char filename[]) const {
00316 return LoadSave::saveFile(config->motion.makePath(filename).c_str());
00317 }
00318
00319
00320 void MotionSequenceEngine::setSaveDegrees() { loadSaveMode=(float)M_PI/180; }
00321
00322 void MotionSequenceEngine::setTime(unsigned int x) {
00323 playtime=x;
00324 const WorldState * st=WorldState::getCurrent();
00325 for(unsigned int i=0; i<NumOutputs; i++) {
00326 if(setRange(x,prevs[i],nexts[i])) {
00327 OutputCmd& cmd=getKeyFrame((playspeed<0)?nexts[i]:prevs[i]).cmd;
00328 if(cmd.weight<=0)
00329 cmd.value=st->outputs[i];
00330 }
00331 }
00332 }
00333
00334 void MotionSequenceEngine::setOutputCmd(unsigned int i, const OutputCmd& cmd) {
00335 Move& p_m=getKeyFrame(prevs[i]);
00336 if(playtime==p_m.starttime) {
00337 p_m.cmd=cmd;
00338 } else {
00339 Move_idx_t x = newKeyFrame();
00340 if(x==invalid_move)
00341 return;
00342 Move& cur=getKeyFrame(x);
00343 Move& prev_m=getKeyFrame(prevs[i]);
00344 cur.cmd=cmd;
00345 cur.starttime=playtime;
00346 cur.prev=prevs[i];
00347 cur.next=nexts[i];
00348 prev_m.next=x;
00349 if(nexts[i]!=invalid_move)
00350 getKeyFrame(nexts[i]).prev=x;
00351 else {
00352 if(playtime>endtime)
00353 endtime=playtime;
00354 }
00355 prevs[i]=x;
00356
00357 }
00358 curstamps[i]=-1U;
00359 }
00360
00361 void MotionSequenceEngine::setPose(const PostureEngine& pose) {
00362 for(unsigned int i=0; i<NumOutputs; i++)
00363 if(pose.getOutputCmd(i).weight>0)
00364 setOutputCmd(i,pose.getOutputCmd(i));
00365 }
00366
00367 void MotionSequenceEngine::setExplicitPose(const PostureEngine& pose) {
00368 for(unsigned int i=0; i<NumOutputs; i++)
00369 setOutputCmd(i,pose.getOutputCmd(i));
00370 }
00371
00372 PostureEngine MotionSequenceEngine::getPose() {
00373 PostureEngine pose;
00374 getPose(pose);
00375 return pose;
00376 }
00377
00378 void MotionSequenceEngine::getPose(PostureEngine& pose) {
00379 for(unsigned int i=0; i<NumOutputs; i++)
00380 pose.setOutputCmd(i,getOutputCmd(i));
00381 }
00382
00383 unsigned int MotionSequenceEngine::overlayMotion(const std::string& msFile) {
00384 DynamicMotionSequence ms;
00385 if(ms.loadFile(msFile.c_str())==0)
00386 return 0;
00387 overlayMotion(ms);
00388 return ms.getEndTime();
00389 }
00390
00391
00392
00393
00394
00395
00396
00397
00398 void MotionSequenceEngine::overlayMotion(const MotionSequenceEngine& ms) {
00399
00400 for(unsigned int i=0; i<NumOutputs; i++) {
00401
00402 Move_idx_t myPrevIdx=prevs[i];
00403 unsigned int myPrevTime=getKeyFrame(prevs[i]).starttime;
00404 Move_idx_t myNextIdx=getKeyFrame(myPrevIdx).next;
00405 unsigned int myNextTime=-1U;
00406 if(myNextIdx!=invalid_move)
00407 myNextTime=getKeyFrame(myNextIdx).starttime;
00408 for(Move_idx_t curOtherIdx=ms.starts[i]; curOtherIdx!=invalid_move; curOtherIdx=ms.getKeyFrame(curOtherIdx).next) {
00409 const Move& curOther=ms.getKeyFrame(curOtherIdx);
00410 while(myNextTime <= curOther.starttime+getTime()) {
00411
00412 myPrevIdx=myNextIdx;
00413 myPrevTime=myNextTime;
00414 myNextIdx=getKeyFrame(myPrevIdx).next;
00415 myNextTime= (myNextIdx==invalid_move ? -1U : getKeyFrame(myNextIdx).starttime);
00416 }
00417 if(curOther.cmd.weight > 0) {
00418 if(curOther.starttime+getTime() == myPrevTime) {
00419
00420
00421 getKeyFrame(myPrevIdx).cmd=curOther.cmd;
00422 } else {
00423
00424 Move_idx_t insIdx=newKeyFrame();
00425
00426 if(insIdx==invalid_move)
00427 return;
00428 Move& ins=getKeyFrame(insIdx);
00429 ins.prev=myPrevIdx;
00430 ins.next=myNextIdx;
00431 ins.cmd=curOther.cmd;
00432 ins.starttime=curOther.starttime+getTime();
00433 getKeyFrame(myPrevIdx).next=insIdx;
00434 if(myNextIdx!=invalid_move)
00435 getKeyFrame(myNextIdx).prev=insIdx;
00436 if(myPrevIdx==prevs[i]) {
00437 nexts[i]=insIdx;
00438 curstamps[i]=-1U;
00439 }
00440 myPrevIdx=insIdx;
00441 myPrevTime=ins.starttime;
00442 if(myPrevTime>endtime)
00443 endtime=myPrevTime;
00444 }
00445 }
00446 }
00447 }
00448 advanceTime(ms.getEndTime());
00449 }
00450
00451 void MotionSequenceEngine::compress() {
00452 for(unsigned int i=0; i<NumOutputs; i++) {
00453 Move_idx_t prev=getKeyFrame(starts[i]).next;
00454 if(prev==invalid_move)
00455 break;
00456 Move_idx_t cur=getKeyFrame(prev).next;
00457 if(cur==invalid_move)
00458 break;
00459 Move_idx_t next=getKeyFrame(cur).next;
00460 while(next!=invalid_move) {
00461 OutputCmd tmp;
00462 Move& prev_m=getKeyFrame(prev);
00463 Move& cur_m=getKeyFrame(cur);
00464 Move& next_m=getKeyFrame(next);
00465 calcOutput(tmp,cur_m.starttime,prev_m,next_m);
00466 if(tmp==cur_m.cmd || (tmp.weight==0 && cur_m.cmd.weight==0) ) {
00467 prev_m.next=next;
00468 next_m.prev=prev;
00469 eraseKeyFrame(cur);
00470 } else
00471 prev=cur;
00472 cur=next;
00473 next=next_m.next;
00474 }
00475 }
00476 }
00477
00478 void MotionSequenceEngine::makeSafe(const float vels[NumOutputs], float margin) {
00479 float comps[NumOutputs];
00480 for(unsigned int i=0;i<NumOutputs;i++)
00481 comps[i]=vels[i]*margin;
00482 unsigned int t=0;
00483 Move_idx_t tprevs[NumOutputs];
00484 Move_idx_t tnexts[NumOutputs];
00485 for(unsigned int i=0;i<NumOutputs;i++) {
00486 tnexts[i]=getKeyFrame(tprevs[i]=starts[i]).next;
00487 curstamps[i]=-1U;
00488 }
00489 while(t!=-1U) {
00490 for(unsigned int i=0; i<NumOutputs; i++) {
00491
00492 if(tnexts[i]!=invalid_move && (getKeyFrame(tprevs[i]).cmd.weight!=0 || getKeyFrame(tnexts[i]).cmd.weight!=0) && getKeyFrame(tprevs[i]).starttime==t) {
00493 float dv=std::abs(getKeyFrame(tprevs[i]).cmd.value-getKeyFrame(tnexts[i]).cmd.value);
00494 unsigned int dt=getKeyFrame(tnexts[i]).starttime-getKeyFrame(tprevs[i]).starttime;
00495 if(dv/dt>comps[i]) {
00496 unsigned int delay=(unsigned int)(dv/comps[i])-dt;
00497 for(unsigned int j=0; j<NumOutputs; j++)
00498 for(Move_idx_t c=tnexts[j]; c!=invalid_move; c=getKeyFrame(c).next)
00499 getKeyFrame(c).starttime+=delay;
00500 }
00501 }
00502 }
00503 t=setNextFrameTime(tprevs,tnexts);
00504 }
00505
00506 }
00507
00508 bool MotionSequenceEngine::isPlaying() {
00509 return playing && ((playspeed>0) ? (playtime<endtime) : (playtime>0));
00510 }
00511
00512
00513 void MotionSequenceEngine::play() {
00514 if(playspeed>0)
00515 setTime(0);
00516 else
00517 setTime(endtime);
00518 resume();
00519 }
00520
00521 void MotionSequenceEngine::resume() {
00522 playing=true;
00523 lasttime=get_time();
00524 const WorldState * st=WorldState::getCurrent();
00525 for(unsigned int i=0; i<NumOutputs; i++) {
00526 Move_idx_t cur=starts[i];
00527 while(cur!=invalid_move) {
00528 if(getKeyFrame(cur).cmd.weight!=0) {
00529 getKeyFrame(starts[i]).cmd.value=st->outputs[i];
00530 break;
00531 }
00532 cur=getKeyFrame(cur).next;
00533 }
00534 }
00535 }
00536
00537 unsigned int MotionSequenceEngine::setNextFrameTime(Move_idx_t p[NumOutputs], Move_idx_t n[NumOutputs]) const {
00538 unsigned int ans=-1U;
00539 for(unsigned int i=0; i<NumOutputs; i++)
00540 if(n[i]!=invalid_move && getKeyFrame(n[i]).starttime<ans)
00541 ans=getKeyFrame(n[i]).starttime;
00542 if(ans!=-1U) {
00543 const WorldState * st=WorldState::getCurrent();
00544 for(unsigned int i=0; i<NumOutputs; i++) {
00545 if(setRange(ans,p[i],n[i])) {
00546 const OutputCmd& cmd=getKeyFrame((playspeed<0)?n[i]:p[i]).cmd;
00547 if(cmd.weight<=0)
00548 const_cast<OutputCmd&>(cmd).value=st->outputs[i];
00549 }
00550 }
00551 }
00552 return ans;
00553 }
00554
00555 unsigned int MotionSequenceEngine::readWord(const char buf[], const char * const bufend, char wrd[], const unsigned int wordlen) {
00556 const char* origbuf=buf;
00557 wrd[0]='\0';
00558 unsigned int i;
00559
00560 for(;buf<bufend && isspace(*buf) && *buf!='\n' && *buf!='\r';buf++) {}
00561
00562 for(i=0; buf<bufend && !isspace(*buf); buf++)
00563 if(i<wordlen-1)
00564 wrd[i++]=*buf;
00565 wrd[i]='\0';
00566 if(buf>=bufend)
00567 return -1U;
00568 return buf-origbuf;
00569 }
00570
00571 unsigned int MotionSequenceEngine::getOutputIndex(const char name[], unsigned int idx) {
00572 unsigned int n=strlen(name);
00573 while(name[n-1]=='~')
00574 --n;
00575 if(idx<NumOutputs) {
00576 unsigned int startidx=idx;
00577 for(;idx<NumOutputs;idx++)
00578 if(strncmp(name,outputNames[idx],n)==0)
00579 return idx;
00580 for(idx=0;idx<startidx;idx++)
00581 if(strncmp(name,outputNames[idx],n)==0)
00582 return idx;
00583 return NumOutputs;
00584 } else {
00585 for(idx=0;idx<NumOutputs;idx++)
00586 if(strncmp(name,outputNames[idx],n)==0)
00587 return idx;
00588 return idx;
00589 }
00590 }
00591
00592
00593
00594
00595
00596