Files
cdev-1.7.2n/extensions/tclnew/tcl_cdev80.cc
2022-12-13 12:44:04 +01:00

931 lines
30 KiB
C++

// 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 <tcl.h>
#include <math.h>
#include <cdevData.h>
#include <cdevSystem.h>
#include <cdevRequestObject.h>
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<len; ++i, ++count) list[i] = Tcl_NewDoubleObj(res[count]);
} else {
for (int i=0; i<len; ++i) list[i] = HandleLevel(res, count, level+1, bounds, dim);
}
return Tcl_NewListObj(len, list);
}
/// Handle float *data, recursively if dimension > 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<len; ++i, ++count) list[i] = Tcl_NewDoubleObj((double)res[count]);
} else {
for (int i=0; i<len; ++i) list[i] = HandleLevel(res, count, level+1, bounds, dim);
}
return Tcl_NewListObj(len, list);
}
/// Handle short *data, recursively if dimension > 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<len; ++i, ++count) list[i] = Tcl_NewIntObj((int)res[count]);
} else {
for (int i=0; i<len; ++i) list[i] = HandleLevel(res, count, level+1, bounds, dim);
}
return Tcl_NewListObj(len, list);
}
/// Handle uint *data, recursively if dimension > 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<len; ++i, ++count) list[i] = Tcl_NewIntObj((int)res[count]);
} else {
for (int i=0; i<len; ++i) list[i] = HandleLevel(res, count, level+1, bounds, dim);
}
return Tcl_NewListObj(len, list);
}
/// Handle long *data, recursively if dimension > 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<len; ++i, ++count) list[i] = Tcl_NewLongObj(res[count]);
} else {
for (int i=0; i<len; ++i) list[i] = HandleLevel(res, count, level+1, bounds, dim);
}
return Tcl_NewListObj(len, list);
}
/// Handle ulong *data, recursively if dimension > 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<len; ++i, ++count) list[i] = Tcl_NewLongObj((long)res[count]);
} else {
for (int i=0; i<len; ++i) list[i] = HandleLevel(res, count, level+1, bounds, dim);
}
return Tcl_NewListObj(len, list);
}
/// Handle uchar (byte) *data, recursively if dimension > 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<len; ++i, ++count) list[i] = Tcl_NewIntObj((int)res[count]);
} else {
for (int i=0; i<len; ++i) list[i] = HandleLevel(res, count, level+1, bounds, dim);
}
return Tcl_NewListObj(len, list);
}
/** Handle Timestamp *data, recursively if dimension > 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<len; ++i, ++count) {
ts[0] = Tcl_NewLongObj(res[count].secPastEpoch);
ts[1] = Tcl_NewLongObj(res[count].nsec);
list[i] = Tcl_NewListObj(2,ts);
}
} else {
for (int i=0; i<len; ++i) list[i] = HandleLevel(res, count, level+1, bounds, dim);
}
return Tcl_NewListObj(len, list);
}
/// Handle char* *data, recursively if dimension > 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; i<len; ++i, ++count) list[i] = Tcl_NewStringObj(res[count],-1);
} else {
for (int i=0; i<len; ++i) list[i] = HandleLevel(res, count, level+1, bounds, dim);
}
return Tcl_NewListObj(len, list);
}
/// Convert a tagged entry inside a cdevData to a Tcl_Obj
Tcl_Obj* Tcl_ObjFromcdevData(int tag, cdevData* data) {
int type = data->getType(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; i<argc; ++i) {
Tcl_SetStringObj(obj, argv[i], -1);
Tcl_ListObjGetElements(interp, obj, &objc, &objv);
if (objc == 2) { // most often the case
tag = cdevData::addTag(Tcl_GetStringFromObj(objv[0], &len));
Tcl_ListObjGetElements(interp, objv[1], &objc, &objv);
if (objc == 1) { // scalar
long l; double d;
if (Tcl_GetLongFromObj(NULL, objv[0], &l) == TCL_OK) {
ptr->insert(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; ic<objc ; ++ic) {
if (Tcl_GetLongFromObj(interp, objv[ic], &lv[ic]) != TCL_OK) {
status = TCL_ERROR;
break;
}
}
ptr->insert(tag, lv, objc);
} else if (Tcl_GetDoubleFromObj(NULL, objv[0], &d) == TCL_OK) {
double dv[objc];
dv[0] = d;
for (int ic=1; ic<objc ; ++ic) {
if (Tcl_GetDoubleFromObj(interp, objv[ic], &dv[ic]) != TCL_OK) {
status = TCL_ERROR;
break;
}
}
ptr->insert(tag, dv, objc);
} else {
char *s[objc];
for(j=0; j<objc; ++j) s[j] = Tcl_GetStringFromObj(objv[j], &len);
ptr->insert(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; j<objc; ++j) {
taglist[j] = cdevData::addTag(Tcl_GetStringFromObj(objv[j], &len));
}
} else if (strcmp(s, "-unroll") == 0) {
if (++i == argc) {
Tcl_AppendResult(interp, " expecting number", NULL);
status = TCL_ERROR;
break;
}
Tcl_SetStringObj(obj, argv[i], -1);
if (Tcl_GetIntFromObj(interp, obj, &unroll) != TCL_OK) {
status = TCL_ERROR;
break;
}
} else {
if (i != argc-1) {
Tcl_AppendResult(interp, " expecting callbacktag at end", NULL);
status = TCL_ERROR;
break;
}
cbtag = Tcl_NewStringObj(argv[i],-1);
}
} else { // This is a string with spaces
tag = cdevData::addTag(Tcl_GetStringFromObj(objv[0], &len));
Tcl_Obj *concat = Tcl_ConcatObj(objc-1, objv+1);
ptr->insert(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; i<data->taglen; ++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; i<data->taglen; ++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; i<data->taglen; ++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; j<len; ++j) {
int slen;
Tcl_Obj *tagobj = Tcl_DuplicateObj(data->cbtag);
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; i<number; ++i) {
statuss[i] = CDEV_SUCCESS;
requests[i] = cdevRequestObject::attachPtr(Tcl_GetStringFromObj(objv[i], &len), Tcl_GetStringFromObj(message, &len));
if (requests[i] == NULL) {
statuss[i] = CDEV_INVALIDOBJ;
if (status == CDEV_SUCCESS) {
status = CDEV_INVALIDOBJ;
obj = Tcl_NewObj();
}
Tcl_ListObjAppendElement(interp, obj, objv[i]);
}
}
if (status != CDEV_SUCCESS) {
Tcl_Obj *listv[3];
listv[0] = Tcl_NewStringObj("Cannot find service for: ",-1);
listv[1] = obj;
listv[2] = message;
Tcl_IncrRefCount(message);
Tcl_SetObjResult(interp, Tcl_NewListObj(3, listv));
}
}
TclMultiDeviceRequest::~TclMultiDeviceRequest() {
delete requests;
delete statuss;
device = NULL;
message = NULL;
}
/// synchronous send
int TclSingleDeviceRequest::send(Tcl_cdevData *data, Tcl_Obj *&obj) {
if (status == CDEV_INVALIDOBJ) return status;
if (data->hasContext()) 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; i<number; ++i) {
if (statuss[i] == CDEV_SUCCESS) {
if (data->hasContext()) 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; j<number; ++j) {
if (statuss[j] == CDEV_SUCCESS) {
if (data->hasContext()) 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 <cdevCns/cdevCns.hxx>
#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<numFD;i++) {
Cdev_RegisterFD(fds[i],1,NULL);
}
cdevSystem::defaultSystem().addFdChangedCallback(Cdev_RegisterFD, NULL);
if(Tcl_PkgProvide(interp, "Cdev", "8.0") != TCL_OK) {
return TCL_ERROR;
}
// Alternatively call poll periodically
// Cdev_UpdateProc(ClientData dummy);
Tcl_CreateCommand(interp, "cdev", CdevCmd, (ClientData *) NULL, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp, "cdeverrorcode", CdevErrorCodeCmd, (ClientData *) NULL, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(interp, "cdevdebug", CdevDebugCmd, (ClientData *) NULL, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateExitHandler(Cdev_ExitProc, NULL);
return TCL_OK;
}