// a CDEV access interface for tcl // Johannes van Zeijts, March 95, first version // May 95, second version, using Walt Akers callback example // Dec 95, added arbitrary dimensional array output and context // Jan 96, added handling of strings with spaces // Aug 96, deleted callbackinfo when we are not monitoring // Aug 96, converted to tcl7.5 // Nov 1, 96, moved to cdev 1.4 // March, 97, overhaul, allowed multiple devices /* Output formatting Attention given to 'Strings with spaces' which have special meaning in tcl #devices = 1 #tags = 0 (only value tag) dim = 0 1 String String with spaces dim = 1 1 1 1 St1 St2 St3 {St w sp1} {St w sp2} {St w sp3} dim = 2 {11 12 13} {21 22 23} {st1 st2 st3} {...} {{st w sp1} {st w sp2} {st w sp3}} { ...} #tags > 0 (value tag + other tags) Each tag in its own list index Result is always list of length (#tags + 1) dim = 0 1 St1 {St w sp1} dim = 1 {1 1 1} {St1 St2 St3} {{St w sp1} {St w sp2} {St w sp3}} dim = 2 {{1 1 1} {1 1 1}} {{{st w sp1} {st w sp2} {st w sp3}} {{st w sp1} {st w sp2} {st w sp3}}} --------------------------------------------------------------------------------------- #devices > 1 , #tags = 0 dim = 0 1 1 1 {str w sp} {str w sp} dim = 1 {1 1 1} {1 1 1} {{str w sp} ..} { } #tags > 0 dim = 0 {0 1 2} {0 1 2} dim = 1 {{1 1 1} {1 1 1}} */ #include #include #include #include #include #include // cdev includes #include #include #include #include #include static int VALUE_TAG_ID =- 1; #define VALUE_TAG ((VALUE_TAG_ID<0 && cdevData::tagC2I("value", &VALUE_TAG_ID)!= CDEV_SUCCESS)?-1:VALUE_TAG_ID) static int STATUS_TAG_ID =- 1; #define STATUS_TAG ((STATUS_TAG_ID<0 && cdevData::tagC2I("status", &STATUS_TAG_ID)!= CDEV_SUCCESS)?-1:STATUS_TAG_ID) static int SEVERITY_TAG_ID =- 1; #define SEVERITY_TAG ((SEVERITY_TAG_ID<0 && cdevData::tagC2I("severity", &SEVERITY_TAG_ID)!= CDEV_SUCCESS)?-1:SEVERITY_TAG_ID) static int cdevdebug = 0; class tclCdevData { public: tclCdevData(); void parse(Tcl_Interp*, char* *, int); ~tclCdevData(); cdevData out, context; int hascontext; int tagn, *taglist; char *callbacktag; int state, async; } ; tclCdevData::~tclCdevData() { delete taglist; } tclCdevData::tclCdevData():hascontext(0), tagn(0), taglist(NULL), async(0), state(TCL_ERROR) { } ; // Parses command line, fills up the outgoing cdevData and the context (if any) void tclCdevData::parse(Tcl_Interp *interp, char **argv, int argc) { int i, c1, c2; cdevData *ptr; char **s1 = NULL; char **s2 = NULL; // Point to 'out' cdevData until we hit the context (if any) ptr = &out; for(i = 3; i < argc; i++) { // skip 'cdev' '$device' and '$message' if(Tcl_SplitList(interp, argv[i], &c1, &s1) != TCL_OK) { return; } if(c1 == 2) { // {tag value} pair, check if the value is a scalar if(Tcl_SplitList(interp, s1[1], &c2, &s2) != TCL_OK) { free((char *) s1); return; } if(c2 == 1) { // we have a scalar ptr->insert(s1[0], s1[1]); // insert scalar } else { ptr->insert(s1[0], s2, c2); // insert vector of scalars, does not handle matrices } free((char *) s2); } else if(c1 == 1) { // can be one of "-context" , "-tags" or a callbacktag if(strcmp(argv[i], "-context") == 0) { // all remaining 'tag value' pairs go in the context ptr = &context; hascontext = 1; } else if(strcmp(argv[i], "-tags") == 0) { // get the list of tags to print out from the result data i++; // points to next entry in argv if(i == argc) { interp->result = "expected tag names"; free((char *) s1); return; } if(Tcl_SplitList(interp, argv[i], &tagn, &s2) != TCL_OK) { interp->result = "tag problem"; free((char *) s1); return; } else { taglist = new int[tagn]; for(int j = 0; j < tagn; j++) { if(cdevData::tagC2I(s2[j], &taglist[j]) != CDEV_SUCCESS) { interp->result = "unknown tag"; free((char *) s2); free((char *) s1); return; } } } free((char *) s2); } else { // single entry, a callbacktag, better be at end of input if(i != argc-1) { interp->result = "expecting callbacktag at end"; free((char *) s1); return; } // Here we have a callback tag, hence we want to send asynchronously callbacktag = argv[i]; async = 1; } } else { // c1 > 2 // tag / string pair (i.e. "value this is a string with spaces embedded" char **av = s1; av++; char *concat = Tcl_Concat(c1-1, av); ptr->insert(s1[0], concat); if(cdevdebug == 1) { fprintf(stderr, "inserted: %s, with value: %s\n", s1[0], concat); } free(concat); } free((char *) s1); } state = TCL_OK; } class callbackInfo { public: callbackInfo(Tcl_Interp*, char *); callbackInfo(Tcl_Interp*, char*, char *); ~callbackInfo(); Tcl_Interp *interp; char *s; cdevCallback *callback; void addtags(int, int *); int *tags, ntags; } ; callbackInfo::callbackInfo(Tcl_Interp *e, char *arg) { interp = e; s = new char[strlen(arg)+1]; strcpy(s, arg); ntags = 0; tags = NULL; callback = NULL; } callbackInfo::callbackInfo(Tcl_Interp *e, char *arg1, char *arg2) { interp = e; s = new char[strlen(arg1)+strlen(arg2)+1]; strcpy(s, arg1); strcat(s, arg2); ntags = 0; tags = NULL; callback = NULL; } callbackInfo::~callbackInfo() { delete tags; delete s; } void callbackInfo::addtags(int n, int *v) { ntags = n; tags = new int[n]; for(int i = 0; i < ntags; i++) { tags[i] = v[i]; } delete callback; } static char cs[512]; cdevBounds *Bounds; int Dim; static int K = 0; void HandleLevel(char **in, int level, Tcl_DString &ds) { int i; for(i = 0; i < Bounds[level].length; i++) { if(level >= Dim-1) { Tcl_DStringAppendElement(&ds, in[K]); K++; } else { Tcl_DStringStartSublist(&ds); HandleLevel(in, level+1, ds); Tcl_DStringEndSublist(&ds); } } } // Format a cdevData tag result into a tcl result // mode == #ntags void HandleTag(int tag, cdevData &result, Tcl_DString &ds, int mode, int ndevices) { int i, len; size_t dim; char **sp; if(result.getDim(tag, &dim) != CDEV_SUCCESS) { cs[0] = NULL; if (ndevices == 1 && mode == 0) { Tcl_DStringAppend(&ds, cs, -1); // append an empty string } else { Tcl_DStringAppendElement(&ds, cs); // append an empty list } return; } // Always retrieve the result as a char*, cdevData does the conversion for me! if(dim == 0) { /* * // put this in if we want format conversions. * i = result.getType(tag); * if ((i == CDEV_FLOAT) || (i == CDEV_DOUBLE)) { * result.get(tag,&dres); * sprintf(cs,"%.2g",dres); * } else { */ cs[0] = NULL; result.get(tag, cs, 255); if (mode == 0 && ndevices == 1) { Tcl_DStringAppend(&ds,cs,-1); } else { Tcl_DStringAppendElement(&ds,cs); } return; } else { // dim > 0 Dim = (int) dim; K = 0; Bounds = new cdevBounds[Dim]; result.getBounds(tag, Bounds, Dim); len = 1; for(i = 0; i < Dim; i++) { len *= Bounds[i].length; } sp = new char*[len]; result.get(tag, sp); if (mode>0 || (mode==0&&ndevices>1)) Tcl_DStringStartSublist(&ds); HandleLevel(sp, 0, ds); if (mode>0 || (mode==0&&ndevices>1)) Tcl_DStringEndSublist(&ds); // append the result to a Tcl_DString } delete Bounds; while(len > 0) { len--; delete sp[len]; } delete [] sp; } int Cdev_Handleresult(int *tags, int ntags, cdevData &result, Tcl_DString &ds, int ndevices) { // assume we always return the "value" tag if (ndevices>1 && ntags>0) Tcl_DStringStartSublist(&ds); HandleTag(VALUE_TAG, result, ds, ntags, ndevices); if(ntags > 0) { for(int i = 0; i < ntags; i++) { HandleTag(tags[i], result, ds, ntags, ndevices); } } if (ndevices>1 && ntags>0) Tcl_DStringEndSublist(&ds); return TCL_OK; } // executes inside poll() whenever 'file' is readable void Cdev_CallbackFunction(int, void *userarg, cdevRequestObject&, cdevData &result) { Tcl_DString ds; // could check status and do appropriate action like: set Error(info->s) ??? callbackInfo *info = (callbackInfo *) userarg; Tcl_DStringInit(&ds); int i = Cdev_Handleresult(info->tags, info->ntags, result, ds, 1); Tcl_SetVar2(info->interp, "Control", info->s, Tcl_DStringValue(&ds), TCL_GLOBAL_ONLY); Tcl_DStringFree(&ds); } // same as above, plus removes callbackinfo void Cdev_CallbackFunctionOnce(int, void *userarg, cdevRequestObject&, cdevData &result) { Tcl_DString ds; // have to check status and do appropriate action like: set Error(info->s) ??? callbackInfo *info = (callbackInfo *) userarg; Tcl_DStringInit(&ds); int i = Cdev_Handleresult(info->tags, info->ntags, result, ds, 1); Tcl_SetVar2(info->interp, "Control", info->s, Tcl_DStringValue(&ds), TCL_GLOBAL_ONLY); Tcl_DStringFree(&ds); delete info; } // Old way of polling, superseded by FileHandler calls, but since FD's are not working yet, still in use void Cdev_UpdateProc(ClientData dummy) { cdevSystem::defaultSystem().poll(); Tcl_CreateTimerHandler(200, Cdev_UpdateProc, dummy); } void Cdev_FileProc(ClientData, int) { cdevSystem::defaultSystem().poll(); } int CdevDebugCmd(ClientData, Tcl_Interp*, int, char* *) { if(cdevdebug == 0) { cdevdebug = 1; } else { cdevdebug = 0; } return TCL_OK; } void Cdev_RegisterFD(int*, Tcl_File fd, int condition) { printf("fd callback\n"); if(condition) { Tcl_CreateFileHandler(fd, TCL_READABLE, Cdev_FileProc, NULL); } else { Tcl_DeleteFileHandler(fd); } } typedef cdevRequestObject *cdevRequestObjectPtr; int CdevCmd(ClientData, Tcl_Interp *interp, int argc, char **argv) { // cdev device message {tag value} {..} cb // cdev device message {tag value} {..} -context (tag value} {..} cb char **devices = NULL; int i, n, ndevices; Tcl_DString ds; cdevData result; cdevCallback *cb = NULL; callbackInfo *info; cdevRequestObjectPtr request, *requests; tclCdevData data; int delinfo = 0; if(argc < 3) { Tcl_AppendResult(interp, "cdev: wrong # args", (char *) NULL); return TCL_ERROR; } ndevices = 0; if(Tcl_SplitList(interp, argv[1], &ndevices, &devices) != TCL_OK) { return TCL_ERROR; } if(ndevices == 1) { request = cdevRequestObject::attachPtr(argv[1], argv[2]); free((char*)devices); } else { requests = new cdevRequestObjectPtr[ndevices]; for(n = 0; n < ndevices; n++) { requests[n] = cdevRequestObject::attachPtr(devices[n], argv[2]); } } data.parse(interp, argv, argc); if(data.state != TCL_OK) { return TCL_ERROR; } if(data.async) { // we want to sent it async if(ndevices == 1) { info = new callbackInfo(interp, data.callbacktag); if(data.tagn != 0) { info->addtags(data.tagn, data.taglist); } if(strncmp(argv[2], "monitorOn", 9) == 0) { cb = new cdevCallback(Cdev_CallbackFunction, (void *) info); } else if(strncmp(argv[2], "monitorOff", 10) == 0) { delinfo = 1; cb = new cdevCallback(NULL, NULL); } else { cb = new cdevCallback(Cdev_CallbackFunctionOnce, (void *) info); } info->callback = cb; if(data.hascontext) { if(cdevdebug == 1) { cout << "Context data: " << endl; data.context.asciiDump(stdout); } if(request->setContext(data.context) != CDEV_SUCCESS) { interp->result = "setContext error"; delete info; return TCL_ERROR; } } if(cdevdebug == 1) { cout << argv[1] << " " << argv[2] << " OutBound data: " << endl; data.out.asciiDump(stdout); } if(request->sendCallback(data.out, *cb) != 0) { delete info; return TCL_ERROR; } else { if(delinfo) delete info; return TCL_OK; } } else { // ndevices > 1, send each one separately int j = 0; if(strncmp(argv[2], "monitorOn", 9) == 0) { j = 1; } else if(strncmp(argv[2], "monitorOff", 10) == 0) { j = 2; } for(n = 0; n < ndevices; n++) { info = new callbackInfo(interp, devices[n], data.callbacktag); if(data.tagn != 0) { info->addtags(data.tagn, data.taglist); } if(j == 1) { cb = new cdevCallback(Cdev_CallbackFunction, (void *) info); } else if(j == 2) { delinfo = 1; cb = new cdevCallback(NULL, NULL); } else { cb = new cdevCallback(Cdev_CallbackFunctionOnce, (void *) info); } info->callback = cb; if(data.hascontext) { if(requests[n]->setContext(data.context) != 0) { interp->result = "setContext error"; delete requests; free((char *) devices); delete info; return TCL_ERROR; } } if(requests[n]->sendCallback(data.out, *cb) != CDEV_SUCCESS) { delete requests; delete info; free((char *) devices); return TCL_ERROR; } else { if(delinfo) delete info; } } delete requests; free((char *) devices); return TCL_OK; } } // send it sync Tcl_DStringInit(&ds); if(ndevices == 1) { if(data.hascontext) { if(cdevdebug == 1) { cout << "Context data: " << endl; data.context.asciiDump(stdout); } if(request->setContext(data.context) != CDEV_SUCCESS) { interp->result = "setContext error"; return TCL_ERROR; } } if(cdevdebug == 1) { cout << argv[1] << " " << argv[2] << " OutBound data: " << endl; data.out.asciiDump(stdout); } if(request->send(data.out, result) != CDEV_SUCCESS) { sprintf(interp->result, "error in send: %s %s", argv[1], argv[2]); return TCL_ERROR; } if(cdevdebug == 1) { cout << "Result data: " << endl; result.asciiDump(stdout); } if(STATUS_TAG !=- 1) { if(result.get(STATUS_TAG, &i) == CDEV_SUCCESS) { if(i != CDEV_SUCCESS) { if(SEVERITY_TAG !=- 1) { Tcl_DStringFree(&ds); result.get(SEVERITY_TAG, cs, 255); Tcl_DStringAppend(&ds, cs, -1); Tcl_DStringResult(interp, &ds); } else { interp->result = "cdev status != 0"; } return TCL_ERROR; } } } if(Cdev_Handleresult(data.taglist, data.tagn, result, ds, 1) != TCL_OK) { interp->result = "cannot handle cdev result"; Tcl_DStringFree(&ds); return TCL_ERROR; } Tcl_DStringResult(interp, &ds); interp->freeProc = TCL_DYNAMIC; } else { // ndevices > 1 size_t valuedim; int addbraces = 0; for(n = 0; n < ndevices; n++) { if(data.hascontext) { if(requests[n]->setContext(data.context) != CDEV_SUCCESS) { interp->result = "setContext error"; delete requests; free((char *) devices); return TCL_ERROR; } } if(requests[n]->send(data.out, result) != CDEV_SUCCESS) { sprintf(interp->result, "error in send: %s %s", devices[n], argv[2]); delete requests; free((char *) devices); return TCL_ERROR; } if(STATUS_TAG !=- 1) { if(result.get(STATUS_TAG, &i) == CDEV_SUCCESS) { if(i != CDEV_SUCCESS) { if(SEVERITY_TAG !=- 1) { Tcl_DStringFree(&ds); result.get(SEVERITY_TAG, cs, 255); Tcl_DStringAppend(&ds, cs, -1); Tcl_DStringResult(interp, &ds); } else { interp->result = "cdev status != 0"; } delete requests; free((char *) devices); return TCL_ERROR; } } } if(Cdev_Handleresult(data.taglist, data.tagn, result, ds, ndevices) != TCL_OK) { interp->result = "cannot handle cdev result"; Tcl_DStringFree(&ds); return TCL_ERROR; } } Tcl_DStringResult(interp, &ds); interp->freeProc = TCL_DYNAMIC; delete requests; free((char *) devices); } return TCL_OK; } int CdevDirectoryCmd(ClientData, Tcl_Interp *interp, int argc, char **argv) { int ndevices = 0; cdevData result; Tcl_DString ds; cdevCallback *cb = NULL; callbackInfo *info; int i; tclCdevData data; int delinfo = 0; if(argc < 3) { Tcl_AppendResult(interp, "cdevDirectory: wrong # args", (char *) NULL); return TCL_ERROR; } if(argc < 3) { Tcl_AppendResult(interp, "cdevDirectory: wrong # args", (char *) NULL); return TCL_ERROR; } cdevDevice &device = cdevDevice::attachRef("cdevDirectory"); data.parse(interp, argv-1, argc+1); if(data.state != TCL_OK) { return TCL_ERROR; } if(data.async) { // we want to sent it async info = new callbackInfo(interp, data.callbacktag); if(data.tagn != 0) { info->addtags(data.tagn, data.taglist); } if(strncmp(argv[2], "monitorOn", 9) == 0) { cb = new cdevCallback(Cdev_CallbackFunction, (void *) info); } else if(strncmp(argv[2], "monitorOff", 10) == 0) { delinfo = 1; cb = new cdevCallback(NULL, NULL); } else { cb = new cdevCallback(Cdev_CallbackFunctionOnce, (void *) info); } info->callback = cb; if(cdevdebug == 1) { cout << argv[1] << " " << argv[2] << " OutBound data: " << endl; data.out.asciiDump(stdout); } if(device.sendCallback(argv[1], data.out, *cb) != 0) { delete info; return TCL_ERROR; } else { if(delinfo) delete info; return TCL_OK; } } // send it sync Tcl_DStringInit(&ds); if(cdevdebug == 1) { cout << argv[1] << " " << argv[2] << " OutBound data: " << endl; data.out.asciiDump(stdout); } if(device.send(argv[1], data.out, result) != CDEV_SUCCESS) { sprintf(interp->result, "error in send: %s", argv[1]); return TCL_ERROR; } if(cdevdebug == 1) { cout << "Result data: " << endl; result.asciiDump(stdout); } if(STATUS_TAG !=- 1) { if(result.get(STATUS_TAG, &i) == CDEV_SUCCESS) { if(i != CDEV_SUCCESS) { if(SEVERITY_TAG !=- 1) { Tcl_DStringFree(&ds); result.get(SEVERITY_TAG, cs, 255); Tcl_DStringAppend(&ds, cs, -1); Tcl_DStringResult(interp, &ds); } else { interp->result = "cdev status != 0"; } return TCL_ERROR; } } } if(Cdev_Handleresult(data.taglist, data.tagn, result, ds, 1) != TCL_OK) { interp->result = "cannot handle cdev result"; Tcl_DStringFree(&ds); return TCL_ERROR; } Tcl_DStringResult(interp, &ds); interp->freeProc = TCL_DYNAMIC; return TCL_OK; } extern"C"int Cdev_Init(Tcl_Interp *interp) { // int i,fd[20],numFD = 20; // System.getFd(fd, numFD); // for (i=0;i