EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  <20122013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024  Index 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  <20122013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: RE: Long string support in CA clients and device support
From: Mark Rivers <[email protected]>
To: "'Eric Norum'" <[email protected]>, Andrew Johnson <[email protected]>
Cc: "[email protected] talk" <[email protected]>
Date: Tue, 13 Nov 2012 19:57:39 +0000
Folks,

I've just done some investigation to see how asynPortDriver currently handles writing and reading strings in the parameter library. 

The lowest level class is paramVal.cpp which implements setString and getString as follows:

void paramVal::setString(const char *value)
{
    if (type != asynParamOctet)
        throw ParamValWrongType("paramVal::setString can only handle asynParamOctet");
    if (!isDefined() || (strcmp(data.sval, value)))
    {
        setDefined(true);
        if (data.sval != NULL)
            free(data.sval);
        data.sval = epicsStrDup(value);
        setValueChanged();
    }
}

char* paramVal::getString()
{
    if (type != asynParamOctet)
        throw ParamValWrongType("paramVal::getString can only handle asynParamOctet");
    if (!isDefined())
        throw ParamValNotDefined("paramVal::geString value not defined");
    return data.sval;
}

So setString just calls epicsStrDup (which adds a Nil terminator) and getString just returns a pointer to the string.  No problems here.

The next class up is paramList.cpp, which implements setString and getString as follows:

asynStatus paramList::setString(int index, const char *value)
{
    if (index < 0 || index >= this->nVals) return asynParamBadIndex;
    try {
        getParameter(index)->setString(value);
        registerParameterChange(getParameter(index), index);
    }
    catch (ParamValWrongType&){
        return asynParamWrongType;
    }
    catch (ParamListInvalidIndex&) {
        return asynParamBadIndex;
    }
    return asynSuccess;
}

asynStatus paramList::getString(int index, int maxChars, char *value)
{
    asynStatus status=asynSuccess;
    
    try {
        if (maxChars > 0) {
            paramVal *pVal = getParameter(index);
            status = pVal->getStatus();
            strncpy(value, pVal->getString(), maxChars-1);
            value[maxChars-1] = '\0';
        }
    }
    catch (ParamValWrongType&){
        return asynParamWrongType;
    }
    catch (ParamValNotDefined&){
        return asynParamUndefined;
    }
    catch (ParamListInvalidIndex&) {
        return asynParamBadIndex;
    }
    return status;
}

setString just calls paramVal->setString.  getString calls strncpy, which will return a Nil terminated string if there is room.  And then it sets the last element of the array to Nil, so the returned string is guaranteed to be Nil terminated, even if the buffer was not large enough.

The next class up is asynPortDriver.  It implements setStringParam and getStringParam as follows:

asynStatus asynPortDriver::setStringParam(int list, int index, const char *value)
{
    asynStatus status;
    static const char *functionName = "setStringParam";

    status = this->params[list]->setString(index, value);
    if (status) reportSetParamErrors(status, index, list, functionName);
    return(status);
}

asynStatus asynPortDriver::getStringParam(int list, int index, int maxChars, char *value)
{
    asynStatus status;
    static const char *functionName = "getStringParam";

    status = this->params[list]->getString(index, maxChars, value);
    if (status) reportGetParamErrors(status, index, list, functionName);
    return(status);
}

So it just calls paramList::setString and paramList::getString, which were described above.

asynPortDriver also implements writeOctet and readOctet, which are the methods called by device support (via a thin C wrapper function)

asynStatus asynPortDriver::writeOctet(asynUser *pasynUser, const char *value, 
                                    size_t nChars, size_t *nActual)
{
    int addr=0;
    int function = pasynUser->reason;
    asynStatus status = asynSuccess;
    const char *functionName = "writeOctet";

    status = getAddress(pasynUser, &addr); if (status != asynSuccess) return(status);

    /* Set the parameter in the parameter library. */
    status = (asynStatus)setStringParam(addr, function, (char *)value);

     /* Do callbacks so higher layers see any changes */
    status = (asynStatus)callParamCallbacks(addr, addr);

    if (status) 
        epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, 
                  "%s:%s: status=%d, function=%d, value=%s", 
                  driverName, functionName, status, function, value);
    else        
        asynPrint(pasynUser, ASYN_TRACEIO_DRIVER, 
              "%s:%s: function=%d, value=%s\n", 
              driverName, functionName, function, value);
    *nActual = nChars;
    return status;
}

