From e23dfd8097d7c2547ad46ca02a5f71f9f7f3f961 Mon Sep 17 00:00:00 2001 From: zolliker Date: Mon, 5 Sep 2005 08:01:07 +0000 Subject: [PATCH] new modules --- ease.c | 819 ++++++++++++++++++++++++++++++++++++++ pardef.c | 1145 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1964 insertions(+) create mode 100644 ease.c create mode 100644 pardef.c diff --git a/ease.c b/ease.c new file mode 100644 index 0000000..0db5561 --- /dev/null +++ b/ease.c @@ -0,0 +1,819 @@ +/*--------------------------------------------------------------------------- +ease.c + +basics for (ea)sy implementable (s)ample (e)nvironment devices. +handles background activity and connections over rs232. + +Markus Zolliker, March 2005 +---------------------------------------------------------------------------- +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "sics.h" +#include "ease.h" + +#define EASE_FLAGBITS (8*sizeof(long)) + +static ParClass easeBaseClass = { "EaseBase", sizeof(EaseBase) }; +static ParClass easeDrivClass = { "EaseDriv", sizeof(EaseDriv) }; +static int parameterChange = 0; + +/*----------------------------------------------------------------------------*/ +ParClass *EaseBaseClass(void) { + return ParMakeClass(&easeBaseClass, NULL); +} +/*----------------------------------------------------------------------------*/ +ParClass *EaseDrivClass(void) { + return ParMakeClass(&easeDrivClass, EaseBaseClass()); +} +/*----------------------------------------------------------------------------*/ +EaseBase *EaseBaseCast(void *object) { + return ParCast(&easeBaseClass, object); +} +/*----------------------------------------------------------------------------*/ +EaseDriv *EaseDrivCast(void *object) { + return ParCast(&easeDrivClass, object); +} +/*----------------------------------------------------------------------------*/ +void EaseStop(EaseBase *eab) { + FsmStop(eab->task, eab->idle); + eab->state = EASE_notconnected; +} +/*----------------------------------------------------------------------------*/ +void EaseWriteError(EaseBase *eab) { + int l; + switch (eab->errCode) { + case NOTCONNECTED: + ParPrintf(eab, eError, "ERROR: unconnected socket for %s", eab->p.name); + break; + case FAILEDCONNECT: + ParPrintf(eab, eError, "ERROR: can not connect to %s:%d (terminalserver off or no ethernet connection)", + eab->ser->pHost, eab->ser->iPort); + break; + case TIMEOUT: + ParPrintf(eab, eError, "ERROR: timeout on %s\ncmd: %s\nans: %s", + eab->p.name, eab->cmd, eab->ans); + break; + case INCOMPLETE: + ParPrintf(eab, eError, "ERROR: incomplete answer %s from %s", eab->ans, + eab->p.name); + break; + case EASE_ILL_ANS: + l = strlen(eab->cmd); + if (l > 0 && eab->cmd[l-1] < ' ') { + l--; + } + ParPrintf(eab, eError, "ERROR: illegal answer %s from %s (cmd: %*s)", + eab->ans, eab->p.name, l, eab->cmd); + break; + case EASE_DEV_CHANGED: + ParPrintf(eab, eError, "ERROR: controller was exchanged on %s", eab->p.name); + EaseStop(eab); + break; + case EASE_FAULT: + ParPrintf(eab, eError, "ERROR: error on %s", eab->p.name); + break; + default: + ParPrintf(eab, eError, "ERROR: error code %d on %s", eab->errCode, eab->p.name); + break; + } +} +/*----------------------------------------------------------------------------*/ +void EaseWrite(EaseBase *eab, char *cmd) { + int iRet; + char trash[64]; + int l; + + if (eab->errCode || eab->state == EASE_expect) return; + while (availableRS232(eab->ser) == 1) { + l=sizeof(trash); + iRet = readRS232TillTerm(eab->ser, trash, &l); + if (iRet < 0) break; + ParPrintf(eab, -2, "trash: %s\n", trash); + } + snprintf(eab->cmd, sizeof(eab->cmd), "%s%s", cmd, eab->ser->sendTerminator); + iRet = writeRS232(eab->ser, eab->cmd, strlen(eab->cmd)); + if (iRet < 0) { + FsmStop(eab->task, eab->idle); + snprintf(eab->msg, sizeof eab->msg, + "connection to %s:%d lost", eab->ser->pHost, eab->ser->iPort); + ParPrintf(eab, eError, "ERROR: %s", eab->msg); + eab->state = EASE_notconnected; + eab->errCode = iRet; + return; + } + ParPrintf(eab, -2, "cmd: %s", cmd); + eab->state = EASE_expect; + eab->cmdtime = time(NULL); +} +/*----------------------------------------------------------------------------*/ +int EaseWaitRead(EaseBase *eab) { + int cnt=0; + + if (eab->state < EASE_idle) { + return NOTCONNECTED; + } + FsmPause(eab->task, 1); + while (eab->state == EASE_expect) { + /* TaskYield(pServ->pTasker); does not work (pardef is not recursive) */ + FsmTaskHandler(eab->task); + cnt++; + } + FsmPause(eab->task, 0); + /* if (cnt>1) printf("TaskYield %d\n", cnt); */ + return 1; +} +/*----------------------------------------------------------------------------*/ +void EaseParHasChanged(void) { + parameterChange = 1; +} +/*----------------------------------------------------------------------------*/ +void EaseSavePars(void) { + char *pFile = NULL; + + if (parameterChange) { + parameterChange = 0; + + assert(pServ->pSics); + + pFile = IFindOption(pSICSOptions,"statusfile"); + if (pFile) { + WriteSicsStatus(pServ->pSics,pFile,0); + } + } +} +/*----------------------------------------------------------------------------*/ +int EaseHandler(EaseBase *eab) { + EaseDriv *ead; + int iret; + + ead = EaseDrivCast(eab); + + EaseSavePars(); + if (eab->state == EASE_expect) { + if (eab->cmdtime !=0 && time(NULL) > eab->cmdtime + eab->p.period*2) { + snprintf(eab->msg, sizeof eab->msg, "no response since %d sec", + (int)(time(NULL) - eab->cmdtime)); + } + return 0; + } + if (eab->state == EASE_lost) { + snprintf(eab->msg, sizeof eab->msg, "no response"); + } + if (eab->state == EASE_notconnected) { + return 0; + } + if (eab->state == EASE_connecting) { + iret = initRS232Finished(eab->ser); + if (iret == 0) return 0; /* waiting for connection */ + if (iret < 0) { + snprintf(eab->msg, sizeof eab->msg, + "connection for %s failed", eab->p.name); + ParPrintf(eab, eError, "%s", eab->msg); + eab->state = EASE_notconnected; + return 0; + } else { + snprintf(eab->msg, sizeof eab->msg, + "get a first answer from %s", eab->p.name); + eab->state = EASE_idle; + } + } + if (eab->errCode) { + EaseWriteError(eab); + eab->errCode = 0; + if (ead) { + ead->hwstate = HWFault; + FsmStop(eab->task, eab->doit); + } + return 0; + } + if (ead) { + if (eab->state == EASE_lost) { + if (FsmStop(eab->task, eab->doit)) { + ParPrintf(eab, -1, "stop %s caused by timeout", eab->p.name); + } + return 0; + } + if (ead->stopped && ead->hwstate == HWBusy) { + ead->stopped = 0; + ead->hwstate = HWIdle; + if (FsmStop(eab->task, eab->doit)) { + ParPrintf(eab, -1, "%s stopped", eab->p.name); + return 0; + } + } + } else { + if (eab->state == EASE_lost) { + return 0; + } + } + return 1; +} +/*----------------------------------------------------------------------------*/ +int EaseNextFullRead(EaseBase *eab) { + time_t thisPeriod; + + thisPeriod = time(NULL) / eab->p.period; + if (thisPeriod != eab->readPeriod) { + eab->readPeriod = thisPeriod; + return 1; + } + return 0; +} +/*----------------------------------------------------------------------------*/ +int EaseCheckDoit(EaseBase *eab) { + int i, n; + + if (eab->todo == NULL) { + n = eab->maxflag / EASE_FLAGBITS; + for (i=0; i<=n; i++) { + if (eab->updateFlags[i]) { + eab->todo = eab->doit; + return 1; + } + } + } + return eab->todo != NULL; +} +/*----------------------------------------------------------------------------*/ +static long EaseIdle(long pc, void *object) { + EaseBase *eab = EaseBaseCast(object); + EaseDriv *ead = EaseDrivCast(object); + struct timeval tm; + FsmFunc todo; + + switch (pc) { default: /* FSM BEGIN *******************************/ + + idle: + if (! EaseCheckDoit(eab)) goto rd; + doit: + todo = eab->todo; + eab->todo = NULL; + FsmCall(todo); + return __LINE__; case __LINE__: /**********************************/ + + rd: + /* + if (eab->state == EASE_lost) { + snprintf(eab->msg, sizeof eab->msg, "no response from %s", + eab->p.type->name); + } + */ + FsmCall(eab->read); + return __LINE__; case __LINE__: /**********************************/ + + ParLog(eab); /* just for the case ParLog was not included in the read function */ + if (EaseCheckDoit(eab)) goto doit; + /* + gettimeofday(&tm, NULL); + printf("stop %s %f\n", eab->evc->pName, tm.tv_usec / 1e6); + */ + FsmWait(1); + return __LINE__; case __LINE__: /**********************************/ + /* + gettimeofday(&tm, NULL); + printf("start %s %f\n", eab->evc->pName, tm.tv_usec / 1e6); + */ + goto idle; + + /* never return */ + return 0; } /* FSM END ********************************************/ + +} +/*----------------------------------------------------------------------------*/ +static float EaseGetValue(void *obj, SConnection *pCon) { + float val; + EaseBase *eab; + + assert(eab = EaseBaseCast(obj)); + ParGetFloat(pCon, eab, "", &val); + return val; +} +/*----------------------------------------------------------------------------*/ +int EaseUpdate(int flag) { + EaseBase *eab = EaseBaseCast(ParObject()); + + ParAccess(usUser); ParSave(0); + if (ParActionIs(PAR_SET) > 0) { + assert(flag >= 0); + assert(flag <= eab->maxflag); + eab->updateFlags[flag / EASE_FLAGBITS] |= 1 << (flag % EASE_FLAGBITS); + FsmSpeed(eab->task); + return 1; + } + return 0; +} +/*----------------------------------------------------------------------------*/ +int EaseNextUpdate(void *object) { + EaseBase *eab = EaseBaseCast(object); + unsigned long mask, p; + int i, n, flag; + + n = eab->maxflag / EASE_FLAGBITS; + for (i = 0; i <= n; i++) { + mask = eab->updateFlags[i]; + p = 1; + flag = 0; + /* find first */ + while (flag < 32) { + if (mask & p) { + eab->updateFlags[i] &= ~ p; + return flag + i * EASE_FLAGBITS; + } + p <<= 1; + flag++; + } + } + return -1; +} +/*----------------------------------------------------------------------------*/ +int EaseGetUpdate(void *object, int flag) { + EaseBase *eab = EaseBaseCast(object); + int i; + + assert(flag >= 0); + assert(flag <= eab->maxflag); + i = flag / EASE_FLAGBITS; + if ((1 << (flag % EASE_FLAGBITS)) & eab->updateFlags[i]) { + return flag; + } + return 0; +} +/*----------------------------------------------------------------------------*/ +void EaseSetUpdate(void *object, int flag, int state) { + EaseBase *eab = EaseBaseCast(object); + + assert(flag >= 0); + assert(flag <= eab->maxflag); + + if (state) { + eab->updateFlags[flag / EASE_FLAGBITS] |= 1 << (flag % EASE_FLAGBITS); + } else { + eab->updateFlags[flag / EASE_FLAGBITS] &= ~ (1 << (flag % EASE_FLAGBITS)); + } +} +/*----------------------------------------------------------------------------*/ +static long EaseRun(void *obj, SConnection *pCon, float fVal) { + EaseBase *eab = EaseBaseCast(obj); + EaseDriv *ead = EaseDrivCast(obj); + + assert(ead ); + + if (! eab->doit) { + ParPrintf(ead, -1, "missing run function %s", eab->p.name); + return 0; + } + if (FsmStop(eab->task, eab->doit)) { + ParPrintf(ead, -1, "running %s cancelled", eab->p.name); + } + if (eab->todo) { + ParPrintf(ead, -1, "%s busy", eab->p.name); + return 0; + } + ead->targetValue = fVal; + EaseParHasChanged(); /* assume that targetValue has to be saved */ + ead->stopped = 0; + /* eab->todo = eab->doit; */ + EaseSetUpdate(eab, EASE_RUN, 1); + ead->hwstate = HWBusy; + if (ead->maxwait >= 0) { + ead->timeout = time(NULL) + ead->maxwait; + } else { + ead->maxwait = -1; + ead->timeout = time(NULL) + 9999999; /* approx. infinite */ + } + ead->finish = 0; + ead->usedSettle = 0; + ead->tolState = EASE_outOfTolerance; + return 1; +} +/*----------------------------------------------------------------------------*/ +static int EaseHalt(void *obj) { + EaseDriv *ead; + + assert(ead = EaseDrivCast(obj)); + ead->stopped = 1; + return 1; +} +/*----------------------------------------------------------------------------*/ +static int EaseIsInTol(void *obj) { + EaseDriv *ead = EaseDrivCast(obj); + float f; + + f = EaseGetValue(ead, NULL) - ead->targetValue; + if (ead->tolState == EASE_outOfTolerance) { + if (fabsf(f) < ead->tolerance) { + ParPrintf(obj, eWarning, "%s is within tolerance", ead->b.p.name); + ead->tolState = EASE_inTolerance; + return 1; + } + return 0; + } else { + if (fabsf(f) > ead->tolerance * 1.11) { + if (ead->tolState == EASE_inTolerance) { + ParPrintf(obj, eWarning, "%s is out of tolerance by %f", + ead->b.p.name, f); + } + ead->tolState = EASE_outOfTolerance; + return 0; + } + return 1; + } +} +/*----------------------------------------------------------------------------*/ +static int EaseErrHandler(void *obj) { + /* to be implemented */ + return 1; +} +/*----------------------------------------------------------------------------*/ +static int EaseCheckStatus(void *obj, SConnection *pCon) { + EaseDriv *ead = EaseDrivCast(obj); + time_t now, t; + + assert(ead); + SCSave(&ead->b.p.conn, pCon); + now = time(NULL); + if (now > ead->timeout) { + ParPrintf(obj, eWarning, "maxwait expired when driving %s", ead->b.p.name); + ead->hwstate = HWIdle; + } else if (EaseIsInTol(obj)) { + if (ead->finish == 0) { + ead->finish = now - ead->usedSettle; + if (now < ead->finish + ead->settle) { + ParPrintf(obj, eWarning, "settle %s for %ld sec", + ead->b.p.name, (long)(ead->finish + ead->settle - now)); + } + } + if (now > ead->finish + ead->settle && ead->hwstate != HWIdle) { + ParPrintf(obj, eWarning, "%s has reached target", ead->b.p.name); + ead->hwstate = HWIdle; + } + } else if (ead->finish != 0) { + ead->usedSettle = now - ead->finish; + ead->finish = 0; + } + return ead->hwstate; +} +/*----------------------------------------------------------------------------*/ +static EVMode EaseGetMode(void *obj) { + EaseDriv *ead = EaseDrivCast(obj); + + assert(ead); + + if (ead->hwstate == HWBusy) { + return EVDrive; + } + if (ead->tolState == EASE_notMonitored) { + return EVIdle; + } + return EVMonitor; +} +/*----------------------------------------------------------------------------*/ +static int EaseCheckLimits(void *obj, float fVal, char *error, int errLen) { + EaseDriv *ead; + + assert(ead = EaseDrivCast(obj)); + + /* lower limit */ + if (fVal < ead->lowerLimit) { + snprintf(error, errLen, "ERROR: %g violates lower limit of device",fVal); + return 0; + } + /* upper limit */ + if (fVal > ead->upperLimit) { + snprintf(error, errLen, "ERROR: %g violates upper limit of device",fVal); + return 0; + } + return 1; +} +/*----------------------------------------------------------------------------*/ +static int EaseRestart(EaseBase *eab) { + int iRet; + + if (eab->task) { + FsmStop(eab->task, eab->idle); + FsmRestartTask(eab->task, eab->idle); + } + eab->todo = eab->start; + eab->state = EASE_connecting; + iRet = initRS232WithFlags(eab->ser, 3); + if (iRet != 1) { + eab->errCode = iRet; + EaseWriteError(eab); + return -1; + } + snprintf(eab->msg, sizeof eab->msg, + "connecting to %s:%d", eab->ser->pHost, eab->ser->iPort); + return 0; +} +/*----------------------------------------------------------------------------*/ +static int EaseInit(SConnection *pCon, EaseBase *eab, int argc, char *argv[], + int maxflag, + FsmHandler handler, + FsmFunc start, + FsmFunc idle, + FsmFunc read) { + + /* args (starting from argv[0]): + + + : + */ + int port, iRet, i; + rs232 *ser; + char *colon, *host; + char buf[64]; + + assert(eab); assert(handler); assert(start); assert(read || idle); + eab->handler = handler; + eab->start = start; + eab->read = read; + if (idle) { + eab->idle = idle; + } else { + eab->idle = EaseIdle; + } + + if (argc < 1 || argc > 2) { + SCWrite(pCon, "illegal number of arguments", eError); + return 0; + } + + colon = strchr(argv[0], ':'); + if (!colon && argc == 1) { /* use a rs232 object */ + ser = FindCommandData(pServ->pSics, argv[0], "RS232 Controller"); + if (ser == NULL) { + SCWrite(pCon, "ERROR: rs232 not found", eError); + return 0; + } + } else { + if (argc == 1) { /* host:port syntax */ + port=atoi(colon+1); + i = colon-argv[0]; + if (i >= sizeof buf) { + SCWrite(pCon, "ERROR: host name too long", eError); + return 0; + } + strncpy(buf, argv[0], i); + buf[i]='\0'; + host = buf; + } else if (argc == 2) { /* host port syntax */ + host = argv[0]; + port=atoi(argv[1]); + } + if (port==0) { + SCWrite(pCon, "ERROR: illegal port number", eError); + return 0; + } + ser = createRS232(host, port); + if (ser == NULL) { + SCWrite(pCon, "out of memory", eError); + return 0; + } + } + eab->ser = ser; + + eab->errCode = 0; + eab->cmdtime = 0; + eab->version[0] = '\0'; + eab->maxflag = maxflag; + eab->updateFlags = calloc(maxflag / EASE_FLAGBITS + 1, sizeof (*eab->updateFlags)); + if (eab->updateFlags == NULL) { + SCWrite(pCon, "out of memory", eError); + return 0; + } + + setRS232ReplyTerminator(eab->ser,"\r"); + setRS232SendTerminator(eab->ser,"\r"); + setRS232Timeout(eab->ser,10000); /* milliseconds */ + setRS232Debug(eab->ser,0); + + eab->task = NULL; + if (EaseRestart(eab) < 0) return -1; + eab->task = FsmStartTask(eab, eab->handler, eab->idle); + + TaskRegister(pServ->pTasker, (TaskFunc)FsmTaskHandler, NULL, FsmKill, + eab->task, 0); + return 1; +} +/*----------------------------------------------------------------------------*/ +static void *EaseGetInterface(void *obj, int iCode) { + EaseBase *eab = EaseBaseCast(obj); + EaseDriv *ead = EaseDrivCast(obj); + + assert(eab); + if (iCode == DRIVEID) { + if (ead) { + return ead->drivInt; + } else { + return NULL; + } + } else if (iCode == ENVIRINTERFACE) { + return ead->evInt; + } + return NULL; +} +/*----------------------------------------------------------------------------*/ +void *EaseMakeBase(SConnection *con, void *class, int argc, char *argv[], + int dynamic, int maxflag, + ParDef pardef, + FsmHandler handler, + FsmFunc start, + FsmFunc idle, + FsmFunc read) { + + /* args (starting from argv[0]): + MakeObject objectname + + */ + EaseBase *eab; + char *creationCmd = NULL; + + if (dynamic) { + creationCmd = ParArg2Text(argc, argv, NULL, 0); + } + eab = ParMake(con, argv[1], class, pardef, creationCmd); + if (!eab) return NULL; + ParCheck(&easeBaseClass, eab); + if (! EaseInit(con, eab, argc-3, argv+3, maxflag, + handler, start, idle, read)) { + RemoveCommand(pServ->pSics, argv[1]); + return NULL; + } + return eab; +} +/*----------------------------------------------------------------------------*/ +void *EaseMakeDriv(SConnection *con, void *class, int argc, char *argv[], + int dynamic, int maxflag, + ParDef pardef, + FsmHandler handler, + FsmFunc start, + FsmFunc idle, + FsmFunc read, + FsmFunc run) { + + int iret; + EaseDriv *ead; + char *creationCmd = NULL; + + assert(run); + if (dynamic) { + creationCmd = ParArg2Text(argc, argv, NULL, 0); + } + ead = ParMake(con, argv[1], class, pardef, creationCmd); + if (!ead) return NULL; + ParCheck(&easeDrivClass, ead); + ead->b.doit = run; + + ead->evInt = CreateEVInterface(); + ead->drivInt = CreateDrivableInterface(); + ead->b.p.desc->GetInterface = EaseGetInterface; + + if (! ead->evInt || ! ead->drivInt || + ! EaseInit(con, (EaseBase *)ead, argc-3, argv+3, maxflag, + handler, start, idle, read)) { + RemoveCommand(pServ->pSics, argv[1]); + return NULL; + } + + ead->evInt->GetMode = EaseGetMode; + ead->evInt->IsInTolerance = EaseIsInTol; + ead->evInt->HandleError = EaseErrHandler; + + ead->hwstate = HWIdle; + ead->drivInt->Halt = EaseHalt; + ead->drivInt->CheckLimits = EaseCheckLimits; + ead->drivInt->SetValue = EaseRun; + ead->drivInt->CheckStatus = EaseCheckStatus; + ead->drivInt->GetValue = EaseGetValue; + + /* EMon interface to be implemented */ + return ead; +} +/*----------------------------------------------------------------------------*/ +void EaseMsgPar(void *object) { + EaseBase *eab = EaseBaseCast(object); + + assert(eab); + + ParName("status"); + ParLogAs(NULL); + if (eab->msg[0] == '\0') { + ParList(""); /* do not list when empty */ + } else { + ParList(NULL); + } + ParFixedStr(eab->msg, sizeof eab->msg, NULL); + return; +} +/*----------------------------------------------------------------------------*/ +int EaseRestartWrapper(void *object, void *userarg, int argc, char *argv[]) { + EaseBase *eab = EaseBaseCast(object); + + EaseRestart(eab); + return 0; +} +/*----------------------------------------------------------------------------*/ +void EaseBasePar(void *object) { + EaseBase *eab = EaseBaseCast(object); + + assert(eab); + + ParName("restart"); ParAccess(usUser); ParCmd(EaseRestartWrapper, NULL); + + if (ParActionIs(PAR_KILL)) { + if (eab->ser) { + KillRS232(eab->ser); + free(eab->ser); + eab->ser = NULL; + } + if (pServ->pTasker && eab->task) { + FsmStopTask(eab->task); + } + if (eab->updateFlags) { + free(eab->updateFlags); + eab->updateFlags = NULL; + } + } + + return; +} +/*----------------------------------------------------------------------------*/ +int EaseSend(void *object, void *userarg, int argc, char *argv[]) { + EaseBase *eab = EaseBaseCast(object); + int iret; + char cmd[32]; + char ans[64]; + char *term; + + iret = EaseWaitRead(eab); + if (iret >= 0) { + term = eab->ser->sendTerminator; + ParArg2Text(argc, argv, cmd, sizeof(cmd) - strlen(term)); + ParPrintf(eab, -2, "cmd: %s", cmd); + strcat(cmd, term); + iret = transactRS232(eab->ser, cmd, strlen(cmd), + ans, sizeof ans); + ParPrintf(eab, -2, "ans: %s", ans); + ParPrintf(eab, eValue, "%s", ans); + } + if (iret < 0) { + eab->errCode = iret; + EaseWriteError(eab); + } + return 0; +} +/*----------------------------------------------------------------------------*/ +void EaseSendPar(void *object) { + ParName("send"); ParAccess(usUser); ParCmd(EaseSend, NULL); + return; +} +/*----------------------------------------------------------------------------*/ +void EaseDrivPar(void *object, char *fmt, char *unit) { + EaseDriv *ead; + + assert(ead = EaseDrivCast(object)); + + ParName("upperLimit"); + ParFmt(fmt); ParTail(unit); ParAccess(usUser); ParSave(1); + ParFloat(&ead->upperLimit, 310.); + + ParName("lowerLimit"); + if (ead->lowerLimit != 0) { + ParFmt(fmt); ParTail(unit); + }; + ParAccess(usUser); ParSave(1); + ParFloat(&ead->lowerLimit, 0.0); + + ParName("tolerance"); + ParFmt(fmt); ParTail(unit); ParAccess(usUser); ParSave(1); + ParFloat(&ead->tolerance, 0.0); + + ParName("maxwait"); + if (ead->maxwait < 0) { + ParTail("disabled"); + } else { + ParTail("sec"); + } + ParAccess(usUser); ParSave(1); + ParInt(&ead->maxwait, 7200); + + ParName("settle"); + ParTail("sec"); ParAccess(usUser); ParSave(1); + ParInt(&ead->settle, 0); + + ParName("targetValue"); + ParFmt(fmt); ParTail(unit); + ParFloat(&ead->targetValue, PAR_NAN); + + if (ParActionIs(PAR_KILL) && ead->drivInt) { + free(ead->drivInt); + } + return; +} diff --git a/pardef.c b/pardef.c new file mode 100644 index 0000000..033d4eb --- /dev/null +++ b/pardef.c @@ -0,0 +1,1145 @@ +/*--------------------------------------------------------------------------- +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, "