JNIEXPORT jint JNICALL Java_JMatLink_engOpenSingleUseNATIVE (JNIEnv *env, jobject obj, jstring startCmdS_JNI) { const char *openS = (*env)->GetStringUTFChars(env, startCmdS_JNI, 0); int retStatus = 0; int engineI; /* find unused entry in engine array */ engineI = getUnusedEnginePointer(); if (engineI==0) return 0; // no more pointers avaiblable if (!(engineP[engineI] = engOpenSingleUse(openS, NULL, &retStatus))) { if (debugB) fprintf(stderr, "\nCan't start MATLAB engine\n"); (*env)->ReleaseStringUTFChars(env, startCmdS_JNI, openS); // free memory delEnginePointer(engineI); return 0; } (*env)->ReleaseStringUTFChars(env, startCmdS_JNI, openS); // free memory return engineI; }
MatLabEngine::MatLabEngine( std::string host ) : _ep( 0 ), _matLabLogFileStreamPtr( 0 ) { static std::string startError = "Could not start MatLab Engine.\n" "The most common reasons for a MatLab exception are as follows:\n\n" " 1) MatLab isn't installed on this (the local) machine. MatLab *MUST*\n" " be installed on the machine on which MDL2MGA is run.\n" " IF YOU ARE RUNNING 64-bit WINDOWS XP, YOU MUST INSTALL THE\n" " 32-BIT VERSION OF MATLAB FOR MDL2MGA TO RUN.\n\n" " 2) If MatLab is installed on this machine, it may be that the\n" " MatLab service is not registered. To register the MatLab service,\n" " in a command-prompt type:\n" " matlab /regserver\n" " Once you have done this, try running MDL2MGA again.\n\n" " 3) If MatLab is installed on this machine and registered as a server,\n" " it may be that MatLab is taking a long time to initialize. So long,\n" " in fact, that MDL2MGA is timing out before MatLab is able to respond\n" " to it. To test this, re-execute MDL2MGA at least 3 more times.\n" " If MDL2MGA still will not run, please contact your support center.\n\n"; // int retcode; // if ( !( _ep = engOpenSingleUse( "\0", 0, &retcode ) ) ) { // throw Exception( "Cannot start MatLab engine (retcode = " + retcode + ')' ); // } // if ( !( _ep = engOpen( "\0" ) ) ) { #ifdef LINUX host += "\0"; if ( !( _ep = engOpen( host.c_str() ) ) ) { throw MatLabUdm::Exception( startError ); } #else // Windows int retcode; if ( !( _ep = engOpenSingleUse( "\0", 0, &retcode ) ) ) { throw std::exception( "Cannot start MatLab engine (retcode = " + retcode + ')' ); } #endif getBuffer()[ BUFSIZE ] = '\0'; engOutputBuffer( _ep, getBuffer(), BUFSIZE ); }
void mexFunction(int nlhs, mxArray * plhs[], int nrhs, const mxArray * prhs[]) { char command[STRLEN]; char matlabcmd[STRLEN]; char cmd[STRLEN]; char *ptr; int poolsize, retval, block, num, i, status; #ifndef USE_PTHREADS HANDLE TName; InitializeCriticalSection(&enginemutex); #endif initFun(); mexAtExit(exitFun); /* the first argument is always the command string */ if (nrhs < 1) mexErrMsgTxt("invalid number of input arguments"); if (!mxIsChar(prhs[0])) mexErrMsgTxt("invalid input argument #1"); if (mxGetString(prhs[0], command, STRLEN - 1)) mexErrMsgTxt("invalid input argument #1"); /* convert to lower case */ ptr = command; while (*ptr) { *ptr = tolower(*ptr); ptr++; } /****************************************************************************/ if (strcmp(command, "open") == 0) { /* engine open num cmd */ if (nrhs < 2) mexErrMsgTxt("Invalid number of input arguments"); if (nrhs > 1) { if (!mxIsNumeric(prhs[1])) mexErrMsgTxt("Invalid input argument #2, should be numeric"); poolsize = mxGetScalar(prhs[1]); } if (nrhs > 2) { if (!mxIsChar(prhs[2])) mexErrMsgTxt("Invalid input argument #3, should be a string"); mxGetString(prhs[2], matlabcmd, STRLEN - 1); } else { sprintf(matlabcmd, "matlab"); } if (poolsize < 1) mexErrMsgTxt("The number of engines in the pool should be positive"); ENGINEMUTEX_LOCK; engine = engOpenSingleUse(NULL, NULL, &retval); /* returns NULL on failure */ ENGINEMUTEX_UNLOCK; if (!engine) { exitFun(); /* this cleans up all engines */ mexErrMsgTxt("failed to open MATLAB engine"); } } /****************************************************************************/ else if (strcmp(command, "close") == 0) { /* engine close */ exitFun(); retval = 0; plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL); mxGetPr(plhs[0])[0] = retval; } /****************************************************************************/ else if (strcmp(command, "put") == 0) { /* engine put num name val */ if (nrhs != 4) mexErrMsgTxt("Exactly four input arguments needed"); if (!mxIsNumeric(prhs[1])) mexErrMsgTxt("Argument #2 should be numeric"); num = mxGetScalar(prhs[1]); if (num < 1 || num > poolsize) mexErrMsgTxt("Invalid engine number"); if (!mxIsChar(prhs[2])) mexErrMsgTxt("Argument #3 should be a string"); ENGINEMUTEX_LOCK; retval = engPutVariable(engine, mxArrayToString(prhs[2]), prhs[3]); plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL); mxGetPr(plhs[0])[0] = retval; ENGINEMUTEX_UNLOCK; } /****************************************************************************/ else if (strcmp(command, "get") == 0) { /* engine get num name */ if (nrhs != 3) mexErrMsgTxt("Exactly three input arguments needed"); if (!mxIsNumeric(prhs[1])) mexErrMsgTxt("Argument #2 should be numeric"); if (!mxIsChar(prhs[2])) mexErrMsgTxt("Argument #3 should be a string"); num = mxGetScalar(prhs[1]); ENGINEMUTEX_LOCK; plhs[0] = engGetVariable(engine, mxArrayToString(prhs[2])); ENGINEMUTEX_UNLOCK; } /****************************************************************************/ else if (strcmp(command, "isbusy") == 0) { /* engine isbusy num */ if (nrhs != 2) mexErrMsgTxt("Exactly two input arguments needed"); if (!mxIsNumeric(prhs[1])) mexErrMsgTxt("Argument #2 should be numeric"); num = mxGetScalar(prhs[1]); ENGINEMUTEX_LOCK; plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL); mxGetPr(plhs[0])[0] = 1; ENGINEMUTEX_UNLOCK; } /****************************************************************************/ else if (strcmp(command, "eval") == 0) { /* engine eval num str block */ if (nrhs < 3) mexErrMsgTxt("At least three input arguments needed"); if (!mxIsNumeric(prhs[1])) mexErrMsgTxt("Argument #2 should be numeric"); num = mxGetScalar(prhs[1]); if (!mxIsChar(prhs[2])) mexErrMsgTxt("Invalid input argument #3, should be a string"); mxGetString(prhs[2], cmd, STRLEN - 1); if (nrhs > 3) { if (!mxIsNumeric(prhs[3])) mexErrMsgTxt("Invalid input argument #4, should be numeric"); block = mxGetScalar(prhs[3]); } else { block = 0; } ENGINEMUTEX_LOCK; if (!block) { #ifdef USE_PTHREADS retval = pthread_create(&(enginepool[num - 1].tid), NULL, (void *) &evalString, (void *) (enginepool + num - 1)); #else TName = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) evalString, (void *) (cmd), 0, NULL); #endif } else { retval = engEvalString(engine, cmd); } mexPrintf("Finished executing command, retval = %d\n", retval); ENGINEMUTEX_UNLOCK; WaitForSingleObject(TName, INFINITE); /* wait for the thread to become busy */ mexPrintf("Waiting for engine to become busy\n"); BUSYCOND_WAIT; mexPrintf("The engine has become busy\n"); plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL); mxGetPr(plhs[0])[0] = retval; } /****************************************************************************/ else if (strcmp(command, "poolsize") == 0) { /* engine poolsize */ ENGINEMUTEX_LOCK; plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL); mxGetPr(plhs[0])[0] = (engine!=0); ENGINEMUTEX_UNLOCK; } /****************************************************************************/ else if (strcmp(command, "info") == 0) { /* engine info */ ENGINEMUTEX_LOCK; mexPrintf("engine = %d\n", engine); ENGINEMUTEX_UNLOCK; } /****************************************************************************/ else { mexErrMsgTxt("unknown command"); return; } return; }
EXPORT CLASS *init(CALLBACKS *fntable, MODULE *module, int argc, char *argv[]) { if (set_callback(fntable)==NULL) { errno = EINVAL; return NULL; } // open a connection to the Matlab engine int status=0; static char server[1024]; if (gl_global_getvar("matlab_server",server,sizeof(server))) matlab_server = server; if (strcmp(matlab_server,"standalone")==0) engine = engOpenSingleUse(NULL,NULL,&status); else engine = engOpen(matlab_server); if (engine==NULL) { gl_error("unable to start Matlab engine (code %d)",status); return NULL; } // prepare session char debug[8]; if (gl_global_getvar("debug",debug,sizeof(debug))) debugmode = (atoi(debug)==1); engSetVisible(engine,debugmode?1:0); engEvalString(engine,"clear all;"); char env[1024]; _snprintf(env,sizeof(env),"NEVER=%g;INVALID=%g;",TOSERIAL(TS_NEVER),TOSERIAL(TS_INVALID)); engEvalString(engine,env); // collect output from Matlab engOutputBuffer(engine,output,sizeof(output)); // setup the Matlab module and run the class constructor engEvalString(engine,"global passconfig;"); if (engEvalString(engine,argv[0])!=0) gl_error("unable to evaluate function '%s' in Matlab", argv[0]); else gl_matlab_output(); // read the pass configuration mxArray *pcfg= engGetVariable(engine,"passconfig"); if (pcfg && mxIsChar(pcfg)) { char passinfo[1024]; KEYWORD keys[] = { {"NOSYNC",PC_NOSYNC,keys+1}, {"PRETOPDOWN",PC_PRETOPDOWN,keys+2}, {"BOTTOMUP",PC_BOTTOMUP,keys+3}, {"POSTTOPDOWN",PC_POSTTOPDOWN,NULL}, }; PROPERTY pctype = {0,"passconfig",PT_set,1,PA_PUBLIC,NULL,&passconfig,NULL,keys,NULL}; set passdata; if (mxGetString(pcfg,passinfo,sizeof(passinfo))==0 && callback->convert.string_to_property(&pctype,&passdata,passinfo)>0) { passconfig = (PASSCONFIG)passdata; oclass=gl_register_class(module,argv[0],passconfig); if (oclass==NULL) gl_error("unable to register '%s' as a class",argv[0]); DELEGATEDTYPE *pDelegate = new DELEGATEDTYPE; pDelegate->oclass = oclass; strncpy(pDelegate->type,"matlab",sizeof(pDelegate->type)); pDelegate->from_string = object_from_string; pDelegate->to_string = object_to_string; if (gl_publish_variable(oclass,PT_delegated,pDelegate,"data",0,NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__); } else gl_error("passconfig is invalid (expected set of NOSYNC, PRETOPDOWN, BOTTOMUP, and POSTTOPDOWN)", passinfo); } else gl_error("passconfig not specified"); // read the pass configuration mxArray *ans= engGetVariable(engine,"ans"); if (ans && mxIsStruct(ans)) { defaults = mxDuplicateArray(ans); // process the answer int nFields = mxGetNumberOfFields(ans), i; for (i=0; i<nFields; i++) { const char *name = mxGetFieldNameByNumber(ans,i); mxArray *data = mxGetFieldByNumber(ans,0,i); // @todo publish the structure } } else gl_error("result of call to matlab::%s did not return a structure", argv[0]); #ifdef OPTIONAL /* TODO: publish global variables (see class_define_map() for details) */ gl_global_create(char *name, ..., NULL); /* TODO: use gl_global_setvar, gl_global_getvar, and gl_global_find for access */ #endif /* always return the first class registered */ return oclass; }
EXPORT bool glx_init(glxlink *mod) { gl_verbose("initializing matlab link"); gl_verbose("PATH=%s", getenv("PATH")); // initialize matlab engine MATLABLINK *matlab = (MATLABLINK*)mod->get_data(); matlab->status = 0; #ifdef WIN32 if ( matlab->command ) matlab->engine = engOpen(matlab->command); else matlab->engine = engOpenSingleUse(NULL,NULL,&matlab->status); if ( matlab->engine==NULL ) { gl_error("matlab engine start failed, status code is '%d'", matlab->status); return false; } #else matlab->engine = engOpen(matlab->command); if ( matlab->engine==NULL ) { gl_error("matlab engine start failed"); return false; } #endif // set the output buffer if ( matlab->output_buffer!=NULL ) engOutputBuffer(matlab->engine,matlab->output_buffer,(int)matlab->output_size); // setup matlab engine engSetVisible(matlab->engine,window_show(matlab)); gl_debug("matlab link is open"); // special values needed by matlab mxArray *ts_never = mxCreateDoubleScalar((double)(TIMESTAMP)TS_NEVER); engPutVariable(matlab->engine,"TS_NEVER",ts_never); mxArray *ts_error = mxCreateDoubleScalar((double)(TIMESTAMP)TS_INVALID); engPutVariable(matlab->engine,"TS_ERROR",ts_error); mxArray *gld_ok = mxCreateDoubleScalar((double)(bool)true); engPutVariable(matlab->engine,"GLD_OK",gld_ok); mxArray *gld_err = mxCreateDoubleScalar((double)(bool)false); engPutVariable(matlab->engine,"GLD_ERROR",gld_err); // set the workdir if ( strcmp(matlab->workdir,"")!=0 ) { #ifdef WIN32 _mkdir(matlab->workdir); #else mkdir(matlab->workdir,0750); #endif if ( matlab->workdir[0]=='/' ) matlab_exec(matlab,"cd '%s'", matlab->workdir); else matlab_exec(matlab,"cd '%s/%s'", getcwd(NULL,0),matlab->workdir); } // run the initialization command(s) if ( matlab->init ) { mxArray *ans = matlab_exec(matlab,"%s",matlab->init); if ( ans && mxIsDouble(ans) && (bool)*mxGetPr(ans)==false ) { gl_error("matlab init failed"); return false; } else if ( ans && mxIsChar(ans) ) { int buflen = (mxGetM(ans) * mxGetN(ans)) + 1; char *string =(char*)malloc(buflen); int status_error = mxGetString(ans, string, buflen); if (status_error == 0) { gl_error("'%s'",string); return false; } else { gl_error("Did not catch Matlab error"); return false; } } } if ( matlab->rootname!=NULL ) { // build gridlabd data mwSize dims[] = {1,1}; mxArray *gridlabd_struct = mxCreateStructArray(2,dims,0,NULL); /////////////////////////////////////////////////////////////////////////// // build global data LINKLIST *item; mxArray *global_struct = mxCreateStructArray(2,dims,0,NULL); for ( item=mod->get_globals() ; item!=NULL ; item=mod->get_next(item) ) { char *name = mod->get_name(item); GLOBALVAR *var = mod->get_globalvar(item); mxArray *var_struct = NULL; mwIndex var_index; if ( var==NULL ) continue; // do not map module or structured globals if ( strchr(var->prop->name,':')!=NULL ) { // ignore module globals here } else if ( strchr(var->prop->name,'.')!=NULL ) { char struct_name[256]; if ( sscanf(var->prop->name,"%[^.]",struct_name)==0 ) { gld_property prop(var); var_index = mxAddField(global_struct,prop.get_name()); var_struct = matlab_create_value(&prop); if ( var_struct!=NULL ) { //mod->add_copyto(var->prop->addr,mxGetData(var_struct)); mxSetFieldByNumber(global_struct,0,var_index,var_struct); } } } else // simple data { gld_property prop(var); var_index = mxAddField(global_struct,prop.get_name()); var_struct = matlab_create_value(&prop); if ( var_struct!=NULL ) { //mod->add_copyto(var->prop->addr,mxGetData(var_struct)); mxSetFieldByNumber(global_struct,0,var_index,var_struct); } } // update export list if ( var_struct!=NULL ) { mod->set_addr(item,(void*)var_struct); mod->set_index(item,(size_t)var_index); } } // add globals structure to gridlabd structure mwIndex gridlabd_index = mxAddField(gridlabd_struct,"global"); mxSetFieldByNumber(gridlabd_struct,0,gridlabd_index,global_struct); /////////////////////////////////////////////////////////////////////////// // build module data dims[0] = dims[1] = 1; mxArray *module_struct = mxCreateStructArray(2,dims,0,NULL); // add modules for ( MODULE *module = callback->module.getfirst() ; module!=NULL ; module=module->next ) { // create module info struct mwIndex dims[] = {1,1}; mxArray *module_data = mxCreateStructArray(2,dims,0,NULL); mwIndex module_index = mxAddField(module_struct,module->name); mxSetFieldByNumber(module_struct,0,module_index,module_data); // create version info struct const char *version_fields[] = {"major","minor"}; mxArray *version_data = mxCreateStructArray(2,dims,sizeof(version_fields)/sizeof(version_fields[0]),version_fields); mxArray *major_data = mxCreateDoubleScalar((double)module->major); mxArray *minor_data = mxCreateDoubleScalar((double)module->minor); mxSetFieldByNumber(version_data,0,0,major_data); mxSetFieldByNumber(version_data,0,1,minor_data); // attach version info to module info mwIndex version_index = mxAddField(module_data,"version"); mxSetFieldByNumber(module_data,0,version_index,version_data); } gridlabd_index = mxAddField(gridlabd_struct,"module"); mxSetFieldByNumber(gridlabd_struct,0,gridlabd_index,module_struct); /////////////////////////////////////////////////////////////////////////// // build class data dims[0] = dims[1] = 1; mxArray *class_struct = mxCreateStructArray(2,dims,0,NULL); gridlabd_index = mxAddField(gridlabd_struct,"class"); mxSetFieldByNumber(gridlabd_struct,0,gridlabd_index,class_struct); mwIndex class_id[1024]; // index into class struct memset(class_id,0,sizeof(class_id)); // add classes for ( CLASS *oclass = callback->class_getfirst() ; oclass!=NULL ; oclass=oclass->next ) { // count objects in this class mwIndex dims[] = {0,1}; for ( item=mod->get_objects() ; item!=NULL ; item=mod->get_next(item) ) { OBJECT *obj = mod->get_object(item); if ( obj==NULL || obj->oclass!=oclass ) continue; dims[0]++; } if ( dims[0]==0 ) continue; mxArray *runtime_struct = mxCreateStructArray(2,dims,0,NULL); // add class mwIndex class_index = mxAddField(class_struct,oclass->name); mxSetFieldByNumber(class_struct,0,class_index,runtime_struct); // add properties to class for ( PROPERTY *prop=oclass->pmap ; prop!=NULL && prop->oclass==oclass ; prop=prop->next ) { mwIndex dims[] = {1,1}; mxArray *property_struct = mxCreateStructArray(2,dims,0,NULL); mwIndex runtime_index = mxAddField(runtime_struct,prop->name); mxSetFieldByNumber(runtime_struct,0,runtime_index,property_struct); } // add objects to class for ( item=mod->get_objects() ; item!=NULL ; item=mod->get_next(item) ) { OBJECT *obj = mod->get_object(item); if ( obj==NULL || obj->oclass!=oclass ) continue; mwIndex index = class_id[obj->oclass->id]++; // add properties to class for ( PROPERTY *prop=oclass->pmap ; prop!=NULL && prop->oclass==oclass ; prop=prop->next ) { gld_property p(obj,prop); mxArray *data = matlab_create_value(&p); mxSetField(runtime_struct,index,prop->name,data); } // update export list mod->set_addr(item,(void*)runtime_struct); mod->set_index(item,(size_t)index); } } /////////////////////////////////////////////////////////////////////////// // build the object data dims[0] = 0; for ( item=mod->get_objects() ; item!=NULL ; item=mod->get_next(item) ) { if ( mod->get_object(item)!=NULL ) dims[0]++; } dims[1] = 1; memset(class_id,0,sizeof(class_id)); const char *objfields[] = {"name","class","id","parent","rank","clock","valid_to","schedule_skew", "latitude","longitude","in","out","rng_state","heartbeat","lock","flags"}; mxArray *object_struct = mxCreateStructArray(2,dims,sizeof(objfields)/sizeof(objfields[0]),objfields); mwIndex n=0; for ( item=mod->get_objects() ; item!=NULL ; item=mod->get_next(item) ) { OBJECT *obj = mod->get_object(item); if ( obj==NULL ) continue; class_id[obj->oclass->id]++; // index into class struct const char *objname[] = {obj->name&&isdigit(obj->name[0])?NULL:obj->name}; const char *oclassname[] = {obj->oclass->name}; if (obj->name) mxSetFieldByNumber(object_struct,n,0,mxCreateCharMatrixFromStrings(mwSize(1),objname)); mxSetFieldByNumber(object_struct,n,1,mxCreateCharMatrixFromStrings(mwSize(1),oclassname)); mxSetFieldByNumber(object_struct,n,2,mxCreateDoubleScalar((double)class_id[obj->oclass->id])); if (obj->parent) mxSetFieldByNumber(object_struct,n,3,mxCreateDoubleScalar((double)obj->parent->id+1)); mxSetFieldByNumber(object_struct,n,4,mxCreateDoubleScalar((double)obj->rank)); mxSetFieldByNumber(object_struct,n,5,mxCreateDoubleScalar((double)obj->clock)); mxSetFieldByNumber(object_struct,n,6,mxCreateDoubleScalar((double)obj->valid_to)); mxSetFieldByNumber(object_struct,n,7,mxCreateDoubleScalar((double)obj->schedule_skew)); if ( isfinite(obj->latitude) ) mxSetFieldByNumber(object_struct,n,8,mxCreateDoubleScalar((double)obj->latitude)); if ( isfinite(obj->longitude) ) mxSetFieldByNumber(object_struct,n,9,mxCreateDoubleScalar((double)obj->longitude)); mxSetFieldByNumber(object_struct,n,10,mxCreateDoubleScalar((double)obj->in_svc)); mxSetFieldByNumber(object_struct,n,11,mxCreateDoubleScalar((double)obj->out_svc)); mxSetFieldByNumber(object_struct,n,12,mxCreateDoubleScalar((double)obj->rng_state)); mxSetFieldByNumber(object_struct,n,13,mxCreateDoubleScalar((double)obj->heartbeat)); mxSetFieldByNumber(object_struct,n,14,mxCreateDoubleScalar((double)obj->lock)); mxSetFieldByNumber(object_struct,n,15,mxCreateDoubleScalar((double)obj->flags)); n++; } gridlabd_index = mxAddField(gridlabd_struct,"object"); mxSetFieldByNumber(gridlabd_struct,0,gridlabd_index,object_struct); /////////////////////////////////////////////////////////////////////////// // post the gridlabd structure matlab->root = gridlabd_struct; engPutVariable(matlab->engine,matlab->rootname,matlab->root); } /////////////////////////////////////////////////////////////////////////// // build the import/export data for ( LINKLIST *item=mod->get_exports() ; item!=NULL ; item=mod->get_next(item) ) { OBJECTPROPERTY *objprop = mod->get_export(item); if ( objprop==NULL ) continue; // add to published items gld_property prop(objprop->obj,objprop->prop); item->addr = (mxArray*)matlab_create_value(&prop); engPutVariable(matlab->engine,item->name,(mxArray*)item->addr); } for ( LINKLIST *item=mod->get_imports() ; item!=NULL ; item=mod->get_next(item) ) { OBJECTPROPERTY *objprop = mod->get_import(item); if ( objprop==NULL ) continue; // check that not already in export list LINKLIST *export_item; bool found=false; for ( export_item=mod->get_exports() ; export_item!=NULL ; export_item=mod->get_next(export_item) ) { OBJECTPROPERTY *other = mod->get_export(item); if ( memcmp(objprop,other,sizeof(OBJECTPROPERTY)) ) found=true; } if ( !found ) { gld_property prop(objprop->obj,objprop->prop); item->addr = (mxArray*)matlab_create_value(&prop); engPutVariable(matlab->engine,item->name,(mxArray*)item->addr); } } static int32 matlab_flag = 1; gl_global_create("MATLAB",PT_int32,&matlab_flag,PT_ACCESS,PA_REFERENCE,PT_DESCRIPTION,"indicates that MATLAB is available",NULL); mod->last_t = gl_globalclock; return true; }