Hej Ralph,
I don't know if I understand the question 100%.
So let me try to explain, what is happening:
In st.cmd, we start a poller thread, talking to the hardware
(=motion controller unit, MCU in ESS speach)
(And the poller is a model 3 motordriver)
When the poller reads the value from the MCU, it calls
setIntegerParam(axisNo, function, newValue);
callBacksNeeded = 1;
and later
if (callBacksNeeded) {
callParamCallbacks();
}
When the IOC is not yet started, the callbacks are blocked somewhere.
But the setIntegerParam() succeeds and the new value is in the
asyn parameter libray.
The bo record does this, once the record need to be initialized:
static long initBo(boRecord *pr)
{
devPvt *pPvt;
int status;
epicsInt32 value;
status = initCommon((dbCommon *)pr,&pr->out,
processCallbackOutput,interruptCallbackOutput,
interruptCallbackEnumBo,
2, (char*)&pr->znam, NULL, &pr->zsv);
if (status != INIT_OK) return status;
pPvt = pr->dpvt;
/* Read the current value from the device */
status = pasynInt32SyncIO->read(pPvt->pasynUserSync,
&value, pPvt->pasynUser->timeout);
if (status == asynSuccess) {
pr->rval = value;
return INIT_OK;
}
return INIT_DO_NOT_CONVERT;
}
So when the record can read a value from the paramlib,
pasynInt32SyncIO() return asynSuccess, the value
is put into the record and everything is fine.
But, there is a race:
When the iocInit() is faster than the poller, the record
is initialized in initBo(), but kept in
"INVALID DRIVER UDF" alarm state.
Later (could be milliseconds, seconds, whatever)
when the poller can talk to the MCU, it will write the value
into the paramlibrary (as before).
(The dev support will resset UDF to 0)
A record processing is triggered and executed.
The new value is in the record, but the alarm status stays,
until the record is processed another time:
static long processBo(boRecord *pr)
{
devPvt *pPvt = (devPvt *)pr->dpvt;
int status;
epicsMutexLock(pPvt->devPvtLock);
if(pPvt->newOutputCallbackValue && getCallbackValue(pPvt)) {
/* We got a callback from the driver */
if (pPvt->result.status == asynSuccess) {
pr->rval = pPvt->result.value;
pr->val = (pr->rval) ? 1 : 0;
pr->udf = 0;
}
If I change the code like this:
static long processBo(boRecord *pr)
{
devPvt *pPvt = (devPvt *)pr->dpvt;
int status;
epicsMutexLock(pPvt->devPvtLock);
if(pPvt->newOutputCallbackValue && getCallbackValue(pPvt)) {
/* We got a callback from the driver */
fprintf(stdout, "%s/%s:%d %s pPvt->result.status=%d\n",
__FILE__, __FUNCTION__, __LINE__,
pr->name, pPvt->result.status);
if (pPvt->result.status == asynSuccess) {
pr->rval = pPvt->result.value;
pr->val = (pr->rval) ? 1 : 0;
if (pr->udf) {
pr->udf = 0;
pr->nsta = 0;
pr->nsev = 0;
}
}
Everything is fine.
Does this explain anything ?
Thanks to everybody
/Torsten
On 5/28/21 11:02 AM, Ralph Lange via Tech-talk wrote:
Might that be related to the initial readback being an asynchronous
process(ing)?
The whole idea of doing an initial readback as part of record
initialization is based on the assumption that the readback is
immediate, i.e. synchronous.
If it is asynchronous and the device answer is only accepted after the
equivalent of interruptAccept being true, the timing and order of
things may be pretty different. In those cases, initial readback has
to be done as part of PINI processing, as that runs late enough and
works with asynchronous device access. In case that asyn:READBACK is
set, the read request can be done as part of the record initialization
if the device answer is delayed long enough (or queued) to be
processed after interruptAccept. Dropping the device answer would
leave the record with UDF cleared (from the silent initial readback)
but without processing (as the device answer was lost), which
resembles what Torsten is seeing, doesn't it?
Cheers,
~Ralph