diff --git a/src/cas/example/simple/Makefile.Unix b/src/cas/example/simple/Makefile.Unix index bc5e63fc7..4aa108233 100644 --- a/src/cas/example/simple/Makefile.Unix +++ b/src/cas/example/simple/Makefile.Unix @@ -19,7 +19,8 @@ DEPLIBS = $(DEPLIBS_BASE)/libcas.a $(DEPLIBSWOCAS) SRCS.cc += ../main.cc SRCS.cc += ../exServer.cc SRCS.cc += ../exPV.cc -SRCS.cc += ../exSyncPV.cc +SRCS.cc += ../exVectorPV.cc +SRCS.cc += ../exScalarPV.cc SRCS.cc += ../exAsyncPV.cc SRCS.cc += ../exChannel.cc SRCS.cc += ../templInst.cc @@ -27,7 +28,8 @@ SRCS.cc += ../templInst.cc OBJS += main.o OBJS += exServer.o OBJS += exPV.o -OBJS += exSyncPV.o +OBJS += exVectorPV.o +OBJS += exScalarPV.o OBJS += exAsyncPV.o OBJS += exChannel.o OBJS += templInst.o diff --git a/src/cas/example/simple/exPV.cc b/src/cas/example/simple/exPV.cc index 86a588064..13d2fac69 100644 --- a/src/cas/example/simple/exPV.cc +++ b/src/cas/example/simple/exPV.cc @@ -5,8 +5,6 @@ #include #include -const double myPI = 3.14159265358979323846; - osiTime exPV::currentTime; // @@ -20,9 +18,9 @@ exPV::exPV (const casCtx &ctxIn, const pvInfo &setup) : interest(aitFalse) { // - // load initial value + // no dataless PV allowed // - this->scanPV(); + assert (this->info.getElementCount()>=1u); } // @@ -41,55 +39,46 @@ exPV::~exPV() } // -// exPV::scanPV(); +// exPV::update() // -void exPV::scanPV() +caStatus exPV::update(gdd &valueIn) { - caStatus status; - double radians; - gdd *pDD; - float newValue; - float limit; - caServer *pCAS = this->getCAS(); + caServer *pCAS = this->getCAS(); + // + // gettimeofday() is very slow under sunos4 + // + osiTime cur (this->currentTime); + struct timespec t; + caStatus cas; + + if (!pCAS) { + return S_casApp_noSupport; + } + +# if DEBUG + printf("Setting %s too:\n", this->info.getName().string()); + valueIn.dump(); +# endif - if (!pCAS) { - return; + cas = this->updateValue (valueIn); + if (cas || !this->pValue) { + return cas; } + cur.get (t.tv_sec, t.tv_nsec); + this->pValue->setTimeStamp(&t); + this->pValue->setStat (epicsAlarmNone); + this->pValue->setSevr (epicsSevNone); + // - // update current time (so we are not required to do - // this every time that we write the PV which impacts - // throughput under sunos4 because gettimeofday() is - // slow) + // post a value change event // - this->currentTime = osiTime::getCurrent(); - - pDD = new gddScalar (gddAppType_value, aitEnumFloat32); - if (!pDD) { - return; - } - - radians = (rand () * 2.0 * myPI)/RAND_MAX; - if (this->pValue) { - this->pValue->getConvert(newValue); - } - else { - newValue = 0.0f; - } - newValue += (float) (sin (radians) / 10.0); - limit = (float) this->info.getHopr(); - newValue = min (newValue, limit); - limit = (float) this->info.getLopr(); - newValue = max (newValue, limit); - *pDD = newValue; - pDD->setStat (epicsAlarmNone); - pDD->setSevr (epicsSevNone); - status = this->update (*pDD); - if (status) { - errMessage (status, "scan update failed\n"); - } - - pDD->unreference(); + if (this->interest==aitTrue) { + casEventMask select(pCAS->valueEventMask|pCAS->logEventMask); + this->postEvent (select, *this->pValue); + } + + return S_casApp_success; } // @@ -97,7 +86,7 @@ void exPV::scanPV() // void exScanTimer::expire () { - pv.scanPV(); + pv.scan(); } // @@ -124,77 +113,6 @@ const char *exScanTimer::name() const return "exScanTimer"; } -// -// exPV::update () -// -caStatus exPV::update(gdd &valueIn) -{ - gdd *pNewValue; - caServer *pCAS = this->getCAS(); - // - // gettimeofday() is very slow under sunos4 - // - osiTime cur (this->currentTime); - struct timespec t; - gddStatus gdds; - - - if (!pCAS) { - return S_casApp_noSupport; - } - -# if DEBUG - printf("Setting %s too:\n", this->info.getName().string()); - valueIn.dump(); -# endif - - - if (valueIn.isScalar()) { - pNewValue = &valueIn; - pNewValue->reference(); - } - else { - // - // this does not modify the current value - // (because it may be referenced in the event queue) - // - pNewValue = new gddScalar (gddAppType_value, aitEnumFloat32); - if (!pNewValue) { - return S_casApp_noMemory; - } - - gdds = gddApplicationTypeTable:: - app_table.smartCopy(pNewValue, &valueIn); - if (gdds) { - pNewValue->unreference(); - return S_cas_noConvert; - } - - pNewValue->setStat (epicsAlarmNone); - pNewValue->setSevr (epicsSevNone); - } - - cur.get (t.tv_sec, t.tv_nsec); - pNewValue->setTimeStamp(&t); - - // - // release old value and replace it - // with the new one - // - if (this->pValue) { - this->pValue->unreference(); - } - this->pValue = pNewValue; - - if (this->interest==aitTrue) { - casEventMask select(pCAS->valueEventMask|pCAS->logEventMask); - this->postEvent (select, *this->pValue); - } - - return S_casApp_success; -} - - // // exPV::bestExternalType() // @@ -354,7 +272,7 @@ caStatus exPV::getLowLimit(gdd &value) // caStatus exPV::getUnits(gdd &units) { - static aitString str("@#$%"); + static aitString str("furlongs"); units.put(str); return S_cas_success; } @@ -392,3 +310,19 @@ caStatus exPV::getValue(gdd &value) return status; } +// +// exPV::write() +// +caStatus exPV::write (const casCtx &, gdd &valueIn) +{ + return this->update (valueIn); +} + +// +// exPV::read() +// +caStatus exPV::read (const casCtx &, gdd &protoIn) +{ + return exServer::read(*this, protoIn); +} + diff --git a/src/cas/example/simple/exScalarPV.cc b/src/cas/example/simple/exScalarPV.cc new file mode 100644 index 000000000..b2604b84a --- /dev/null +++ b/src/cas/example/simple/exScalarPV.cc @@ -0,0 +1,99 @@ + +#include +#include +#include +#include +#include + +#define myPI 3.14159265358979323846 + +// +// SUN C++ does not have RAND_MAX yet +// +#if !defined(RAND_MAX) +// +// Apparently SUN C++ is using the SYSV version of rand +// +#if 0 +#define RAND_MAX INT_MAX +#else +#define RAND_MAX SHRT_MAX +#endif +#endif + +// +// exScalarPV::scan +// +void exScalarPV::scan() +{ + caStatus status; + double radians; + gdd *pDD; + float newValue; + float limit; + + // + // update current time (so we are not required to do + // this every time that we write the PV which impacts + // throughput under sunos4 because gettimeofday() is + // slow) + // + this->currentTime = osiTime::getCurrent(); + + pDD = new gddScalar (gddAppType_value, aitEnumFloat32); + if (!pDD) { + return; + } + + radians = (rand () * 2.0 * myPI)/RAND_MAX; + if (this->pValue) { + this->pValue->getConvert(newValue); + } + else { + newValue = 0.0f; + } + newValue += (float) (sin (radians) / 10.0); + limit = (float) this->info.getHopr(); + newValue = min (newValue, limit); + limit = (float) this->info.getLopr(); + newValue = max (newValue, limit); + *pDD = newValue; + status = this->update (*pDD); + if (status) { + errMessage (status, "scan update failed\n"); + } + + pDD->unreference(); +} + +// +// exScalarPV::updateValue () +// +// NOTES: +// 1) This should have a test which verifies that the +// incoming value in all of its various data types can +// be translated into a real number? +// 2) We prefer to unreference the old PV value here and +// reference the incomming value because this will +// result in value change events each retaining an +// independent value on the event queue. +// +caStatus exScalarPV::updateValue (gdd &valueIn) +{ + if (!valueIn.isScalar()) { + return S_casApp_outOfBounds; + } + + // + // release old value and replace it + // with the new one + // + if (this->pValue) { + this->pValue->unreference(); + } + valueIn.reference(); + this->pValue = &valueIn; + + return S_casApp_success; +} + diff --git a/src/cas/example/simple/exServer.cc b/src/cas/example/simple/exServer.cc index 6f3baed63..fba5d736f 100644 --- a/src/cas/example/simple/exServer.cc +++ b/src/cas/example/simple/exServer.cc @@ -10,10 +10,12 @@ #include const pvInfo exServer::pvList[] = { - pvInfo (1.0e-1, "jane", 10.0f, 0.0f, excasIoSync), - pvInfo (2.0, "fred", 10.0f, -10.0f, excasIoSync), - pvInfo (1.0e-1, "janet", 10.0f, 0.0f, excasIoAsync), - pvInfo (2.0, "freddy", 10.0f, -10.0f, excasIoAsync) + pvInfo (1.0e-1, "jane", 10.0f, 0.0f, excasIoSync, 1u), + pvInfo (2.0, "fred", 10.0f, -10.0f, excasIoSync, 1u), + pvInfo (1.0e-1, "janet", 10.0f, 0.0f, excasIoAsync, 1u), + pvInfo (2.0, "freddy", 10.0f, -10.0f, excasIoAsync, 1u), + pvInfo (2.0, "alan", 10.0f, -10.0f, excasIoSync, 100u), + pvInfo (20.0, "albert", 10.0f, -10.0f, excasIoSync, 1000u) }; // @@ -96,20 +98,45 @@ const pvInfo *exServer::findPV(const char *pName) casPV *exServer::createPV (const casCtx &ctxIn, const char *pPVName) { const pvInfo *pInfo; + exPV *pPV; pInfo = exServer::findPV(pPVName); if (!pInfo) { return NULL; } - switch (pInfo->getIOType()){ - case excasIoSync: - return new exSyncPV (ctxIn, *pInfo); - case excasIoAsync: - return new exAsyncPV (ctxIn, *pInfo); - default: - return NULL; + // + // create an instance of the appropriate class + // depending on the io type and the number + // of elements + // + if (pInfo->getElementCount()==1u) { + switch (pInfo->getIOType()){ + case excasIoSync: + pPV = new exScalarPV (ctxIn, *pInfo); + break; + case excasIoAsync: + pPV = new exAsyncPV (ctxIn, *pInfo); + break; + default: + pPV = NULL; + break; + } } + else { + pPV = new exVectorPV (ctxIn, *pInfo); + } + + // + // load initial value (this is not done in + // the constructor because the base class's + // pure virtual function would be called) + // + if (pPV) { + pPV->scan(); + } + + return pPV; } // diff --git a/src/cas/example/simple/exServer.h b/src/cas/example/simple/exServer.h index 8cc71a634..80cb2b10b 100644 --- a/src/cas/example/simple/exServer.h +++ b/src/cas/example/simple/exServer.h @@ -1,29 +1,27 @@ // -// Example EPICS CA server +// Example EPICS CA server // +// +// caServer +// | +// exServer +// +// casPV +// | +// exPV------------- +// | | +// exScalarPV exVectorPV +// | +// exAsyncPV +// +// + // // ANSI C // #include #include -#include -#include -#include - -// -// SUN C++ does not have RAND_MAX yet -// -#if !defined(RAND_MAX) -// -// Apparently SUN C++ is using the SYSV version of rand -// -#if 0 -#define RAND_MAX INT_MAX -#else -#define RAND_MAX SHRT_MAX -#endif -#endif // // EPICS @@ -56,20 +54,20 @@ class pvInfo { public: pvInfo (double scanRateIn, const char *pName, aitFloat32 hoprIn, aitFloat32 loprIn, - excasIoType ioTypeIn) : + excasIoType ioTypeIn, unsigned countIn) : scanRate(scanRateIn), name(pName), hopr(hoprIn), - lopr(loprIn), ioType(ioTypeIn) + lopr(loprIn), ioType(ioTypeIn), elementCount(countIn) { } // - // for use when MSVC++ will not build a defualt copy constructor + // for use when MSVC++ will not build a default copy constructor // for this class // pvInfo (const pvInfo ©In) : scanRate(copyIn.scanRate), name(copyIn.name), hopr(copyIn.hopr), lopr(copyIn.lopr), - ioType(copyIn.ioType) + ioType(copyIn.ioType), elementCount(copyIn.elementCount) { } @@ -78,12 +76,14 @@ public: const double getHopr () const { return this->hopr; } const double getLopr () const { return this->lopr; } const excasIoType getIOType () const { return this->ioType; } + const unsigned getElementCount() const { return this->elementCount; } private: const double scanRate; const aitString name; const double hopr; const double lopr; const excasIoType ioType; + const unsigned elementCount; }; class exPV; @@ -113,8 +113,6 @@ public: exPV (const casCtx &ctxIn, const pvInfo &setup); virtual ~exPV(); - void scanPV(); - void show(unsigned level); // @@ -143,8 +141,16 @@ public: // //casChannel *createChannel (); + // + // This gets called when the pv gets a new value + // caStatus update (gdd &value); + // + // Gets called when we add noise to the current value + // + virtual void scan() = 0; + // // Std PV Attribute fetch support // @@ -168,13 +174,47 @@ public: { return this->info.getScanRate(); } + + caStatus read (const casCtx &, gdd &protoIn); + + caStatus write (const casCtx &, gdd &protoIn); + protected: gdd *pValue; exScanTimer *pScanTimer; const pvInfo & info; aitBool interest; -private: static osiTime currentTime; + + virtual caStatus updateValue (gdd &value) = 0; +}; + +// +// exScalerPV +// +class exScalarPV : public exPV { +public: + exScalarPV (const casCtx &ctxIn, const pvInfo &setup) : + exPV (ctxIn, setup) {} + void scan(); +private: + caStatus updateValue (gdd &value); +}; + +// +// exVectorPV +// +class exVectorPV : public exPV { +public: + exVectorPV (const casCtx &ctxIn, const pvInfo &setup) : + exPV (ctxIn, setup) {} + void scan(); + + unsigned maxDimension() const; + aitIndex maxBound (unsigned dimension) const; + +private: + caStatus updateValue (gdd &value); }; // @@ -199,37 +239,16 @@ private: static gddAppFuncTable ft; }; -// -// exSyncPV -// -class exSyncPV : public exPV { -public: - exSyncPV (const casCtx &ctxIn, const pvInfo &setup); - ~exSyncPV(); - - // - // read - // - caStatus read(const casCtx &ctxIn, gdd &value); - - // - // write - // - caStatus write(const casCtx &ctxIn, gdd &value); -private: -}; - // // exAsyncPV -// (asychronous PV) // -class exAsyncPV : public exPV { +class exAsyncPV : public exScalarPV { public: // // exAsyncPV() // - exAsyncPV (const casCtx &ctxIn, const pvInfo &setup) : - exPV (ctxIn, setup) {} + exAsyncPV (const casCtx &ctxIn, const pvInfo &setup) : + exScalarPV (ctxIn, setup) {} // // read diff --git a/src/cas/example/simple/exSyncPV.cc b/src/cas/example/simple/exSyncPV.cc deleted file mode 100644 index b974aa724..000000000 --- a/src/cas/example/simple/exSyncPV.cc +++ /dev/null @@ -1,38 +0,0 @@ - -// -// Example EPICS CA server -// - -#include - -// -// exSyncPV::exSyncPV() -// -exSyncPV::exSyncPV (const casCtx &ctxIn, const pvInfo &setup) : - exPV (ctxIn, setup) -{ -} - -// -// exSyncPV::~exSyncPV() -// -exSyncPV::~exSyncPV() -{ -} - -// -// exSyncPV::write() -// -caStatus exSyncPV::write (const casCtx &, gdd &valueIn) -{ - return this->update (valueIn); -} - -// -// exSyncPV::read() -// -caStatus exSyncPV::read (const casCtx &, gdd &protoIn) -{ - return exServer::read(*this, protoIn); -} - diff --git a/src/cas/example/simple/exVectorPV.cc b/src/cas/example/simple/exVectorPV.cc new file mode 100644 index 000000000..5d2510e8f --- /dev/null +++ b/src/cas/example/simple/exVectorPV.cc @@ -0,0 +1,237 @@ + +#include +#include + +#define myPI 3.14159265358979323846 + +// +// SUN C++ does not have RAND_MAX yet +// +#if !defined(RAND_MAX) +// +// Apparently SUN C++ is using the SYSV version of rand +// +#if 0 +#define RAND_MAX INT_MAX +#else +#define RAND_MAX SHRT_MAX +#endif +#endif + +// +// exVectorPV::maxDimension() +// +unsigned exVectorPV::maxDimension() const +{ + return 1u; +} + +// +// exVectorPV::maxBound() +// +aitIndex exVectorPV::maxBound (unsigned dimension) const +{ + if (dimension==0u) { + return this->info.getElementCount(); + } + else { + return 0u; + } +} + +// +// exVectorPV::scan +// +void exVectorPV::scan() +{ + caStatus status; + double radians; + gdd *pDD; + aitFloat32 *pF, *pFE, *pCF; + float newValue; + float limit; + + // + // update current time (so we are not required to do + // this every time that we write the PV which impacts + // throughput under sunos4 because gettimeofday() is + // slow) + // + this->currentTime = osiTime::getCurrent(); + + pDD = new gddAtomic (gddAppType_value, aitEnumFloat32, + 1u, this->info.getElementCount()); + if (pDD==NULL) { + return; + } + + // + // allocate array buffer + // + pF = new aitFloat32 [this->info.getElementCount()]; + if (!pF) { + pDD->unreference(); + } + + // + // double check for reasonable bounds on the + // current value + // + pCF=NULL; + if (this->pValue) { + if (this->pValue->dimension()==1u) { + const gddBounds *pB = this->pValue->getBounds(); + if (pB[0u].size()==this->info.getElementCount()) { + pCF = *pDD; + } + } + } + + pFE = &pF[this->info.getElementCount()]; + while (pFinfo.getHopr(); + newValue = min (newValue, limit); + limit = (float) this->info.getLopr(); + newValue = max (newValue, limit); + *(pF++) = newValue; + } + + // + // install the buffer into the DD + // + *pDD = pF; + + status = this->update (*pDD); + if (status) { + errMessage (status, "scan update failed\n"); + } + + pDD->unreference(); +} + +// +// exVectorPV::updateValue () +// +// NOTES: +// 1) This should have a test which verifies that the +// incoming value in all of its various data types can +// be translated into a real number? +// 2) We prefer to unreference the old PV value here and +// reference the incomming value because this will +// result in value change events each retaining an +// independent value on the event queue. With large arrays +// this may result in too much memory consumtion on +// the event queue. +// +caStatus exVectorPV::updateValue(gdd &valueIn) +{ + enum {replace, dontReplace} replFlag = dontReplace; + gddStatus gdds; + gdd *pNewValue; + + // + // Check bounds of incoming request + // (and see if we are replacing all elements - + // replaceOk==TRUE) + // + if (valueIn.isAtomic()) { + if (valueIn.dimension()!=1u) { + return S_casApp_badDimension; + } + const gddBounds* pb = valueIn.getBounds(); + if (pb[0u].first()!=0u) { + return S_casApp_outOfBounds; + } + if (pb[0u].size()==this->info.getElementCount()) { + replFlag = replace; + } + else if (pb[0u].size()>this->info.getElementCount()) { + return S_casApp_outOfBounds; + } + } + else if (!valueIn.isScalar()) { + // + // no containers + // + return S_casApp_outOfBounds; + } + + if (replFlag==replace) { + // + // It is most efficient to replace all elements + // + valueIn.reference(); + pNewValue = &valueIn; + } + else { + aitFloat32 *pF, *pFE; + + // + // Create a new array data descriptor + // (so that old values that may be referenced on the + // event queue are not replaced) + // + pNewValue = new gddAtomic (gddAppType_value, aitEnumFloat32, + 1u, this->info.getElementCount()); + if (pNewValue==NULL) { + return S_casApp_noMemory; + } + + // + // copy over the old values if they exist + // (or initialize all elements to zero) + // + if (this->pValue) { + gdds = pNewValue->copy(this->pValue); + if (gdds) { + pNewValue->unreference(); + return S_cas_noConvert; + } + } + else { + // + // allocate array buffer + // + pF = new aitFloat32 [this->info.getElementCount()]; + if (!pF) { + pNewValue->unreference(); + return S_casApp_noMemory; + } + + // + // Install (and initialize) array buffer + // if no old values exist + // + pFE = &pF[this->info.getElementCount()]; + while (pFput(&valueIn); + if (gdds) { + pNewValue->unreference(); + return S_cas_noConvert; + } + } + + if (this->pValue) { + this->pValue->unreference(); + } + this->pValue = pNewValue; + + return S_casApp_success; +} + diff --git a/src/cas/example/simple/main.cc b/src/cas/example/simple/main.cc index a973b036f..bd627254b 100644 --- a/src/cas/example/simple/main.cc +++ b/src/cas/example/simple/main.cc @@ -15,11 +15,6 @@ int main (int argc, const char **argv) aitBool forever = aitTrue; int i; - pCAS = new exServer(32u,5u,500u); - if (!pCAS) { - return (-1); - } - for (i=1; isetDebugLevel(debugLevel); if (forever) {