// 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 // Feb, 98 Make it work with select event handling // For generic services this works only in cdev/1.6.2 // January 1999: Complete rewrite using Tcl_Obj model #include #include #include #include #include const int VALUE = cdevData::addTag("value"); static int cdevdebug = 0; /** Handle double *data, recursively if dimension > 1. Result is a Tcl_ListObj @author Johannes van Zeijts */ Tcl_Obj* HandleLevel(double *res, int &count, int level, cdevBounds *bounds, int dim) { int len = bounds[level].length; Tcl_Obj *list[len]; if (level == dim-1) { for (int i=0; i 1. Result is a Tcl_ListObj Tcl_Obj* HandleLevel(float *res, int &count, int level, cdevBounds *bounds, int dim) { int len = bounds[level].length; Tcl_Obj *list[len]; if (level == dim-1) { for (int i=0; i 1. Result is a Tcl_ListObj Tcl_Obj* HandleLevel(short *res, int &count, int level, cdevBounds *bounds, int dim) { int len = bounds[level].length; Tcl_Obj *list[len]; if (level == dim-1) { for (int i=0; i 1. Result is a Tcl_ListObj Tcl_Obj* HandleLevel(unsigned short *res, int &count, int level, cdevBounds *bounds, int dim) { int len = bounds[level].length; Tcl_Obj *list[len]; if (level == dim-1) { for (int i=0; i 1. Result is a Tcl_ListObj Tcl_Obj* HandleLevel(long *res, int &count, int level, cdevBounds *bounds, int dim) { int len = bounds[level].length; Tcl_Obj *list[len]; if (level == dim-1) { for (int i=0; i 1. Result is a Tcl_ListObj Tcl_Obj* HandleLevel(unsigned long *res, int &count, int level, cdevBounds *bounds, int dim) { int len = bounds[level].length; Tcl_Obj *list[len]; if (level == dim-1) { for (int i=0; i 1. Result is a Tcl_ListObj Tcl_Obj* HandleLevel(unsigned char *res, int &count, int level, cdevBounds *bounds, int dim) { int len = bounds[level].length; Tcl_Obj *list[len]; if (level == dim-1) { for (int i=0; i 1. Result is a Tcl_ListObj Each entry is a list of length 2 (seconds / nseconds) */ Tcl_Obj* HandleLevel(cdev_TS_STAMP *res, int &count, int level, cdevBounds *bounds, int dim) { int len = bounds[level].length; Tcl_Obj *list[len]; Tcl_Obj *ts[2]; if (level == dim-1) { for (int i=0; i 1. Result is a Tcl_ListObj Tcl_Obj* HandleLevel(char **res, int &count, int level, cdevBounds *bounds, int dim) { int len = bounds[level].length; Tcl_Obj *list[len]; if (level == dim-1) { for (int i=0; igetType(tag); if (type == CDEV_INVALID) { Tcl_Obj* obj = Tcl_NewObj(); Tcl_AppendStringsToObj(obj,NULL); return obj; } size_t dim; data->getDim(tag, &dim); if (dim == 0) { if (type == CDEV_DOUBLE || type == CDEV_FLOAT) { double res; data->get(tag, &res); return Tcl_NewDoubleObj(res); } else if (type == CDEV_INT32) { long res; data->get(tag, &res); return Tcl_NewLongObj(res); } else if (type == CDEV_STRING) { char *s; data->find(tag, (void*&) s); return Tcl_NewStringObj(s,-1); } else if (type == CDEV_INT16) { int res; data->get(tag, &res); return Tcl_NewIntObj(res); } else if (type == CDEV_UINT16) { int res; data->get(tag, &res); return Tcl_NewIntObj(res); } else if (type == CDEV_UINT32) { unsigned long res; data->get(tag, &res); return Tcl_NewLongObj((long)res); } else if (type == CDEV_BYTE) { unsigned char res; data->get(tag, &res); return Tcl_NewIntObj((int)res); } else if (type == CDEV_TIMESTAMP) { cdev_TS_STAMP t; data->get(tag, &t); Tcl_Obj *ts[2]; ts[0] = Tcl_NewLongObj(t.secPastEpoch); ts[1] = Tcl_NewLongObj(t.nsec); return Tcl_NewListObj(2, ts); } else { Tcl_Obj *obj = Tcl_NewStringObj("unknown tag: ",-1); char *s; cdevData::tagI2C(tag, s); Tcl_AppendStringsToObj(obj,s,NULL); return obj; } } else { // dim > 0 cdevBounds Bounds[dim]; data->getBounds(tag, Bounds, dim); int count = 0; if (type == CDEV_DOUBLE) { double *res; data->find(tag, (void*&)res); return HandleLevel(res, count, 0, Bounds, dim); } else if (type == CDEV_FLOAT) { float *res; data->find(tag, (void*&)res); return HandleLevel(res, count, 0, Bounds, dim); } else if (type == CDEV_INT32) { long *res; data->find(tag, (void*&)res); return HandleLevel(res, count, 0, Bounds, dim); } else if (type == CDEV_STRING) { char **res; data->find(tag, (void*&) res); return HandleLevel(res, count, 0, Bounds, dim); } else if (type == CDEV_INT16) { short *res; data->find(tag, (void*&)res); return HandleLevel(res, count, 0, Bounds, dim); } else if (type == CDEV_UINT16) { unsigned short *res; data->find(tag, (void*&)res); return HandleLevel(res, count, 0, Bounds, dim); } else if (type == CDEV_UINT32) { unsigned long *res; data->find(tag, (void*&) res); return HandleLevel(res, count, 0, Bounds, dim); } else if (type == CDEV_BYTE) { unsigned char *res; data->find(tag, (void*&)res); return HandleLevel(res, count, 0, Bounds, dim); } else if (type == CDEV_TIMESTAMP) { cdev_TS_STAMP *res; data->find(tag, (void*&)res); return HandleLevel(res, count, 0, Bounds, dim); } else { Tcl_Obj *obj = Tcl_NewStringObj("unknown tag: ",-1); char *s; cdevData::tagI2C(tag, s); Tcl_AppendStringsToObj(obj,s,NULL); return obj; } } } /// Keeps parsed command line data, and contains callback information class Tcl_cdevData { public: Tcl_cdevData(Tcl_Interp*, int, char**); ~Tcl_cdevData(); int unroll, status, taglen, *statuss, *taglist; cdevData *out, *context; Tcl_Obj *cbtag, *tags, *devices; int hasContext(), hasData(), isAsynch(); static void ObjIniter(); static Tcl_Obj *arrayname; static Tcl_Obj *statusname; static Tcl_Obj *donename; static Tcl_Interp *interp; static Tcl_Obj* getCdevErrorCode(int); }; /// Need to incr the refcount of the static Tcl_Obj, otherwise they may be removed by the tcl memory manager void Tcl_cdevData::ObjIniter() { Tcl_IncrRefCount(arrayname); Tcl_IncrRefCount(statusname); Tcl_IncrRefCount(donename); } Tcl_Obj *Tcl_cdevData::arrayname = Tcl_NewStringObj("Control",-1); Tcl_Obj *Tcl_cdevData::statusname = Tcl_NewStringObj("CdevStatus",-1); Tcl_Obj *Tcl_cdevData::donename = Tcl_NewStringObj("CdevDone",-1); Tcl_Interp *Tcl_cdevData::interp; Tcl_cdevData::~Tcl_cdevData() { delete out; delete context; delete taglist; delete statuss; if (tags != NULL) Tcl_DecrRefCount(tags); if (cbtag != NULL) Tcl_DecrRefCount(cbtag); if (devices != NULL) Tcl_DecrRefCount(devices); } /// Convert int into a CDEV error string Tcl_Obj *Tcl_cdevData::getCdevErrorCode(int code) { static Tcl_Obj *objm2= Tcl_NewStringObj("CDEV_WARNING",-1); static Tcl_Obj *objm1= Tcl_NewStringObj("CDEV_ERROR",-1); static Tcl_Obj *obj0 = Tcl_NewStringObj("CDEV_SUCCESS",-1); static Tcl_Obj *obj1 = Tcl_NewStringObj("CDEV_INVALIDOBJ",-1); static Tcl_Obj *obj2 = Tcl_NewStringObj("CDEV_INVALIDARG",-1); static Tcl_Obj *obj3 = Tcl_NewStringObj("CDEV_INVALIDSVC",-1); static Tcl_Obj *obj4 = Tcl_NewStringObj("CDEV_INVALIDOP",-1); static Tcl_Obj *obj5 = Tcl_NewStringObj("CDEV_NOTCONNECTED",-1); static Tcl_Obj *obj6 = Tcl_NewStringObj("CDEV_IOFAILED",-1); static Tcl_Obj *obj7 = Tcl_NewStringObj("CDEV_CONFLICT",-1); static Tcl_Obj *obj8 = Tcl_NewStringObj("CDEV_NOTFOUND",-1); static Tcl_Obj *obj9 = Tcl_NewStringObj("CDEV_TIMEOUT",-1); static Tcl_Obj *obj10= Tcl_NewStringObj("CDEV_CONVERT",-1); static Tcl_Obj *obj11= Tcl_NewStringObj("CDEV_OUTOFRANGE",-1); static Tcl_Obj *obj12= Tcl_NewStringObj("CDEV_NOACCESS",-1); static Tcl_Obj *obj13= Tcl_NewStringObj("CDEV_ACCESSCHANGED",-1); static Tcl_Obj *obj60= Tcl_NewStringObj("CDEV_DISCONNECTED",-1); static Tcl_Obj *obj61= Tcl_NewStringObj("CDEV_RECONNECTED",-1); static Tcl_Obj *obj70= Tcl_NewStringObj("CDEV_DELETE_CALLBACK",-1); static Tcl_Obj *obj80= Tcl_NewStringObj("CDEV_NOTCONSERVER",-1); static Tcl_Obj *obj81= Tcl_NewStringObj("CDEV_NOTFOUNDSERVER",-1); static Tcl_Obj *obj82= Tcl_NewStringObj("CDEV_CONN_TIMEOUT",-1); static Tcl_Obj *obj86= Tcl_NewStringObj("CDEV_BADIO",-1); static Tcl_Obj *obj87= Tcl_NewStringObj("CDEV_OVERFLOW",-1); static Tcl_Obj *obj88= Tcl_NewStringObj("CDEV_INCOMPLETE",-1); static Tcl_Obj *obj89= Tcl_NewStringObj("CDEV_CBK_FINISHED",-1); static Tcl_Obj *obj90= Tcl_NewStringObj("CDEV_PAUSED",-1); static Tcl_Obj *obj91= Tcl_NewStringObj("CDEV_MSG_ERR",-1); static Tcl_Obj *objdefault= Tcl_NewStringObj("UNKNOWN",-1); Tcl_Obj *obj; switch (code) { case -2: obj = objm2; break; case -1: obj = objm1; break; case 0: obj = obj0; break; case 1: obj = obj1; break; case 2: obj = obj2; break; case 3: obj = obj3; break; case 4: obj = obj4; break; case 5: obj = obj5; break; case 6: obj = obj6; break; case 7: obj = obj7; break; case 8: obj = obj8; break; case 9: obj = obj9; break; case 10: obj = obj10; break; case 11: obj = obj11; break; case 12: obj = obj12; break; case 13: obj = obj13; break; case 60: obj = obj60; break; case 61: obj = obj61; break; case 70: obj = obj70; break; case 80: obj = obj80; break; case 81: obj = obj81; break; case 82: obj = obj82; break; case 86: obj = obj86; break; case 87: obj = obj87; break; case 88: obj = obj88; break; case 89: obj = obj89; break; case 90: obj = obj90; break; case 91: obj = obj91; break; default: obj = objdefault; } Tcl_IncrRefCount(obj); return obj; } int Tcl_cdevData::hasContext() { if (context == NULL) return 0; cdevDataIterator iterator(context); return iterator.init(); } int Tcl_cdevData::hasData() { cdevDataIterator iterator(out); return iterator.init(); } int Tcl_cdevData::isAsynch() { return (cbtag != NULL); } /// Parses its arguments into a cdevData result, context, and switches Tcl_cdevData::Tcl_cdevData(Tcl_Interp*, int argc, char** argv) { int tag, i, j, len, objc; Tcl_Obj *obj = Tcl_NewObj(); Tcl_Obj **objv; out = new cdevData(); context = NULL; cdevData *ptr = out; status = TCL_OK; statuss = NULL; taglen = 0; unroll = 0; taglist = NULL; cbtag = NULL; tags = NULL; devices = NULL; for(i=0; iinsert(tag, l); } else if (Tcl_GetDoubleFromObj(NULL, objv[0], &d) == TCL_OK) { ptr->insert(tag, d); } else { ptr->insert(tag, Tcl_GetStringFromObj(objv[0], &len)); } } else if (objc > 1) { long l; double d; if (Tcl_GetLongFromObj(NULL, objv[0], &l) == TCL_OK) { long lv[objc]; lv[0] = l; for (int ic=1; icinsert(tag, lv, objc); } else if (Tcl_GetDoubleFromObj(NULL, objv[0], &d) == TCL_OK) { double dv[objc]; dv[0] = d; for (int ic=1; icinsert(tag, dv, objc); } else { char *s[objc]; for(j=0; jinsert(tag, s, objc); } } } else if (objc == 1) { char *s = Tcl_GetStringFromObj(obj, &len); if (strcmp(s, "-context") == 0) { context = new cdevData(); ptr = context; } else if (strcmp(s, "-tags") == 0) { if (++i == argc) { Tcl_AppendResult(interp, " expecting tags", NULL); status = TCL_ERROR; break; } Tcl_SetStringObj(obj, argv[i], -1); Tcl_ListObjGetElements(interp, obj, &objc, &objv); taglen = objc; taglist = new int[taglen]; tags = Tcl_DuplicateObj(obj); for (j=0; jinsert(tag, Tcl_GetStringFromObj(concat, &len)); Tcl_DecrRefCount(concat); } } Tcl_DecrRefCount(obj); } /// Based on the list of tags in the Tcl_cdevData, return a Tcl_List with the converted cdevData Tcl_Obj* Cdev_HandleResult(Tcl_cdevData *data, cdevData &result) { if (data->taglen == 0) { return Tcl_ObjFromcdevData(VALUE, &result); } else { Tcl_Obj *objv[data->taglen+1]; objv[0] = Tcl_ObjFromcdevData(VALUE, &result); for (int i=0; itaglen; ++i) { objv[i+1] = Tcl_ObjFromcdevData(data->taglist[i], &result); } return Tcl_NewListObj(data->taglen+1, objv); } } /** entry point for cdev callbacks results are put in the global array 'Control' the '-tags' and '-unroll' options determine what is put it. the VALUE tag is always returned (even if is empty/not in the result cdevData) */ void Cdev_CallbackFunction(int status, void *userarg, cdevRequestObject&, cdevData &result) { Tcl_cdevData *data = (Tcl_cdevData *)userarg; if (data == NULL) return; if (cdevdebug == 1) { int lenl; printf("callback %d %s\n", status, Tcl_GetStringFromObj(data->cbtag, &lenl)); result.asciiDump(); } Tcl_Obj *obj; int len; if (data->status != status) { data->status = status; obj = Tcl_NewObj(); Tcl_SetIntObj(obj, status); obj = Tcl_ObjSetVar2(data->interp, data->statusname, data->cbtag, obj, TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG); } cdevDataIterator iterator(&result); if (iterator.init()) { // there are entries available if (data->unroll == 0) { obj = Cdev_HandleResult( data, result); obj = Tcl_ObjSetVar2(data->interp, data->arrayname, data->cbtag, obj, TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG); } else if (data->unroll == 1) { obj = Tcl_ObjFromcdevData(VALUE, &result); obj = Tcl_ObjSetVar2(data->interp, data->arrayname, data->cbtag, obj, TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG); for (int i=0; itaglen; ++i) { obj = Tcl_ObjFromcdevData(data->taglist[i], &result); Tcl_Obj *tag; Tcl_ListObjIndex(data->interp, data->tags, i, &tag); Tcl_Obj *tagobj = Tcl_DuplicateObj(data->cbtag); Tcl_AppendStringsToObj(tagobj,".",Tcl_GetStringFromObj(tag, &len),NULL); obj = Tcl_ObjSetVar2(data->interp, data->arrayname, tagobj, obj, TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG); Tcl_DecrRefCount(tagobj); } } else if (data->unroll == 2) { Tcl_Obj **names, **values, *vtag = Tcl_ObjFromcdevData(VALUE, &result); int nameslen; Tcl_ListObjGetElements(data->interp, vtag, &nameslen, &names); Tcl_ObjSetVar2(data->interp, data->arrayname, data->cbtag, vtag, TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG); for (int i=0; itaglen; ++i) { Tcl_Obj *tag, *listobj = Tcl_ObjFromcdevData(data->taglist[i], &result); // This better be a list of length len Tcl_ListObjGetElements(data->interp, listobj, &len, &values); if (len != nameslen) { Tcl_DecrRefCount(listobj); continue;} Tcl_ListObjIndex(data->interp, data->tags, i, &tag); for (int j=0; jcbtag); Tcl_AppendStringsToObj(tagobj,".",Tcl_GetStringFromObj(names[j], &slen),".",Tcl_GetStringFromObj(tag, &slen),NULL); Tcl_ObjSetVar2(data->interp, data->arrayname, tagobj, values[j], TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG); Tcl_DecrRefCount(tagobj); } Tcl_DecrRefCount(listobj); } } } if ((cdevCallback::isTransactionDone() == 1) || (status == CDEV_CBK_FINISHED)) { Tcl_Obj *sobj = Tcl_NewObj(); Tcl_SetIntObj(sobj, status); Tcl_ObjSetVar2(data->interp, data->donename, data->cbtag, sobj, TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG); /// The multi device callback reuses the same data for each request. Do not delete it in that case if (data->statuss == NULL) delete data; } } /** Multi device callback, passes work to regular callback function The index into the device array is carried in the private data of the cdevRequestObject* */ void Cdev_MultiCallbackFunction(int status, void *userarg, cdevRequestObject& req, cdevData &result) { Tcl_cdevData *data = (Tcl_cdevData *)userarg; int index = (int) req.getPrivate(); data->status = data->statuss[index]; Tcl_Obj *obj, *savecb = data->cbtag; Tcl_ListObjIndex(data->interp, data->devices, index, &obj); data->cbtag = Tcl_DuplicateObj(obj); int len; Tcl_AppendStringsToObj(data->cbtag,".",Tcl_GetStringFromObj(savecb,&len),NULL); Cdev_CallbackFunction(status, userarg, req, result); data->statuss[index] = data->status; Tcl_DecrRefCount(data->cbtag); data->cbtag = savecb; } typedef cdevRequestObject *cdevRequestObjectPtr; /// Letter/Envelope class for (device/message) and (N devices/message) class TclRequest { public: TclRequest(Tcl_Interp *interp, int argc, char** argv); virtual ~TclRequest(); /// forwarded to the letter virtual int send(Tcl_cdevData *data, Tcl_Obj *&obj) {return Req->send(data, obj);} /// forwarded to the letter pointer virtual int sendCallback(Tcl_cdevData *data) {return Req->sendCallback(data);} /// forwarded to the letter pointer virtual int getStatus() {return Req->getStatus();} protected: TclRequest(Tcl_Obj*, Tcl_Obj*); Tcl_Obj *device, *message; /// The pointer to the letter TclRequest *Req; }; /// device/message for a single device class TclSingleDeviceRequest : public TclRequest { public: TclSingleDeviceRequest(Tcl_Interp *interp, Tcl_Obj *dev, Tcl_Obj* m); ~TclSingleDeviceRequest(); int getStatus() {return status;} int send(Tcl_cdevData *data, Tcl_Obj *&); int sendCallback(Tcl_cdevData *data); protected: int status; cdevRequestObjectPtr request; }; /** devices/message for multiple devices If the collection code ever matures, this code would try to use that. */ class TclMultiDeviceRequest : public TclRequest { public: TclMultiDeviceRequest(Tcl_Interp *interp, Tcl_Obj *dev, Tcl_Obj* m); ~TclMultiDeviceRequest(); int send(Tcl_cdevData *data, Tcl_Obj *&); int getStatus() {return status;} int sendCallback(Tcl_cdevData *data); protected: int number, status, *statuss; cdevRequestObjectPtr *requests; }; /// Constructor of the envelope class /// Create a letter class based on the length of the devices in argv[1] TclRequest::TclRequest(Tcl_Interp *interp, int argc, char** argv) { device = Tcl_NewStringObj(argv[1],-1); message = Tcl_NewStringObj(argv[2],-1); int objc; Tcl_ListObjLength(interp, device, &objc); if (objc == 1) { Req = new TclSingleDeviceRequest(interp, device, message); } else { Req = new TclMultiDeviceRequest(interp, device, message); } } /// Constructor for the letter class TclRequest::TclRequest(Tcl_Obj* dev, Tcl_Obj*m) : device(dev), message(m) { Req = NULL; } TclRequest::~TclRequest() { delete Req; if (device != NULL) Tcl_DecrRefCount(device); if (message != NULL) Tcl_DecrRefCount(message); } TclSingleDeviceRequest::TclSingleDeviceRequest(Tcl_Interp *interp, Tcl_Obj *dev, Tcl_Obj* m) : TclRequest(dev,m) { int len; status = CDEV_SUCCESS; request = cdevRequestObject::attachPtr(Tcl_GetStringFromObj(device, &len), Tcl_GetStringFromObj(message, &len)); if (request == NULL) { status = CDEV_INVALIDOBJ; Tcl_Obj *listv[3]; listv[0] = Tcl_NewStringObj("Cannot find service for: ",-1); listv[1] = device; Tcl_IncrRefCount(device); listv[2] = message; Tcl_IncrRefCount(message); Tcl_SetObjResult(interp, Tcl_NewListObj(3, listv)); } } TclSingleDeviceRequest::~TclSingleDeviceRequest() { device = NULL; message = NULL; } TclMultiDeviceRequest::TclMultiDeviceRequest(Tcl_Interp *interp, Tcl_Obj *dev, Tcl_Obj* m) : TclRequest(dev,m) { Tcl_Obj *obj, **objv; Tcl_ListObjGetElements(NULL, device, &number, &objv); int len; requests = new cdevRequestObjectPtr[number]; statuss = new int[number]; status = CDEV_SUCCESS; for (int i=0; ihasContext()) request->setContext(*(data->context)); cdevData result; status = request->send(data->out, result); obj = Cdev_HandleResult( data, result); return status; } /// asynchronous send int TclSingleDeviceRequest::sendCallback(Tcl_cdevData *data) { if (status == CDEV_INVALIDOBJ) { Tcl_Obj *sobj = Tcl_NewObj(); Tcl_SetIntObj(sobj, status); sobj = Tcl_ObjSetVar2(data->interp, data->statusname, data->cbtag, sobj, TCL_GLOBAL_ONLY); return status; } if (data->hasContext()) request->setContext(*(data->context)); int len; cdevCallback *cb; data->status = 1000; // preset it to "UNKNOWN" // initialize the status Tcl_Obj *sobj = Tcl_NewObj(); Tcl_SetIntObj(sobj, data->status); sobj = Tcl_ObjSetVar2(data->interp, data->statusname, data->cbtag, sobj, TCL_GLOBAL_ONLY); if (strncmp(Tcl_GetStringFromObj(message, &len), "monitorOff", 10) == 0) { cb = new cdevCallback(Cdev_CallbackFunction, NULL); } else { cb = new cdevCallback(Cdev_CallbackFunction, (void *) data); } if (request->sendCallback(data->out, *cb) != CDEV_SUCCESS) { return CDEV_ERROR; } return CDEV_SUCCESS; } /// synchronous send int TclMultiDeviceRequest::send(Tcl_cdevData *data, Tcl_Obj *&obj) { cdevData result; Tcl_Obj *objv[number]; int oldstatus = status; status = CDEV_SUCCESS; for (int i=0; ihasContext()) requests[i]->setContext(*data->context); statuss[i] = requests[i]->send(data->out, result); if (statuss[i] != CDEV_SUCCESS) { status = statuss[i]; } } objv[i] = Cdev_HandleResult( data, result); } obj = Tcl_NewListObj(number, objv); if (oldstatus != CDEV_SUCCESS) status = oldstatus; return status; } /// asynchronous send int TclMultiDeviceRequest::sendCallback(Tcl_cdevData *data) { Tcl_Obj **objv; Tcl_ListObjGetElements(NULL, device, &number, &objv); int len; cdevCallback *cb; data->devices = device; Tcl_IncrRefCount(device); data->statuss = new int[number]; for (int j=0; jhasContext()) requests[j]->setContext(*(data->context)); requests[j]->setPrivate((void*) j); data->statuss[j] = 1000; // preset it to "UNKNOWN" Tcl_Obj *sobj = Tcl_NewObj(); Tcl_SetIntObj(sobj, data->statuss[j]); Tcl_Obj *st = Tcl_DuplicateObj(objv[j]); Tcl_AppendStringsToObj(st, ".", Tcl_GetStringFromObj(data->cbtag,&len), NULL); Tcl_ObjSetVar2(data->interp, data->statusname, st, sobj, TCL_GLOBAL_ONLY); Tcl_DecrRefCount(st); if (strncmp(Tcl_GetStringFromObj(message, &len), "monitorOff", 10) == 0) { cb = new cdevCallback(Cdev_MultiCallbackFunction, NULL); } else { cb = new cdevCallback(Cdev_MultiCallbackFunction, (void *) data); } if (requests[j]->sendCallback(data->out, *cb) != CDEV_SUCCESS) { } } else { // RequestObj was invalid Tcl_Obj *sobj = Tcl_NewObj(); Tcl_SetIntObj(sobj, statuss[j]); Tcl_Obj *st = Tcl_DuplicateObj(objv[j]); Tcl_AppendStringsToObj(st, ".", Tcl_GetStringFromObj(data->cbtag,&len), NULL); Tcl_ObjSetVar2(data->interp, data->statusname, st, sobj, TCL_GLOBAL_ONLY); Tcl_DecrRefCount(st); } } return CDEV_SUCCESS; } static int Finished = 0; void Cdev_FileProc(ClientData clientData, int mask) { int fd = (int) clientData; if (mask == TCL_EXCEPTION) { Tcl_DeleteFileHandler(fd); return; } cdevSystem::defaultSystem().poll(); } void Cdev_RegisterFD(int fd, int condition, void *) { if (Finished == 1) { return; } if (condition == 1) { Tcl_CreateFileHandler(fd, TCL_READABLE|TCL_EXCEPTION, Cdev_FileProc, (ClientData) fd); } else { Tcl_DeleteFileHandler(fd); } } void Cdev_ExitProc(ClientData) { Finished = 1; // cdev filehandler crashes when its deleted twice (at least that what I think is the problem) } /** Entry point for tcl command Steps: Attach to a RequestObject. Return TCL_ERROR if not valid (attachPtr returns NULL). Parse the command line arguments into a cdevData and switches. send or sendCallback the request. */ int CdevCmd(ClientData, Tcl_Interp *interp, int argc, char **argv) { if (argc < 3) { Tcl_AppendResult(interp,"usage: cdev $device $message \"$tag $value\" ...", NULL); return TCL_ERROR; } TclRequest request(interp, argc, argv); if (request.getStatus() != CDEV_SUCCESS) { return TCL_ERROR; } Tcl_cdevData *data = new Tcl_cdevData(interp, argc-3, argv+3); if (data->status != TCL_OK) { delete data; return TCL_ERROR; } Tcl_Obj *obj; if (data->isAsynch()) { request.sendCallback(data); cdevSystem::defaultSystem().flush(); cdevSystem::defaultSystem().poll(); // keep data around } else { request.send(data, obj); } if (request.getStatus() != CDEV_SUCCESS) { delete data; if (!data->isAsynch()) { Tcl_SetObjResult(interp, obj); } return TCL_ERROR; } if (!data->isAsynch()) { Tcl_SetObjResult(interp, obj); delete data; } return TCL_OK; } int CdevErrorCodeCmd(ClientData, Tcl_Interp *interp, int argc, char **argv) { if (argc < 2) { Tcl_AppendResult(interp,"usage: cdeverrorcode $error", NULL); return TCL_ERROR; } Tcl_Obj *obj = Tcl_NewStringObj(argv[1], -1); int status; if (Tcl_GetIntFromObj(interp, obj, &status) != TCL_OK) { return TCL_ERROR; } Tcl_DecrRefCount(obj); Tcl_SetObjResult(interp, Tcl_cdevData::getCdevErrorCode(status)); return TCL_OK; } int CdevDebugCmd(ClientData, Tcl_Interp *, int, char **) { if (cdevdebug == 0) { cdevdebug = 1; } else { cdevdebug = 0; } return TCL_OK; } #ifdef SUN #include #endif // 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); } //extern "C" int Cdev_Init(Tcl_Interp *interp) { extern"C"int Tclcdev_Init(Tcl_Interp *interp) { int i, numFD = 20; int fds[numFD]; #ifdef SUN cdevCnsInit(); // Hook into the BNL ADO Nameserver #endif Tcl_cdevData::interp = interp; Tcl_cdevData::ObjIniter(); // This installs cdev into the tcl FD select. cdevSystem::defaultSystem().getFd(fds, numFD); for (i=0;i