/*--------------------------------------------------------------------------- pardef.c my way to define and handle object parameters What is a bit unusual, is the long list of static variables in this module. This design allows short argument lists. Markus Zolliker, March 2005 ---------------------------------------------------------------------------- */ #include #include #include #include #include #include #include "logger.h" #include "pardef.h" #include "sugar.h" #include "commandlog.h" #define ILLNUM -2 #define ILLARGC -3 #define AMBIGUOS -4 #define ILLPRIV -5 #define BADLOG -6 #define UNKPAR -7 typedef enum { NO_OP, FMT_OP, SET_OP, GET_OP, INIT_OP } ParOp; static ParData *obj = NULL; static ParAct act; static ParAct action; static char *parName; static int cmdArgc; static char **cmdArgs; static char *thisPar; static int returnValue; static SConnection *sicsConnection; static char *outputFormat; static int access; static FILE *saveFile; static int doit; static char *listTail; static char **enumList; static char *logName; static time_t now; static ParInfo *par; static float floatValue; static char combiName[80]; static char *grpFmt; static char *loggerDir = NULL; static int showTime = 0; static int exact; void ParKill(void *object); static ParClass parClass={"Par", sizeof(ParData), 0, {&parClass}}; /*----------------------------------------------------------------------------*/ void *ParCast(ParClass *class, void *object) { ParClass *t; if (! object) return NULL; t = ((ParData *)object)->class; assert(t->base[0] == &parClass); if (t && t->base[class->level] == class) { return object; } else { return NULL; } } /*----------------------------------------------------------------------------*/ void *ParCheck(ParClass *class, void *object) { ParClass *t; assert(object); t = ((ParData *)object)->class; assert(t->base[0] == &parClass); assert(t && t->base[class->level] == class); return object; } /*----------------------------------------------------------------------------*/ void *ParMakeClass(ParClass *class, ParClass *base) { int i; if (class->base[0] == NULL) { /* initialize */ if (base == NULL) { base = &parClass; } assert(class->size >= base->size); for (i=0; i<=base->level; i++) { class->base[i] = base->base[i]; } assert(ibase[i] = class; class->level = i; } else if (base) { /* check */ for (i=0; i<=base->level; i++) { assert(class->base[i] == base->base[i]); } assert(class->base[i] == class); assert(class->level == i); } return class; } /*--------------------------------------------------------------------------*/ int ParPrintf(void *object, int iOut, const char *fmt, ...) { va_list ap; char buf0[128]; int l; char *buf, *dyn = NULL; SConnection *con; ParData *pobj = object; con = NULL; if (pobj == NULL) { if (obj) { if (sicsConnection) { con = sicsConnection; } else { pobj = obj; } } } if (!con && pobj) { con = SCLoad(&pobj->conn); if (!con && iOut < 0) return 0; } if (iOut < 0 && (!pobj || -iOut > pobj->verbose)) return 0; if (iOut<0) iOut=eStatus; va_start(ap, fmt); l = vsnprintf(buf0, sizeof(buf0), fmt, ap); va_end(ap); if (l >= sizeof buf0) { /* we have probably a C99 conforming snprintf and need a larger buffer */ dyn = malloc(l+1); if (dyn != NULL) { va_start(ap, fmt); vsnprintf(dyn, l+1, fmt, ap); va_end(ap); } buf = dyn; } else { buf = buf0; } if (con) { SCWrite(con, buf, iOut); /* writes to command log when user or manager */ } else if (iOut >= 0) { WriteToCommandLog(">", buf); /* no connection, write to commandlog only */ } if (dyn) free(dyn); return 1; } /*-------------------------------------------------------------------------*/ static void ParDo(SConnection *con, ParData *o, ParAct a, char *parName) { act = a; action = a; sicsConnection = con; if (parName && strcmp(parName, "*") == 0) { thisPar = ""; } else { thisPar = parName; } returnValue = 0; par = NULL; grpFmt = NULL; assert(obj==NULL); obj = o; obj->pardef(obj); obj = NULL; } /*--------------------------------------------------------------------------*/ char *ParArg2Text(int argc, char *argv[], char *res, int maxsize) { int i, l; char *p; if (res == NULL) { maxsize = 0; for (i=0; iname, name)); if (o->creationCmd) { fprintf(fil, "if {[catch { %s }] == 0} {\n", o->creationCmd); } ParDo(0, o, PAR_SAVE, NULL); if (o->creationCmd) { fprintf(fil, "}\n"); } return returnValue; } /*--------------------------------------------------------------------------*/ int ParLog(void *object) { ParData *o = ParCheck(&parClass, object); int next; now = time(NULL); next = now - (o->logTime / o->period + 1) * o->period; if (next >= 0) { showTime = 1; ParDo(0, o, PAR_LOG, NULL); o->logTime = now; o->logPending = 0; } return next; } /*-------------------------------------------------------------------------*/ static int ParCallBack(int event, void *eventData, void *userData) { char *pBuf = (char *)eventData; SConnection *con = (SConnection *)userData; if (event == VALUECHANGE) { SCWrite(con,pBuf,eValue); return 1; } return 1; } /*----------------------------------------------------------------------------*/ static int ParOutError(SConnection *con, ParData *o) { switch (returnValue) { case AMBIGUOS: SCPrintf(con, eError, "ERROR: doubly defined parameter %s.%s", o->name, thisPar); break; case ILLPRIV: SCPrintf(con, eError, "ERROR: insufficient privilege to change %s.%s", o->name, thisPar); break; case ILLNUM: SCPrintf(con, eError, "ERROR: illegal value", o->name, cmdArgs[0]); break; case ILLARGC: SCPrintf(con, eError, "ERROR: illegal number of arguments for %s %s", o->name, thisPar); break; case BADLOG: SCPrintf(con, eError, "ERROR: can not create log directory for %s %s", o->name, thisPar); break; case UNKPAR: SCPrintf(con, eError, "ERROR: %s %s is unknown", o->name, thisPar); break; /* case BUSY: SCPrintf(con, eError, "ERROR: %s busy", o->name); break; */ default: if (returnValue < 0) { return -1; } else { return 1; } } return -1; } /*----------------------------------------------------------------------------*/ static void ParListSugar(SConnection *con, ParData *o) { ParInfo *p; char buf[512]; int l, i; char *sugar; p = o->infoList; l = snprintf(buf, sizeof buf, "%s sugar =", o->name); while (p != NULL) { if (p->log) { sugar = LoggerName(p->log); if (strchr(sugar, '.') == NULL && strcmp(sugar, o->name) != 0) { i = snprintf(buf + l, sizeof buf - l, " %s", sugar); if ( i<=0 ) break; l += i; if (l >= sizeof buf - 1) break; } } p = p->next; } SCWrite(con, buf, eStatus); } /*----------------------------------------------------------------------------*/ static int ParExecute(SConnection *con, SicsInterp *sics, void *object, int argc, char *argv[]) { static char *empty[1]={""}; long id; int iret, logIt = 0; ParData *o = ParCheck(&parClass, object); char *setArgv[2]; if (SCGetRights(con) <= usUser) { SCSave(&o->conn, con); } if (argc>1) thisPar = argv[1]; if (argc >= 2 && 0==strcasecmp(argv[1], "list")) { cmdArgc = argc - 2; cmdArgs = argv + 2; ParDo(con, o, PAR_LIST, NULL); return 1; } if (argc == 1) { /* no args */ ParDo(con, o, PAR_SHOW, ""); if (returnValue == 0) { SCSendOK(con); returnValue = 1; } } else if ((0 == strcasecmp(argv[1], "log") || 0 == strcasecmp(argv[1], "unlog"))) { if (argc < 3) { returnValue = ILLARGC; thisPar = argv[1]; } else { cmdArgc = argc - 3; cmdArgs = argv + 3; doit = toupper(argv[1][0]) != 'U'; exact = 1; ParDo(con, o, PAR_LOGSWITCH, argv[2]); } } else if ((0 == strcasecmp(argv[1], "save") || 0 == strcasecmp(argv[1], "unsave"))) { if (argc != 3) { returnValue = ILLARGC; } else { doit = toupper(argv[1][0]) != 'U'; ParDo(con, o, PAR_SAVESWITCH, argv[2]); } SCparChange(con); } else if (strcmp(argv[1],"interest") == 0) { if (!o->pCall) { o->pCall = CreateCallBackInterface(); } assert(o->pCall); id = RegisterCallback(o->pCall, VALUECHANGE, ParCallBack, con, NULL); SCRegister(con, pServ->pSics, o->pCall, id); SCSendOK(con); return 1; } else if (strcmp(argv[1],"uninterest") == 0) { if (o->pCall) { RemoveCallback2(o->pCall, con); } SCSendOK(con); return 1; } else if (strcmp(argv[1],"sugar") == 0) { ParListSugar(con, o); return 1; } else { if (strcmp(argv[1], "=") == 0) { cmdArgc = argc - 2; cmdArgs = argv + 2; ParDo(con, o, PAR_SET, ""); logIt = 1; } else { if (argc == 2) { ParDo(con, o, PAR_SHOW, argv[1]); } else { cmdArgc = argc - 2; cmdArgs = argv + 2; ParDo(con, o, PAR_SET, argv[1]); logIt = 1; } /* parameter not found, try to use args as set value for pure object not a very good idea: a typo in a parameter name will change pure value --> use always '=' if (returnValue == 0) { cmdArgc = argc - 1; cmdArgs = argv + 1; errPar = thisPar; ParDo(con, o, PAR_SET, ""); if (returnValue <= 0) { thisPar = errPar; returnValue = UNKPAR; } } */ } } if (returnValue == 0) { returnValue = UNKPAR; } iret = ParOutError(con, o); if (logIt) ParLog(o); /* log changes */ return iret; } /*----------------------------------------------------------------------------*/ static void KillLogger(ParInfo *par) { if (par->log != NULL) { LoggerKill(par->log); if (par->sugarStatus == 1) { RemoveCommand(pServ->pSics, LoggerName(par->log)); par->sugarStatus = 0; } par->log = NULL; } } /*----------------------------------------------------------------------------*/ int ParSwitchLog(int on, char *name) { char buf[80], alias[80]; KillLogger(par); if (on) { if (par->name[0] == '\0') { snprintf(buf, sizeof buf, "%s", obj->name); } else { snprintf(buf, sizeof buf, "%s.%s", obj->name, par->name); } if (name == NULL) { name = buf; } if (loggerDir == NULL) { loggerDir = IFindOption(pSICSOptions, "LoggerDir"); if (loggerDir == NULL) loggerDir="./"; LoggerSetDir(loggerDir); } par->log = LoggerMake(name, obj->period, exact); if (par->log == NULL) { return BADLOG; } if (par->sugarStatus == 0 && name != buf && strcmp(name,buf) != 0) { snprintf(alias, sizeof alias, "%s %s", obj->name, par->name); par->sugarStatus = SugarMake(name, alias); } } return 1; } /*--------------------------------------------------------------------------*/ void ParFind(void) { ParInfo *p, **last, **endList; assert(obj); if (par == NULL) { last = &obj->infoList; par = *last; } else { last = &par->next; } if (par) { /* start search after the actual parameter */ p = par->next; while (p != NULL && 0 != strcmp(p->name, parName)) { p = p->next; } } else { p = NULL; } if (p == NULL) { /* not found: search again from list head */ p = obj->infoList; while (p != NULL && 0 != strcmp(p->name, parName)) { if (p == par) { p = NULL; break; } p = p->next; } if (p == NULL) { p = calloc(1, sizeof *p); if (p == NULL) return; p->name = parName; p->log = NULL; p->saveIt = 0; p->saveLog = 0; p->state = PAR_ALWAYS_READY; p->next = *last; *last = p; } } par = p; } /*--------------------------------------------------------------------------*/ long ParText2Int(char *text) { long num = 0; if (strcasecmp(text, "undefined") == 0) { return PAR_LNAN; } if (enumList) { while (enumList[num] != NULL) { if (strcasecmp(enumList[num],text) == 0) { return num; } num++; } } return -1; } /*--------------------------------------------------------------------------*/ char *ParInt2Text(int num) { int i; static char buf[12]; if (num == PAR_LNAN) { return "undefined"; } if (! enumList) return NULL; for (i = 0; i <= num; i++) { if (enumList[i] == NULL) { return NULL; } } return enumList[num]; } /*----------------------------------------------------------------------------*/ void ParGroup(char *groupFmt) { grpFmt = groupFmt; } /*----------------------------------------------------------------------------*/ void ParName(char *name) { if (grpFmt && *grpFmt) { snprintf(combiName, sizeof combiName, grpFmt, name); parName = combiName; } else { parName = name; } ParFind(); outputFormat = NULL; action = act; assert(obj); switch (act) { case PAR_SHOW: case PAR_SET: enumList = NULL; if (0==strcasecmp(name, thisPar)) { access = -1; } else { action = PAR_NOOP; } return; case PAR_LIST: enumList = NULL; listTail = NULL; doit = -1; return; case PAR_LOG: return; case PAR_LOGSWITCH: if (0==strcasecmp(name, thisPar)) { access = -1; } else { action = PAR_NOOP; } return; case PAR_SAVESWITCH: if (0==strcasecmp(name, thisPar)) { access = -1; } else { action = PAR_NOOP; } return; case PAR_INIT: /* for save: use -1 as default value */ access = -1; doit = -1; logName = ""; /* log by default */ exact = 1; /* exact by default */ return; case PAR_GET: if (0 != strcasecmp(name, thisPar)) { action = PAR_NOOP; } return; case PAR_SAVE: case PAR_KILL: case PAR_NOOP: return; } } /*----------------------------------------------------------------------------*/ static int RestoreMode(void) { return (pServ->pReader && (SCGetRights(sicsConnection) <= usInternal)); } /*----------------------------------------------------------------------------*/ ParOp ParWhat(int numeric) { static char buf[80]; char *sp, *lname; int i; ParOp op; int on; assert(obj); switch (action) { case PAR_LIST: if (doit < 0) { if (listTail && cmdArgc == 0) { doit = 1; } else { doit = 0; } } if (doit || (cmdArgc > 0 && 0 == strcasecmp(cmdArgs[0], "all"))) return FMT_OP; break; case PAR_LOG: if (par && par->log) { return FMT_OP; } break; case PAR_LOGSWITCH: if (cmdArgc > 1) { returnValue = ILLARGC; break; } if (returnValue) { returnValue = AMBIGUOS; break; } if (par) { returnValue = 1; if (cmdArgc > 0) { lname = cmdArgs[0]; } else { lname = NULL; } par->saveLog = 1; ParSwitchLog(doit, lname); LoggerSetNumeric(par->log, numeric); SCparChange(sicsConnection); return NO_OP; } break; case PAR_SAVESWITCH: if (cmdArgc > 1) { returnValue = ILLARGC; break; } if (returnValue) { returnValue = AMBIGUOS; break; } if (par) { returnValue = 1; par->saveIt = doit; return NO_OP; } break; case PAR_INIT: if (! RestoreMode()) { if (access < 0) { access = usInternal; } if (doit < 0) { if (access == usInternal) { doit = 0; } else { doit = 1; } } if (doit) { /* set save flag */ if (par) { par->saveIt = 1; } } } if (logName) { /* set log */ if (par) { if (logName[0] != '\0') { lname = logName; } else { lname = NULL; } ParSwitchLog(1, lname); LoggerSetNumeric(par->log, numeric); } } return INIT_OP; case PAR_SHOW: if (returnValue) { returnValue = AMBIGUOS; break; } returnValue = 1; return FMT_OP; case PAR_SET: if (returnValue) { returnValue = AMBIGUOS; break; } if (access < 0) { access = usInternal; } if (access < SCGetRights(sicsConnection)) { returnValue = ILLPRIV; break; } returnValue = 1; return SET_OP; case PAR_SAVE: if (parName[0] == '\0') { parName = "*"; } if (par->saveLog) { if (par->log) { fprintf(saveFile, " %s log %s %s\n", obj->name, parName, LoggerName(par->log)); } else { fprintf(saveFile, " %s unlog %s\n", obj->name, parName); } } if (par && par->saveIt) { return FMT_OP; } break; case PAR_GET: if (returnValue) { returnValue = AMBIGUOS; break; } return GET_OP; default: return NO_OP; } action = PAR_NOOP; /* for the result of ParActionIs */ return NO_OP; } /*----------------------------------------------------------------------------*/ void ParOut(char *buf) { int l, i, j, m, ln, lp, iret; char *p; char buffer[256]; char *endp; char *pnam; char *sep; char *saved, *logged; time_t last; assert(obj); assert(outputFormat==NULL); switch (action) { case PAR_LIST: if (par->log) { snprintf(buffer, sizeof buffer, "%s", LoggerName(par->log)); } else { if (parName[0] != '\0') { snprintf(buffer, sizeof buffer, "%s.%s", obj->name, parName); } else { snprintf(buffer, sizeof buffer, "%s", obj->name); } } if (enumList) { i = strtol(buf, &endp, 0); if (endp != buf) { listTail = ParInt2Text(i); /* overwrite listTail */ } } p = buf; /* find dot or end of number */ while (*p == ' ') p++; /* skip blanks */ i = 0; if (p[i] == '-') i++; j = i; while (p[i] >= '0' && p[i] <='9') i++; l = strlen(p); ln = strlen(buffer) - strlen(obj->name); if (i != j && (buf[i] == '.' || buf[i] <= ' ')) { l += 16 - i - ln; /* decimal point or end of number at least 16 chars after object name */ lp = strlen(p); if (l < lp) l = lp; m = 23 - l - ln; /* unit/comment at least 23 chars after object name */ } else { /* non numeric value */ l = 21 - ln; /* text right justified if possible */ /* m = 1; */ m = 23 - l - ln; } if (m < 1) m = 1; if (listTail == NULL) listTail = ""; m += strlen(listTail); if (l <= 0) l = 1; saved = ""; logged = ""; if (cmdArgc > 0 && 0 == strcasecmp(cmdArgs[0], "all")) { if (!par->log) { logged = " (not logged)"; } if (par->saveIt) { saved = " (saved)"; } } ParPrintf(NULL, eStatus, "%s %*s%*s%s%s", buffer, l, p, m, listTail, logged, saved); break; case PAR_SHOW: if (parName[0]) { p=" "; } else { p=""; } ParPrintf(NULL, eValue, "%s%s%s = %s", obj->name, p, parName, buf); break; case PAR_SET: if (parName[0]) { p=" "; } else { p=""; } ParPrintf(NULL, eValue, "%s%s%s = %s", obj->name, p, parName, buf); if (!obj->logPending) { obj->logPending = 1; TaskRegister(pServ->pTasker, ParLog, NULL, NULL, obj, 0); /* schedule ParLog */ } break; case PAR_LOG: if (par->log) { if (par->state == PAR_NOW_READY) { par->state = PAR_NOT_READY; last = LoggerLastTime(par->log); if (last != 0 && now - obj->period > last) { LoggerWrite(par->log, now - obj->period, obj->period, buf); } } else if (par->state != PAR_ALWAYS_READY) { break; } if (showTime && obj->pCall) { snprintf(buffer, sizeof buffer, "