1209 lines
30 KiB
C
1209 lines
30 KiB
C
/*---------------------------------------------------------------------------
|
|
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 <errno.h>
|
|
#include "sics.h"
|
|
#include "splitter.h"
|
|
#include "ease.h"
|
|
#include "sicshipadaba.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, char *reason)
|
|
{
|
|
if (eab->state >= EASE_idle) {
|
|
FsmStop(eab->task, eab->idle);
|
|
closeRS232(eab->ser);
|
|
}
|
|
if (reason != NULL) {
|
|
snprintf(eab->msg, sizeof eab->msg, "offline (%s)", reason);
|
|
ParPrintf(eab, eLogError, "ERROR: %s", eab->msg);
|
|
}
|
|
eab->state = EASE_offline;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
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:
|
|
if (eab->p.verbose < 1)
|
|
break;
|
|
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, "controller exchanged");
|
|
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)
|
|
{
|
|
/* cmd==NULL: repeat the same command */
|
|
int iRet;
|
|
char trash[64];
|
|
int l;
|
|
|
|
if (eab->errCode) {
|
|
FsmStop(eab->task, eab->idle);
|
|
eab->state = EASE_abort;
|
|
return;
|
|
}
|
|
if (eab->state == EASE_expect || eab->state == EASE_abort)
|
|
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);
|
|
}
|
|
if (cmd) {
|
|
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,
|
|
"offline (connection to %s:%d lost)", eab->ser->pHost, eab->ser->iPort);
|
|
ParPrintf(eab, eError, "ERROR: %s", eab->msg);
|
|
closeRS232(eab->ser);
|
|
eab->state = EASE_notconnected;
|
|
eab->errCode = iRet;
|
|
return;
|
|
}
|
|
if (cmd) {
|
|
ParPrintf(eab, -2, "cmd: %s", cmd);
|
|
} else {
|
|
ParPrintf(eab, -2, "cmd: %s", eab->cmd);
|
|
}
|
|
eab->state = EASE_expect;
|
|
eab->cmdtime = time(NULL);
|
|
eab->chktime = time(NULL) + 5;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
int EaseWaitRead(EaseBase * eab)
|
|
{
|
|
int cnt = 0;
|
|
time_t start;
|
|
|
|
if (eab->state < EASE_idle) {
|
|
return NOTCONNECTED;
|
|
}
|
|
FsmPause(eab->task, 1);
|
|
time(&start);
|
|
while (eab->state == EASE_expect) {
|
|
/* TaskYield(pServ->pTasker); does not work (pardef is not recursive) */
|
|
FsmTaskHandler(eab->task);
|
|
cnt++;
|
|
if (time(NULL) > start + 5) {
|
|
eab->ans[0] = '\0';
|
|
eab->state = EASE_read;
|
|
return TIMEOUT;
|
|
}
|
|
}
|
|
FsmPause(eab->task, 0);
|
|
/* if (cnt>1) ParPrintf(eab, eError, "TaskYield %d\n", cnt); */
|
|
return 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
void EaseParHasChanged(void)
|
|
{
|
|
parameterChange = 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
void EasePchk(void *drv)
|
|
{
|
|
int pc = FsmPc();
|
|
if (pc > 0) {
|
|
ParPrintf(drv, eError, "ERROR: illegal line in fsm %d", pc);
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
void EaseSavePars(void)
|
|
{
|
|
char *pFile = NULL;
|
|
|
|
if (parameterChange) {
|
|
parameterChange = 0;
|
|
|
|
assert(pServ->pSics);
|
|
|
|
pFile = IFindOption(pSICSOptions, "statusfile");
|
|
if (pFile) {
|
|
WriteSicsStatus(pServ->pSics, pFile, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static int EaseRestart(EaseBase * eab)
|
|
{
|
|
int iRet;
|
|
|
|
eab->errCode = 0;
|
|
if (eab->task) {
|
|
FsmStop(eab->task, eab->idle);
|
|
FsmRestartTask(eab->task, eab->idle);
|
|
}
|
|
eab->startOk = 0;
|
|
eab->todo = eab->start;
|
|
eab->state = EASE_connecting;
|
|
iRet = initRS232WithFlags(eab->ser, 3);
|
|
if (iRet != 1) {
|
|
eab->errCode = iRet;
|
|
EaseWriteError(eab);
|
|
return -1;
|
|
}
|
|
if (eab->cmdtime == 0) {
|
|
snprintf(eab->msg, sizeof eab->msg,
|
|
"connecting to %s:%d", eab->ser->pHost, eab->ser->iPort);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
void EaseUpdateHwstate(void *object, int drivStatus) {
|
|
EaseDriv *ead = EaseDrivCast(object);
|
|
int is_running = drivStatus == HWBusy;
|
|
|
|
ead->hwstate = drivStatus;
|
|
|
|
if (ead->is_running != is_running) {
|
|
ead->is_running = is_running;
|
|
ParUpdateAll(ead);
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
int EaseHandler(EaseBase * eab)
|
|
{
|
|
EaseDriv *ead = EaseDrivCast(eab);
|
|
int iret;
|
|
|
|
EaseSavePars();
|
|
if (ead && ead->stopped && ead->hwstate == HWBusy) {
|
|
ead->stopped = 0;
|
|
EaseUpdateHwstate(ead, HWIdle);
|
|
if (FsmStop(eab->task, eab->doit)) {
|
|
ParPrintf(eab, eWarning, "%s stopped", eab->p.name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (eab->state == EASE_offline) {
|
|
return 0;
|
|
}
|
|
if (eab->state == EASE_expect) {
|
|
if (eab->cmdtime != 0 && time(NULL) > eab->cmdtime + eab->p.period) {
|
|
snprintf(eab->msg, sizeof eab->msg, "no response");
|
|
}
|
|
return 0;
|
|
}
|
|
if (eab->state == EASE_lost) {
|
|
snprintf(eab->msg, sizeof eab->msg, "no response");
|
|
}
|
|
if (eab->state == EASE_notconnected) {
|
|
if (time(0) >= eab->cmdtime + 10) {
|
|
EaseRestart(eab);
|
|
}
|
|
return 0;
|
|
}
|
|
if (eab->state == EASE_connecting) {
|
|
iret = initRS232Finished(eab->ser);
|
|
if (iret == 0)
|
|
return 0; /* waiting for connection */
|
|
if (iret < 0) {
|
|
if (errno == ECONNREFUSED) {
|
|
snprintf(eab->msg, sizeof eab->msg,
|
|
"Connection refused");
|
|
} else {
|
|
snprintf(eab->msg, sizeof eab->msg,
|
|
"Connection failed (%s)", strerror(errno));
|
|
}
|
|
if (eab->cmdtime == 0) {
|
|
ParPrintf(eab, eError, "%s: %s", eab->p.name, eab->msg);
|
|
}
|
|
closeRS232(eab->ser);
|
|
eab->state = EASE_notconnected;
|
|
eab->cmdtime = time(0);
|
|
return 0;
|
|
} else {
|
|
if (eab->tmo > 20) {
|
|
eab->tmo = 20;
|
|
}
|
|
eab->msg[0] = '\0';
|
|
eab->state = EASE_idle;
|
|
}
|
|
}
|
|
if (eab->errCode) {
|
|
if (eab->errCode == BADSEND) {
|
|
eab->cmdtime = 0; /* means: this is not a repeated restart */
|
|
EaseRestart(eab);
|
|
return 0;
|
|
}
|
|
if (eab->state != EASE_abort) {
|
|
EaseWriteError(eab);
|
|
}
|
|
eab->errCode = 0;
|
|
if (ead) {
|
|
EaseUpdateHwstate(ead, HWFault);
|
|
FsmStop(eab->task, eab->doit);
|
|
}
|
|
return 0;
|
|
}
|
|
if (ead) {
|
|
if (eab->state == EASE_lost) {
|
|
if (FsmStop(eab->task, eab->doit)) {
|
|
ParPrintf(eab, eWarning, "stop %s caused by timeout", 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;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static long EaseSendIt(long pc, void *object)
|
|
{
|
|
EaseBase *eab = EaseBaseCast(object);
|
|
|
|
switch (pc) {
|
|
default: /* FSM BEGIN ****************************** */
|
|
EasePchk(eab);
|
|
EaseWrite(eab, eab->sendCmd);
|
|
ParPrintf(eab, eWarning, "send cmd> %s", eab->sendCmd);
|
|
eab->sendCmd = NULL;
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
ParPrintf(eab, eWarning, "response> %s", eab->ans);
|
|
return 0;
|
|
} /* FSM END ******************************************* */
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
int EaseCheckDoit(EaseBase * eab)
|
|
{
|
|
int i, n;
|
|
|
|
if (eab->todo == NULL) {
|
|
if (eab->sendCmd != NULL) {
|
|
eab->todo = EaseSendIt;
|
|
return 1;
|
|
}
|
|
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 ****************************** */
|
|
EasePchk(eab);
|
|
if (eab->state == EASE_abort) {
|
|
eab->state = EASE_idle;
|
|
}
|
|
|
|
idle:
|
|
if (!EaseCheckDoit(eab))
|
|
goto rd;
|
|
doit:
|
|
todo = eab->todo;
|
|
eab->todo = NULL;
|
|
FsmCall(todo);
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
eab->startOk = 1;
|
|
rd:
|
|
/* 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__: /**********************************/
|
|
|
|
if (EaseCheckDoit(eab))
|
|
goto doit;
|
|
/*
|
|
gettimeofday(&tm, NULL);
|
|
printf("stop %s %f\n", eab->evc->pName, tm.tv_usec / 1e6);
|
|
*/
|
|
/* FsmWait(1); do we really need that? -- remove for faster response to send */
|
|
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] |= (unsigned long)1 << (flag % EASE_FLAGBITS);
|
|
if (eab->task)
|
|
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 < EASE_FLAGBITS) {
|
|
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 (((unsigned long)1 << (flag % EASE_FLAGBITS)) & eab->updateFlags[i]) {
|
|
return 1;
|
|
}
|
|
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] |= (unsigned long)1 << (flag % EASE_FLAGBITS);
|
|
} else {
|
|
eab->updateFlags[flag / EASE_FLAGBITS] &=
|
|
~((unsigned long)1 << (flag % EASE_FLAGBITS));
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static long EaseRun(void *obj, SConnection * pCon, float fVal)
|
|
{
|
|
EaseBase *eab = EaseBaseCast(obj);
|
|
EaseDriv *ead = EaseDrivCast(obj);
|
|
int canc;
|
|
|
|
assert(ead);
|
|
|
|
ParSaveConn(eab, pCon);
|
|
if (!eab->doit) {
|
|
ParPrintf(ead, eError, "ERROR: missing run function %s", eab->p.name);
|
|
return 0;
|
|
}
|
|
if (!eab->startOk) {
|
|
ParPrintf(ead, eError, "ERROR: %s is not ready to run", eab->p.name);
|
|
return 0;
|
|
}
|
|
if (FsmStop(eab->task, eab->doit)) {
|
|
canc = 1;
|
|
ParPrintf(ead, eWarning, "running %s cancelled", eab->p.name);
|
|
} else {
|
|
canc = 0;
|
|
}
|
|
if (eab->todo) {
|
|
ParPrintf(ead, eError, "ERROR: %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->is_running = -1; /* force update of is_running even when not changed */
|
|
EaseUpdateHwstate(ead, 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;
|
|
if (canc) {
|
|
ParPrintf(ead, eWarning, "rerun %s", eab->p.name);
|
|
}
|
|
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);
|
|
ParSaveConn(ead, pCon);
|
|
if (ead->stopped) {
|
|
return HWIdle;
|
|
} else if (!ead->is_running) {
|
|
ead->stopped = 1;
|
|
return HWIdle;
|
|
}
|
|
now = time(NULL);
|
|
if (now > ead->timeout) {
|
|
ParPrintf(obj, eWarning, "maxwait expired when driving %s",
|
|
ead->b.p.name);
|
|
EaseUpdateHwstate(ead, 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);
|
|
EaseUpdateHwstate(ead, 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 EaseStdHandler(void *object)
|
|
{
|
|
int iret, l;
|
|
EaseBase *eab = EaseBaseCast(object);
|
|
char *corr;
|
|
|
|
assert(eab);
|
|
if (eab->state < EASE_idle)
|
|
goto quit;
|
|
if (availableNetRS232(eab->ser) || availableRS232(eab->ser)) {
|
|
if (strncmp(eab->msg, "ERROR:", 6) != 0) {
|
|
eab->msg[0] = '\0';
|
|
}
|
|
l = sizeof(eab->ans);
|
|
iret = readRS232TillTerm(eab->ser, eab->ans, &l);
|
|
if (eab->state != EASE_expect && eab->state != EASE_lost) {
|
|
if (iret == 1) {
|
|
ParPrintf(eab, eError, "unexpected answer: %s", eab->ans);
|
|
}
|
|
goto quit;
|
|
}
|
|
if (iret == 1) {
|
|
ParPrintf(eab, -2, "ans: %s", eab->ans);
|
|
if (eab->state == EASE_lost) {
|
|
goto quit;
|
|
} else {
|
|
eab->tmo = 120;
|
|
}
|
|
}
|
|
if (iret != 1) {
|
|
eab->errCode = iret;
|
|
eab->state = EASE_idle;
|
|
goto error;
|
|
}
|
|
eab->state = EASE_read;
|
|
} else if (eab->state == EASE_expect) {
|
|
if (time(NULL) > eab->cmdtime + eab->tmo) {
|
|
eab->state = EASE_lost;
|
|
}
|
|
} else if (eab->state == EASE_lost) {
|
|
}
|
|
goto quit;
|
|
error:
|
|
/* EaseWriteError(eab); */
|
|
quit:
|
|
return EaseHandler(eab);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
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];
|
|
|
|
if (handler == NULL) {
|
|
handler = EaseStdHandler;
|
|
}
|
|
assert(eab);
|
|
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->startOk = 0;
|
|
eab->errCode = 0;
|
|
eab->version[0] = '\0';
|
|
eab->maxflag = maxflag;
|
|
eab->sendCmd = NULL;
|
|
eab->tmo = 20;
|
|
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, 5000); /* milliseconds */
|
|
setRS232Debug(eab->ser, 0);
|
|
|
|
eab->task = NULL;
|
|
eab->cmdtime = 0; /* means: this is not a repeated restart */
|
|
if (EaseRestart(eab) < 0)
|
|
return -1;
|
|
eab->task = FsmStartTask(eab, eab->handler, eab->idle, eab->p.name);
|
|
|
|
TaskRegisterN(pServ->pTasker,"ease", (TaskFunc) FsmTaskHandler, NULL, FsmKill,
|
|
eab->task, TASK_PRIO_LOW);
|
|
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 = Arg2Tcl(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 = Arg2Tcl(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->drivInt->Halt = EaseHalt;
|
|
ead->drivInt->CheckLimits = EaseCheckLimits;
|
|
ead->drivInt->SetValue = EaseRun;
|
|
ead->drivInt->CheckStatus = EaseCheckStatus;
|
|
ead->drivInt->GetValue = EaseGetValue;
|
|
|
|
ead->maxwait = -1;
|
|
ead->lowerLimit = 0;
|
|
ead->upperLimit = 1000;
|
|
ead->settle = 0;
|
|
ead->tolerance = 1;
|
|
ead->targetValue = 0;
|
|
ead->is_running = 0;
|
|
|
|
EaseUpdateHwstate(ead, HWIdle);
|
|
/* EMon interface to be implemented */
|
|
return ead;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
void EaseMsgPar(void *object)
|
|
{
|
|
EaseBase *eab = EaseBaseCast(object);
|
|
|
|
assert(eab);
|
|
|
|
ParName("status");
|
|
ParAccess(usUser);
|
|
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);
|
|
char *colon;
|
|
char *hostpos;
|
|
int port, i;
|
|
char host[64], hostport[80];
|
|
char *cCmd;
|
|
|
|
/* short string with length 0 or 1: connect to previous host/port */
|
|
if (argc > 0 && strlen(argv[0]) > 1) {
|
|
colon = strchr(argv[0], ':');
|
|
if (!colon || argc > 1) {
|
|
ParPrintf(object, eError, "ERROR: illegal syntax for: %s restart",
|
|
eab->p.name);
|
|
return 0;
|
|
}
|
|
port = atoi(colon + 1);
|
|
i = colon - argv[0];
|
|
if (i >= sizeof host) {
|
|
ParPrintf(object, eError, "ERROR: host name too long");
|
|
return 0;
|
|
}
|
|
strncpy(host, argv[0], i);
|
|
host[i] = '\0';
|
|
if (eab->p.creationCmd != NULL) {
|
|
hostpos = strstr(eab->p.creationCmd, eab->ser->pHost);
|
|
if (hostpos != NULL) {
|
|
/* replace the second part of creationCmd with new host/port */
|
|
snprintf(hostport, sizeof hostport, "%s:%d", host, port);
|
|
hostpos[0]='\0';
|
|
cCmd = calloc(1, strlen(eab->p.creationCmd) + strlen(hostport) + 1);
|
|
strcpy(cCmd, eab->p.creationCmd);
|
|
strcat(cCmd, hostport);
|
|
free(eab->p.creationCmd);
|
|
eab->p.creationCmd = cCmd;
|
|
}
|
|
}
|
|
if (eab->ser->pHost != NULL) {
|
|
free(eab->ser->pHost);
|
|
}
|
|
eab->ser->pHost = strdup(host);
|
|
eab->ser->iPort = port;
|
|
}
|
|
if (eab->task) {
|
|
EaseStop(eab, NULL);
|
|
}
|
|
eab->cmdtime = 0; /* means: this is not a repeated restart */
|
|
EaseRestart(eab);
|
|
return 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
int EaseDisconnectWrapper(void *object, void *userarg, int argc, char *argv[])
|
|
{
|
|
EaseBase *eab = EaseBaseCast(object);
|
|
|
|
snprintf(eab->msg, sizeof eab->msg, "disconnected");
|
|
ParPrintf(object, eWarning, "%s", eab->msg);
|
|
EaseStop(eab, NULL);
|
|
eab->state = EASE_offline;
|
|
return 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
void EaseBasePar(void *object)
|
|
{
|
|
EaseBase *eab = EaseBaseCast(object);
|
|
|
|
assert(eab);
|
|
|
|
ParName("restart");
|
|
ParAccess(usUser);
|
|
ParCmd(EaseRestartWrapper, NULL);
|
|
|
|
ParName("reconnect");
|
|
ParAccess(usUser);
|
|
ParCmd(EaseRestartWrapper, NULL);
|
|
|
|
ParName("disconnect");
|
|
ParAccess(usUser);
|
|
ParCmd(EaseDisconnectWrapper, 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) {
|
|
eab->sendCmd = ParArg2Str(argc, argv, NULL, 0);
|
|
/*
|
|
ParPrintf(eab, -2, "ans: %s", ans);
|
|
ParPrintf(eab, eValue, "%s", ans);
|
|
*/
|
|
}
|
|
if (iret < 0) {
|
|
eab->errCode = iret;
|
|
EaseWriteError(eab);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
void EaseSendPar(void *object)
|
|
{
|
|
ParName("send");
|
|
ParAccess(usUser);
|
|
ParCmd(EaseSend, NULL);
|
|
return;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
void EaseKillDriv(EaseDriv * ead)
|
|
{
|
|
if (ead->drivInt) {
|
|
free(ead->drivInt);
|
|
}
|
|
if (ead->evInt) {
|
|
free(ead->evInt);
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
void EaseRunPar(void *object)
|
|
{
|
|
EaseDriv *ead;
|
|
|
|
assert(ead = EaseDrivCast(object));
|
|
|
|
ParName("is_running");
|
|
ParAccess(usUser);
|
|
ParInt(&ead->is_running, 0);
|
|
if (ParActionIs(PAR_SET) > 0) {
|
|
if (!ead->is_running) {
|
|
EaseHalt(ead);
|
|
}
|
|
}
|
|
}
|
|
/*----------------------------------------------------------------------------*/
|
|
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, 1.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);
|
|
|
|
EaseRunPar(ead);
|
|
|
|
if (ParActionIs(PAR_KILL)) {
|
|
EaseKillDriv(ead);
|
|
}
|
|
return;
|
|
}
|