asynStatus asynPortDriver::readOctet(asynUser *pasynUser,
                            char *value, size_t maxChars, size_t *nActual,
                            int *eomReason)
{
    int function = pasynUser->reason;
    int addr=0;
    asynStatus status = asynSuccess;
    const char *functionName = "readOctet";
   
    status = getAddress(pasynUser, &addr); if (status != asynSuccess) return(status);
    /* We just read the current value of the parameter from the parameter library.
     * Those values are updated whenever anything could cause them to change */
    status = (asynStatus)getStringParam(addr, function, maxChars, value);
    if (status) 
        epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, 
                  "%s:%s: status=%d, function=%d, value=%s", 
                  driverName, functionName, status, function, value);
    else        
        asynPrint(pasynUser, ASYN_TRACEIO_DRIVER, 
              "%s:%s: function=%d, value=%s\n", 
              driverName, functionName, function, value);
    if (eomReason) *eomReason = ASYN_EOM_END;
    *nActual = strlen(value);
    return(status);
}

So writeOctet calls asynPortDriver::setStringParam.  It returns nActual=nChars, i.e. the string length that was passed in.  Note that writeOctet() is often overridden in derived classes if an action needs to be taken immediately when the string it written.

readOctet calls asynPortDriver::getStringParam.  It returns nActual=strlen(value).  readOctet is rarely overridden in derived classes, typically reading the last value from the parameter library is all that is required.

The final routine it paramList::octetCallback.  This is the function that does callbacks to device support for strings with records that have SCAN=I/O Intr.

asynStatus paramList::octetCallback(int command, int addr)
{
    ELLLIST *pclientList;
    interruptNode *pnode;
    asynStandardInterfaces *pInterfaces = this->pasynInterfaces;
    int address;
    char *value;
    asynStatus status;

    /* Pass octet interrupts */
    value = getParameter(command)->getString();
    getStatus(command, &status);
    if (!pInterfaces->octetInterruptPvt) return(asynParamNotFound);
    pasynManager->interruptStart(pInterfaces->octetInterruptPvt, &pclientList);
    pnode = (interruptNode *)ellFirst(pclientList);
    while (pnode) {
        asynOctetInterrupt *pInterrupt = (asynOctetInterrupt *) pnode->drvPvt;
        pasynManager->getAddr(pInterrupt->pasynUser, &address);
        /* Set the status for the callback */
        pInterrupt->pasynUser->auxStatus = status;
        /* If this is not a multi-device then address is -1, change to 0 */
        if (address == -1) address = 0;
        if ((command == pInterrupt->pasynUser->reason) &&
            (address == addr)) {
            pInterrupt->callback(pInterrupt->userPvt,
                                 pInterrupt->pasynUser,
                                 value, strlen(value), ASYN_EOM_END);
        }
        pnode = (interruptNode *)ellNext(&pnode->node);
    }
    pasynManager->interruptEnd(pInterfaces->octetInterruptPvt);
    return(asynSuccess);
}

It also passes the length as strlen(value).

Conclusions:

- The strings in the parameter library are guaranteed to be Nil terminated because they are only set with epicsStrDup.

- Strings passed back in readOctet and octetCallback are guaranteed to be Nil terminated.

- The lengths returned in readOctet and octetCallback are strlen(value), i.e. without the Nil.  

- Should these be changed to strlen(value)+1?  That will cause NORD to increase to be strlen(value)+1.

I'm willing to do this, but I tend to agree with Eric that it seems more logical for NORD to be the actual string length.

Mark


-----Original Message-----
From: Eric Norum [mailto:[email protected]] 
Sent: Tuesday, November 13, 2012 1:20 PM
To: Andrew Johnson
Cc: Mark Rivers
Subject: Re: Long string support in CA clients and device support

The more that I think about this the more I'm unsure that increasing NORD to include a trailing '\0' is a good idea.  NORD, as its name implies, is the number of characters read.  
Existing message-based device support  of which I'm aware (asyn IP, asyn serial to mention a couple) return just the number of characters actually read -- no extra '\0' added.   So clients dealing with long string waveforms should already know that they have to deal with non-terminated strings.   Having NORD incremented in some places and not in others just seems wrong to me.

-- 
Eric Norum
[email protected]






Replies:
RE: Long string support in CA clients and device support Mark Rivers
References:
Long string support in CA clients and device support Andrew Johnson

Navigate by Date:
Prev: Re: archive data server Kasemir, Kay
Next: RE: Long string support in CA clients and device support Mark Rivers
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  <20122013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
Navigate by Thread:
Prev: Long string support in CA clients and device support Andrew Johnson
Next: RE: Long string support in CA clients and device support Mark Rivers
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  <20122013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
ANJ, 18 Nov 2013 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·