new modules

This commit is contained in:
zolliker
2005-09-05 08:01:07 +00:00
parent 2cca4ba7f0
commit e23dfd8097
2 changed files with 1964 additions and 0 deletions

819
ease.c Normal file
View File

@ -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 <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <fortify.h>
#include <ctype.h>
#include <math.h>
#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]):
<rs232>
<host> <port>
<host>:<port>
*/
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 <driver> <rs232>
<host> <port>
*/
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;
}