From 081cb42dc892565352de77d187ff279570d6e83b Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 5 Feb 2003 23:46:23 +0000 Subject: [PATCH] Moved CA client examples into their own caClientApp Added caServerApp - copied from src/cas/example/simple Made simpleApp significantly more useful Made exampleApp use the _APPNAME_ macro instead of example in various places --- src/makeBaseApp/Makefile | 27 +- src/makeBaseApp/top/caClientApp/Makefile | 24 + .../src => caClientApp}/caExample.c | 2 +- .../src => caClientApp}/caMonitor.c | 0 src/makeBaseApp/top/caServerApp/Makefile | 38 + src/makeBaseApp/top/caServerApp/README | 11 + src/makeBaseApp/top/caServerApp/exAsyncPV.cc | 140 +++ src/makeBaseApp/top/caServerApp/exChannel.cc | 41 + src/makeBaseApp/top/caServerApp/exPV.cc | 348 ++++++++ src/makeBaseApp/top/caServerApp/exScalarPV.cc | 116 +++ src/makeBaseApp/top/caServerApp/exServer.cc | 384 ++++++++ src/makeBaseApp/top/caServerApp/exServer.h | 543 +++++++++++ src/makeBaseApp/top/caServerApp/exVectorPV.cc | 274 ++++++ src/makeBaseApp/top/caServerApp/main.cc | 110 +++ src/makeBaseApp/top/caServerApp/test.adl | 844 ++++++++++++++++++ src/makeBaseApp/top/caServerApp/vxEntry.cc | 80 ++ src/makeBaseApp/top/exampleApp/src/Makefile | 78 +- ...xampleInclude.dbd => _APPNAME_Include.dbd} | 0 .../{exampleMain.cpp => _APPNAME_Main.cpp} | 0 .../top/exampleApp/src/dbSubExample.c | 6 +- .../top/exampleBoot/ioc/README@Common | 6 +- src/makeBaseApp/top/simpleApp/src/Makefile | 85 +- .../top/simpleApp/src/_APPNAME_Include.dbd | 1 + .../top/simpleApp/src/_APPNAME_Main.cpp | 21 + src/makeBaseApp/top/simpleApp/src/base.dbd | 50 ++ 25 files changed, 3122 insertions(+), 107 deletions(-) create mode 100644 src/makeBaseApp/top/caClientApp/Makefile rename src/makeBaseApp/top/{exampleApp/src => caClientApp}/caExample.c (95%) rename src/makeBaseApp/top/{exampleApp/src => caClientApp}/caMonitor.c (100%) create mode 100644 src/makeBaseApp/top/caServerApp/Makefile create mode 100644 src/makeBaseApp/top/caServerApp/README create mode 100644 src/makeBaseApp/top/caServerApp/exAsyncPV.cc create mode 100644 src/makeBaseApp/top/caServerApp/exChannel.cc create mode 100644 src/makeBaseApp/top/caServerApp/exPV.cc create mode 100644 src/makeBaseApp/top/caServerApp/exScalarPV.cc create mode 100644 src/makeBaseApp/top/caServerApp/exServer.cc create mode 100644 src/makeBaseApp/top/caServerApp/exServer.h create mode 100644 src/makeBaseApp/top/caServerApp/exVectorPV.cc create mode 100644 src/makeBaseApp/top/caServerApp/main.cc create mode 100644 src/makeBaseApp/top/caServerApp/test.adl create mode 100644 src/makeBaseApp/top/caServerApp/vxEntry.cc rename src/makeBaseApp/top/exampleApp/src/{exampleInclude.dbd => _APPNAME_Include.dbd} (100%) rename src/makeBaseApp/top/exampleApp/src/{exampleMain.cpp => _APPNAME_Main.cpp} (100%) create mode 100644 src/makeBaseApp/top/simpleApp/src/_APPNAME_Include.dbd create mode 100644 src/makeBaseApp/top/simpleApp/src/_APPNAME_Main.cpp create mode 100644 src/makeBaseApp/top/simpleApp/src/base.dbd diff --git a/src/makeBaseApp/Makefile b/src/makeBaseApp/Makefile index a067a3aef..350b31f96 100644 --- a/src/makeBaseApp/Makefile +++ b/src/makeBaseApp/Makefile @@ -24,12 +24,10 @@ TEMPLATES += top/exampleApp/src/Makefile TEMPLATES += top/exampleApp/src/base.dbd TEMPLATES += top/exampleApp/src/devXxxSoft.c TEMPLATES += top/exampleApp/src/xxxRecord.c -TEMPLATES += top/exampleApp/src/caExample.c -TEMPLATES += top/exampleApp/src/caMonitor.c -TEMPLATES += top/exampleApp/src/exampleInclude.dbd +TEMPLATES += top/exampleApp/src/_APPNAME_Include.dbd TEMPLATES += top/exampleApp/src/sncExample.stt TEMPLATES += top/exampleApp/src/xxxRecord.dbd -TEMPLATES += top/exampleApp/src/exampleMain.cpp +TEMPLATES += top/exampleApp/src/_APPNAME_Main.cpp TEMPLATES += top/exampleApp/src/dbSubExample.c TEMPLATES += top/exampleBoot/Makefile @@ -42,9 +40,30 @@ TEMPLATES += top/exampleBoot/ioc/st.cmd@RTEMS TEMPLATES += top/exampleBoot/ioc/README@Common TEMPLATES += top/exampleBoot/ioc/README@vxWorks +TEMPLATES += top/caClientApp/Makefile +TEMPLATES += top/caClientApp/caExample.c +TEMPLATES += top/caClientApp/caMonitor.c + +TEMPLATES += top/caServerApp/Makefile +TEMPLATES += top/caServerApp/README +TEMPLATES += top/caServerApp/exAsyncPV.cc +TEMPLATES += top/caServerApp/exChannel.cc +TEMPLATES += top/caServerApp/exPV.cc +TEMPLATES += top/caServerApp/exScalarPV.cc +TEMPLATES += top/caServerApp/exServer.cc +TEMPLATES += top/caServerApp/exServer.h +TEMPLATES += top/caServerApp/exVectorPV.cc +TEMPLATES += top/caServerApp/main.cc +TEMPLATES += top/caServerApp/test.adl +TEMPLATES += top/caServerApp/vxEntry.cc + TEMPLATES += top/simpleApp/Makefile TEMPLATES += top/simpleApp/Db/Makefile TEMPLATES += top/simpleApp/src/Makefile +TEMPLATES += top/simpleApp/src/Makefile +TEMPLATES += top/simpleApp/src/_APPNAME_Include.dbd +TEMPLATES += top/simpleApp/src/_APPNAME_Main.cpp +TEMPLATES += top/simpleApp/src/base.dbd TEMPLATES += top/simpleBoot/Makefile TEMPLATES += top/simpleBoot/nfsCommands@vxWorks diff --git a/src/makeBaseApp/top/caClientApp/Makefile b/src/makeBaseApp/top/caClientApp/Makefile new file mode 100644 index 000000000..a42bfb2ac --- /dev/null +++ b/src/makeBaseApp/top/caClientApp/Makefile @@ -0,0 +1,24 @@ +TOP=.. + +include $(TOP)/configure/CONFIG +#---------------------------------------- +# ADD MACRO DEFINITIONS AFTER THIS LINE +#============================= + +PROD_HOST += caExample +caExample_SRCS += caExample.c +caExample_LIBS += ca +caExample_LIBS += Com + +PROD_HOST += caMonitor +caMonitor_SRCS += caMonitor.c +caMonitor_LIBS += ca +caMonitor_LIBS += Com + +ca_DIR = $(EPICS_BASE_LIB) +Com_DIR = $(EPICS_BASE_LIB) + +include $(TOP)/configure/RULES +#---------------------------------------- +# ADD RULES AFTER THIS LINE + diff --git a/src/makeBaseApp/top/exampleApp/src/caExample.c b/src/makeBaseApp/top/caClientApp/caExample.c similarity index 95% rename from src/makeBaseApp/top/exampleApp/src/caExample.c rename to src/makeBaseApp/top/caClientApp/caExample.c index cc342e237..3df548452 100644 --- a/src/makeBaseApp/top/exampleApp/src/caExample.c +++ b/src/makeBaseApp/top/caClientApp/caExample.c @@ -6,7 +6,7 @@ #include "cadef.h" -int main(int argc,char **argv) +main(int argc,char **argv) { double data; chid mychid; diff --git a/src/makeBaseApp/top/exampleApp/src/caMonitor.c b/src/makeBaseApp/top/caClientApp/caMonitor.c similarity index 100% rename from src/makeBaseApp/top/exampleApp/src/caMonitor.c rename to src/makeBaseApp/top/caClientApp/caMonitor.c diff --git a/src/makeBaseApp/top/caServerApp/Makefile b/src/makeBaseApp/top/caServerApp/Makefile new file mode 100644 index 000000000..b30fb3f2b --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/Makefile @@ -0,0 +1,38 @@ +#************************************************************************* +# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE Versions 3.13.7 +# and higher are distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +TOP=.. + +include $(TOP)/configure/CONFIG + +PROD_LIBS := cas ca gdd Com +cas_DIR = $(INSTALL_LIB) +ca_DIR = $(INSTALL_LIB) +gdd_DIR = $(INSTALL_LIB) +Com_DIR = $(INSTALL_LIB) + +# +# Added ws2_32 winmm user32 for the non-dll build +# +SYS_PROD_LIBS_WIN32 := ws2_32 advapi32 user32 + +SRCS += main.cc +SRCS += exServer.cc +SRCS += exPV.cc +SRCS += exVectorPV.cc +SRCS += exScalarPV.cc +SRCS += exAsyncPV.cc +SRCS += exChannel.cc + +PROD_HOST = excas + +include $(TOP)/configure/RULES + + diff --git a/src/makeBaseApp/top/caServerApp/README b/src/makeBaseApp/top/caServerApp/README new file mode 100644 index 000000000..f7554331b --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/README @@ -0,0 +1,11 @@ +The files in this directory build an example ca server. The example +server exports 4 process variables (PVs): "fred", "freddy", "jane", and +"janet". "fred" and "jane" are synchronous PVs. "freddy" and "janet" +are asynchronous. Many ca servers will find that synchronous variables +will meet there needs and therefore will not require the increased +complexity associated with asynchronous PVs. The PVs in the example +server are updated periodically. Some random "noise" is added to each +PVs current value each time that it is updated. + +The example server does not so far implement enumerated data types. + diff --git a/src/makeBaseApp/top/caServerApp/exAsyncPV.cc b/src/makeBaseApp/top/caServerApp/exAsyncPV.cc new file mode 100644 index 000000000..96396b7bb --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/exAsyncPV.cc @@ -0,0 +1,140 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +// +// Example EPICS CA server +// (asynchrronous process variable) +// + +#include "exServer.h" + +// +// exAsyncPV::read() +// (virtual replacement for the default) +// +caStatus exAsyncPV::read (const casCtx &ctx, gdd &valueIn) +{ + exAsyncReadIO *pIO; + + if (this->simultAsychIOCount>=maxSimultAsyncIO) { + return S_casApp_postponeAsyncIO; + } + + this->simultAsychIOCount++; + + pIO = new exAsyncReadIO ( ctx, *this, valueIn ); + if (!pIO) { + return S_casApp_noMemory; + } + + return S_casApp_asyncCompletion; +} + +// +// exAsyncPV::write() +// (virtual replacement for the default) +// +caStatus exAsyncPV::write ( const casCtx &ctx, const gdd &valueIn ) +{ + exAsyncWriteIO *pIO; + + if ( this->simultAsychIOCount >= maxSimultAsyncIO ) { + return S_casApp_postponeAsyncIO; + } + + this->simultAsychIOCount++; + + pIO = new exAsyncWriteIO ( ctx, *this, valueIn ); + if ( ! pIO ) { + return S_casApp_noMemory; + } + + return S_casApp_asyncCompletion; +} + +// +// exAsyncWriteIO::exAsyncWriteIO() +// +exAsyncWriteIO::exAsyncWriteIO ( const casCtx &ctxIn, exAsyncPV &pvIn, + const gdd &valueIn ) : + casAsyncWriteIO ( ctxIn ), pv ( pvIn ), + timer ( pvIn.getCAS()->createTimer () ), pValue(valueIn) +{ + this->timer.start ( *this, 0.1 ); +} + +// +// exAsyncWriteIO::~exAsyncWriteIO() +// +exAsyncWriteIO::~exAsyncWriteIO() +{ + this->pv.removeIO(); + if ( this->pv.getCAS() ) { + this->timer.destroy (); + } +} + +// +// exAsyncWriteIO::expire() +// (a virtual function that runs when the base timer expires) +// +epicsTimerNotify::expireStatus exAsyncWriteIO::expire ( const epicsTime & /* currentTime */ ) +{ + caStatus status; + status = this->pv.update ( this->pValue ); + this->postIOCompletion ( status ); + return noRestart; +} + +// +// exAsyncReadIO::exAsyncReadIO() +// +exAsyncReadIO::exAsyncReadIO ( const casCtx &ctxIn, exAsyncPV &pvIn, + gdd &protoIn ) : + casAsyncReadIO ( ctxIn ), pv ( pvIn ), + timer ( pvIn.getCAS()->createTimer() ), pProto ( protoIn ) +{ + this->timer.start ( *this, 0.1 ); +} + +// +// exAsyncReadIO::~exAsyncReadIO() +// +exAsyncReadIO::~exAsyncReadIO() +{ + this->pv.removeIO (); + if ( this->pv.getCAS() ) { + this->timer.destroy (); + } +} + + +// +// exAsyncReadIO::expire() +// (a virtual function that runs when the base timer expires) +// +epicsTimerNotify::expireStatus exAsyncReadIO::expire ( const epicsTime & /* currentTime */ ) +{ + caStatus status; + + // + // map between the prototype in and the + // current value + // + status = this->pv.exPV::readNoCtx ( this->pProto ); + + // + // post IO completion + // + this->postIOCompletion ( status, *this->pProto ); + + return noRestart; +} + diff --git a/src/makeBaseApp/top/caServerApp/exChannel.cc b/src/makeBaseApp/top/caServerApp/exChannel.cc new file mode 100644 index 000000000..5f4e5b109 --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/exChannel.cc @@ -0,0 +1,41 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +// +// Example EPICS CA server +// + +#include "exServer.h" + +// +// exChannel::setOwner () +// +void exChannel::setOwner(const char * const /* pUserName */, + const char * const /* pHostName */) +{ +} + +// +// exChannel::readAccess () +// +bool exChannel::readAccess () const +{ + return true; +} + +// +// exChannel::writeAccess () +// +bool exChannel::writeAccess () const +{ + return true; +} + + diff --git a/src/makeBaseApp/top/caServerApp/exPV.cc b/src/makeBaseApp/top/caServerApp/exPV.cc new file mode 100644 index 000000000..944579f25 --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/exPV.cc @@ -0,0 +1,348 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +// +// Example EPICS CA server +// +#include "exServer.h" +#include "gddApps.h" + +// +// static data for exPV +// +char exPV::hasBeenInitialized = 0; +gddAppFuncTable exPV::ft; +epicsTime exPV::currentTime; + +// +// special gddDestructor guarantees same form of new and delete +// +class exFixedStringDestructor: public gddDestructor { + virtual void run (void *); +}; + +// +// exPV::exPV() +// +exPV::exPV ( pvInfo &setup, bool preCreateFlag, bool scanOnIn ) : + timer ( this->getCAS()->createTimer() ), + info ( setup ), + interest ( false ), + preCreate ( preCreateFlag ), + scanOn ( scanOnIn ) +{ + // + // no dataless PV allowed + // + assert (this->info.getElementCount()>=1u); + + // + // start a very slow background scan + // (we will speed this up to the normal rate when + // someone is watching the PV) + // + if ( this->scanOn && this->info.getScanPeriod () > 0.0 ) { + this->timer.start ( *this, this->getScanPeriod() ); + } +} + +// +// exPV::~exPV() +// +exPV::~exPV() +{ + this->timer.destroy (); + this->info.unlinkPV(); +} + +// +// exPV::destroy() +// +// this is replaced by a noop since we are +// pre-creating most of the PVs during init in this simple server +// +void exPV::destroy() +{ + if ( ! this->preCreate ) { + delete this; + } +} + +// +// exPV::update() +// +caStatus exPV::update(smartConstGDDPointer pValueIn) +{ + caServer *pCAS = this->getCAS(); + caStatus cas; + +# if DEBUG + printf("Setting %s too:\n", this->info.getName().string()); + valueIn.dump(); +# endif + + cas = this->updateValue (pValueIn); + if ( cas || ( ! this->pValue.valid() ) ) { + return cas; + } + + // + // post a value change event + // + if (this->interest==true && pCAS!=NULL) { + casEventMask select(pCAS->valueEventMask()|pCAS->logEventMask()); + this->postEvent (select, *this->pValue); + } + + return S_casApp_success; +} + +// +// exScanTimer::expire () +// +epicsTimerNotify::expireStatus +exPV::expire ( const epicsTime & /*currentTime*/ ) // X aCC 361 +{ + this->scan(); + if ( this->scanOn && this->getScanPeriod() > 0.0 ) { + return expireStatus ( restart, this->getScanPeriod() ); + } + else { + return noRestart; + } +} + +// +// exPV::bestExternalType() +// +aitEnum exPV::bestExternalType () const +{ + return this->info.getType (); +} + +// +// exPV::interestRegister() +// +caStatus exPV::interestRegister() +{ + caServer *pCAS = this->getCAS(); + + if ( ! pCAS ) { + return S_casApp_success; + } + + this->interest = true; + + if ( this->scanOn && this->getScanPeriod() > 0.0 && + this->getScanPeriod() < this->timer.getExpireDelay() ) { + this->timer.start ( *this, this->getScanPeriod() ); + } + + return S_casApp_success; +} + +// +// exPV::interestDelete() +// +void exPV::interestDelete() +{ + this->interest = false; +} + +// +// exPV::show() +// +void exPV::show ( unsigned level ) const +{ + if (level>1u) { + if ( this->pValue.valid () ) { + printf ( "exPV: cond=%d\n", this->pValue->getStat () ); + printf ( "exPV: sevr=%d\n", this->pValue->getSevr () ); + printf ( "exPV: value=%f\n", static_cast < double > ( * this->pValue ) ); + } + printf ( "exPV: interest=%d\n", this->interest ); + this->timer.show ( level - 1u ); + } +} + +// +// exPV::initFT() +// +void exPV::initFT() +{ + if ( exPV::hasBeenInitialized ) { + return; + } + + // + // time stamp, status, and severity are extracted from the + // GDD associated with the "value" application type. + // + exPV::ft.installReadFunc ("value", &exPV::getValue); + exPV::ft.installReadFunc ("precision", &exPV::getPrecision); + exPV::ft.installReadFunc ("graphicHigh", &exPV::getHighLimit); + exPV::ft.installReadFunc ("graphicLow", &exPV::getLowLimit); + exPV::ft.installReadFunc ("controlHigh", &exPV::getHighLimit); + exPV::ft.installReadFunc ("controlLow", &exPV::getLowLimit); + exPV::ft.installReadFunc ("alarmHigh", &exPV::getHighLimit); + exPV::ft.installReadFunc ("alarmLow", &exPV::getLowLimit); + exPV::ft.installReadFunc ("alarmHighWarning", &exPV::getHighLimit); + exPV::ft.installReadFunc ("alarmLowWarning", &exPV::getLowLimit); + exPV::ft.installReadFunc ("units", &exPV::getUnits); + exPV::ft.installReadFunc ("enums", &exPV::getEnums); + + exPV::hasBeenInitialized = 1; +} + +// +// exPV::getPrecision() +// +caStatus exPV::getPrecision(gdd &prec) +{ + prec.put(4u); + return S_cas_success; +} + +// +// exPV::getHighLimit() +// +caStatus exPV::getHighLimit(gdd &value) +{ + value.put(info.getHopr()); + return S_cas_success; +} + +// +// exPV::getLowLimit() +// +caStatus exPV::getLowLimit(gdd &value) +{ + value.put(info.getLopr()); + return S_cas_success; +} + +// +// exPV::getUnits() +// +caStatus exPV::getUnits(gdd &units) +{ + aitString str("furlongs", aitStrRefConstImortal); + units.put(str); + return S_cas_success; +} + +// +// exPV::getEnums() +// +// returns the eneumerated state strings +// for a discrete channel +// +// The PVs in this example are purely analog, +// and therefore this isnt appropriate in an +// analog context ... +// +caStatus exPV::getEnums ( gdd & enumsIn ) +{ + if ( this->info.getType () == aitEnumEnum16 ) { + static const unsigned nStr = 2; + aitFixedString *str; + exFixedStringDestructor *pDes; + + str = new aitFixedString[nStr]; + if (!str) { + return S_casApp_noMemory; + } + + pDes = new exFixedStringDestructor; + if (!pDes) { + delete [] str; + return S_casApp_noMemory; + } + + strncpy (str[0].fixed_string, "off", + sizeof(str[0].fixed_string)); + strncpy (str[1].fixed_string, "on", + sizeof(str[1].fixed_string)); + + enumsIn.setDimension(1); + enumsIn.setBound (0,0,nStr); + enumsIn.putRef (str, pDes); + + return S_cas_success; + } + + return S_cas_success; +} + +// +// exPV::getValue() +// +caStatus exPV::getValue(gdd &value) +{ + caStatus status; + + if ( this->pValue.valid () ) { + gddStatus gdds; + + gdds = gddApplicationTypeTable:: + app_table.smartCopy ( &value, & (*this->pValue) ); + if (gdds) { + status = S_cas_noConvert; + } + else { + status = S_cas_success; + } + } + else { + status = S_casApp_undefined; + } + return status; +} + +// +// exPV::write() +// (synchronous default) +// +caStatus exPV::write (const casCtx &, const gdd &valueIn) +{ + return this->update (valueIn); +} + +// +// exPV::read() +// (synchronous default) +// +caStatus exPV::read (const casCtx &, gdd &protoIn) +{ + return this->ft.read (*this, protoIn); +} + +// +// exPV::createChannel() +// +// for access control - optional +// +casChannel *exPV::createChannel (const casCtx &ctx, + const char * const /* pUserName */, + const char * const /* pHostName */) +{ + return new exChannel (ctx); +} + +// +// exFixedStringDestructor::run() +// +// special gddDestructor guarantees same form of new and delete +// +void exFixedStringDestructor::run (void *pUntyped) +{ + aitFixedString *ps = (aitFixedString *) pUntyped; + delete [] ps; +} + diff --git a/src/makeBaseApp/top/caServerApp/exScalarPV.cc b/src/makeBaseApp/top/caServerApp/exScalarPV.cc new file mode 100644 index 000000000..1b3731435 --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/exScalarPV.cc @@ -0,0 +1,116 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include +#include + +#include "exServer.h" +#include "gddApps.h" + +#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; + smartGDDPointer pDD; + float newValue; + float limit; + int gddStatus; + + // + // 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 = epicsTime::getCurrent(); + + pDD = new gddScalar (gddAppType_value, aitEnumFloat64); + if ( ! pDD.valid () ) { + return; + } + + // + // smart pointer class manages reference count after this point + // + gddStatus = pDD->unreference(); + assert (!gddStatus); + + radians = (rand () * 2.0 * myPI)/RAND_MAX; + if ( this->pValue.valid () ) { + this->pValue->getConvert(newValue); + } + else { + newValue = 0.0f; + } + newValue += (float) (sin (radians) / 10.0); + limit = (float) this->info.getHopr(); + newValue = tsMin (newValue, limit); + limit = (float) this->info.getLopr(); + newValue = tsMax (newValue, limit); + *pDD = newValue; + aitTimeStamp gddts = this->currentTime; + pDD->setTimeStamp (&gddts); + status = this->update (pDD); + if (status!=S_casApp_success) { + errMessage (status, "scalar scan update failed\n"); + } +} + +// +// 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 each value change events retaining an +// independent value on the event queue. +// +caStatus exScalarPV::updateValue (smartConstGDDPointer pValueIn) +{ + if ( ! pValueIn.valid () ) { + return S_casApp_undefined; + } + + // + // Really no need to perform this check since the + // server lib verifies that all requests are in range + // + if (!pValueIn->isScalar()) { + return S_casApp_outOfBounds; + } + + this->pValue = pValueIn; + + return S_casApp_success; +} + diff --git a/src/makeBaseApp/top/caServerApp/exServer.cc b/src/makeBaseApp/top/caServerApp/exServer.cc new file mode 100644 index 000000000..8607bf7dd --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/exServer.cc @@ -0,0 +1,384 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +// +// fileDescriptorManager.process(delay); +// (the name of the global symbol has leaked in here) +// + +// +// Example EPICS CA server +// +#include "exServer.h" + +// +// static list of pre-created PVs +// +pvInfo exServer::pvList[] = { + pvInfo (1.0e-1, "jane", 10.0f, 0.0f, aitEnumFloat64, excasIoSync, 1u), + pvInfo (2.0, "fred", 10.0f, -10.0f, aitEnumFloat64, excasIoSync, 1u), + pvInfo (1.0e-1, "janet", 10.0f, 0.0f, aitEnumFloat64, excasIoAsync, 1u), + pvInfo (2.0, "freddy", 10.0f, -10.0f, aitEnumFloat64, excasIoAsync, 1u), + pvInfo (2.0, "alan", 10.0f, -10.0f, aitEnumFloat64, excasIoSync, 100u), + pvInfo (20.0, "albert", 10.0f, -10.0f, aitEnumFloat64, excasIoSync, 1000u), + pvInfo (-1.0, "boot", 10.0f, -10.0f, aitEnumEnum16, excasIoSync, 1u), + pvInfo (-1.0, "booty", 10.0f, -10.0f, aitEnumEnum16, excasIoAsync, 1u), + pvInfo (-1.0, "bill", 10.0f, -10.0f, aitEnumFloat64, excasIoSync, 1u) +}; + +const unsigned exServer::pvListNElem = NELEMENTS (exServer::pvList); + +// +// static on-the-fly PVs +// +pvInfo exServer::billy (-1.0, "billy", 10.0f, -10.0f, aitEnumFloat64, excasIoAsync, 1u); +pvInfo exServer::bloaty (-1.0, "bloaty", 10.0f, -10.0f, aitEnumFloat64, excasIoSync, 100000u); + +// +// exServer::exServer() +// +exServer::exServer ( const char * const pvPrefix, + unsigned aliasCount, bool scanOnIn ) : + simultAsychIOCount (0u), + scanOn (scanOnIn) +{ + unsigned i; + exPV *pPV; + pvInfo *pPVI; + pvInfo *pPVAfter = &exServer::pvList[pvListNElem]; + char pvAlias[256]; + const char * const pNameFmtStr = "%.100s%.20s"; + const char * const pAliasFmtStr = "%.100s%.20s%u"; + + exPV::initFT(); + + // + // pre-create all of the simple PVs that this server will export + // + for (pPVI = exServer::pvList; pPVI < pPVAfter; pPVI++) { + pPV = pPVI->createPV (*this, true, scanOnIn); + if (!pPV) { + fprintf(stderr, "Unable to create new PV \"%s\"\n", + pPVI->getName()); + } + + + // + // Install canonical (root) name + // + sprintf(pvAlias, pNameFmtStr, pvPrefix, pPVI->getName()); + this->installAliasName(*pPVI, pvAlias); + + // + // Install numbered alias names + // + for (i=0u; igetName(), i); + this->installAliasName(*pPVI, pvAlias); + } + } + + // + // Install create on-the-fly PVs + // into the PV name hash table + // + sprintf ( pvAlias, pNameFmtStr, pvPrefix, billy.getName() ); + this->installAliasName ( billy, pvAlias ); + sprintf ( pvAlias, pNameFmtStr, pvPrefix, bloaty.getName() ); + this->installAliasName ( bloaty, pvAlias ); +} + +// +// exServer::~exServer() +// +exServer::~exServer() +{ + pvInfo *pPVI; + pvInfo *pPVAfter = + &exServer::pvList[NELEMENTS(exServer::pvList)]; + + // + // delete all pre-created PVs (eliminate bounds-checker warnings) + // + for (pPVI = exServer::pvList; pPVI < pPVAfter; pPVI++) { + pPVI->deletePV (); + } + + this->stringResTbl.traverse ( &pvEntry::destroy ); +} + +// +// exServer::installAliasName() +// +void exServer::installAliasName(pvInfo &info, const char *pAliasName) +{ + pvEntry *pEntry; + + pEntry = new pvEntry(info, *this, pAliasName); + if (pEntry) { + int resLibStatus; + resLibStatus = this->stringResTbl.add(*pEntry); + if (resLibStatus==0) { + return; + } + else { + delete pEntry; + } + } + fprintf ( stderr, +"Unable to enter PV=\"%s\" Alias=\"%s\" in PV name alias hash table\n", + info.getName(), pAliasName ); +} + +// +// exServer::pvExistTest() +// +pvExistReturn exServer::pvExistTest // X aCC 361 + ( const casCtx& ctxIn, const char * pPVName ) +{ + // + // lifetime of id is shorter than lifetime of pName + // + stringId id ( pPVName, stringId::refString ); + pvEntry *pPVE; + + // + // Look in hash table for PV name (or PV alias name) + // + pPVE = this->stringResTbl.lookup ( id ); + if ( ! pPVE ) { + return pverDoesNotExistHere; + } + + pvInfo & pvi = pPVE->getInfo(); + + // + // Initiate async IO if this is an async PV + // + if ( pvi.getIOType() == excasIoSync ) { + return pverExistsHere; + } + else { + if ( this->simultAsychIOCount >= maxSimultAsyncIO ) { + return pverDoesNotExistHere; + } + + this->simultAsychIOCount++; + + exAsyncExistIO *pIO; + pIO = new exAsyncExistIO ( pvi, ctxIn, *this ); + if (pIO) { + return pverAsyncCompletion; + } + else { + return pverDoesNotExistHere; + } + } +} + +// +// exServer::pvAttach() +// +pvAttachReturn exServer::pvAttach // X aCC 361 + (const casCtx &ctx, const char *pName) +{ + // + // lifetime of id is shorter than lifetime of pName + // + stringId id(pName, stringId::refString); + exPV *pPV; + pvEntry *pPVE; + + pPVE = this->stringResTbl.lookup(id); + if (!pPVE) { + return S_casApp_pvNotFound; + } + + pvInfo &pvi = pPVE->getInfo(); + + // + // If this is a synchronous PV create the PV now + // + if (pvi.getIOType() == excasIoSync) { + pPV = pvi.createPV(*this, false, this->scanOn); + if (pPV) { + return *pPV; + } + else { + return S_casApp_noMemory; + } + } + // + // Initiate async IO if this is an async PV + // + else { + if (this->simultAsychIOCount>=maxSimultAsyncIO) { + return S_casApp_postponeAsyncIO; + } + + this->simultAsychIOCount++; + + exAsyncCreateIO *pIO = + new exAsyncCreateIO(pvi, *this, ctx, this->scanOn); + if (pIO) { + return S_casApp_asyncCompletion; + } + else { + return S_casApp_noMemory; + } + } +} + +// +// pvInfo::createPV() +// +exPV *pvInfo::createPV ( exServer & /*cas*/, + bool preCreateFlag, bool scanOn ) +{ + if (this->pPV) { + return this->pPV; + } + + exPV *pNewPV; + + // + // create an instance of the appropriate class + // depending on the io type and the number + // of elements + // + if (this->elementCount==1u) { + switch (this->ioType){ + case excasIoSync: + pNewPV = new exScalarPV ( *this, preCreateFlag, scanOn ); + break; + case excasIoAsync: + pNewPV = new exAsyncPV ( *this, preCreateFlag, scanOn ); + break; + default: + pNewPV = NULL; + break; + } + } + else { + if ( this->ioType == excasIoSync ) { + pNewPV = new exVectorPV ( *this, preCreateFlag, scanOn ); + } + else { + pNewPV = NULL; + } + } + + // + // load initial value (this is not done in + // the constructor because the base class's + // pure virtual function would be called) + // + // We always perform this step even if + // scanning is disable so that there will + // always be an initial value + // + if (pNewPV) { + this->pPV = pNewPV; + pNewPV->scan(); + } + + return pNewPV; +} + +// +// exServer::show() +// +void exServer::show (unsigned level) const +{ + // + // server tool specific show code goes here + // + this->stringResTbl.show(level); + + // + // print information about ca server libarary + // internals + // + this->caServer::show(level); +} + +// +// exAsyncExistIO::exAsyncExistIO() +// +exAsyncExistIO::exAsyncExistIO ( const pvInfo &pviIn, const casCtx &ctxIn, + exServer &casIn ) : + casAsyncPVExistIO ( ctxIn ), pvi ( pviIn ), + timer ( casIn.createTimer () ), cas ( casIn ) +{ + this->timer.start ( *this, 0.00001 ); +} + +// +// exAsyncExistIO::~exAsyncExistIO() +// +exAsyncExistIO::~exAsyncExistIO() +{ + this->cas.removeIO (); + this->timer.destroy (); +} + +// +// exAsyncExistIO::expire() +// (a virtual function that runs when the base timer expires) +// +epicsTimerNotify::expireStatus exAsyncExistIO::expire ( const epicsTime & /*currentTime*/ ) +{ + // + // post IO completion + // + this->postIOCompletion ( pvExistReturn(pverExistsHere) ); + return noRestart; +} + + +// +// exAsyncCreateIO::exAsyncCreateIO() +// +exAsyncCreateIO::exAsyncCreateIO ( pvInfo &pviIn, exServer &casIn, + const casCtx &ctxIn, bool scanOnIn ) : + casAsyncPVAttachIO ( ctxIn ), pvi ( pviIn ), + timer ( casIn.createTimer () ), + cas ( casIn ), scanOn ( scanOnIn ) +{ + this->timer.start ( *this, 0.00001 ); +} + +// +// exAsyncCreateIO::~exAsyncCreateIO() +// +exAsyncCreateIO::~exAsyncCreateIO() +{ + this->cas.removeIO (); + this->timer.destroy (); +} + +// +// exAsyncCreateIO::expire() +// (a virtual function that runs when the base timer expires) +// +epicsTimerNotify::expireStatus exAsyncCreateIO::expire ( const epicsTime & /*currentTime*/ ) +{ + exPV *pPV; + + pPV = this->pvi.createPV ( this->cas, false, this->scanOn ); + if ( pPV ) { + this->postIOCompletion ( pvAttachReturn ( *pPV ) ); + } + else { + this->postIOCompletion ( pvAttachReturn ( S_casApp_noMemory ) ); + } + return noRestart; +} + diff --git a/src/makeBaseApp/top/caServerApp/exServer.h b/src/makeBaseApp/top/caServerApp/exServer.h new file mode 100644 index 000000000..2235adfc3 --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/exServer.h @@ -0,0 +1,543 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +// +// Example EPICS CA server +// +// +// caServer +// | +// exServer +// +// casPV +// | +// exPV----------- +// | | +// exScalarPV exVectorPV +// | +// exAsyncPV +// +// casChannel +// | +// exChannel +// + + +// +// ANSI C +// +#include +#include + +// +// EPICS +// +#include "gddAppFuncTable.h" +#include "epicsTimer.h" +#include "casdef.h" +#include "epicsAssert.h" +#include "resourceLib.h" +#include "tsMinMax.h" + +#ifndef NELEMENTS +# define NELEMENTS(A) (sizeof(A)/sizeof(A[0])) +#endif + +#define maxSimultAsyncIO 1000u + +// +// info about all pv in this server +// +enum excasIoType { excasIoSync, excasIoAsync }; + +class exPV; +class exServer; + +// +// pvInfo +// +class pvInfo { +public: + pvInfo ( double scanPeriodIn, const char *pNameIn, + aitFloat32 hoprIn, aitFloat32 loprIn, + aitEnum typeIn, excasIoType ioTypeIn, + unsigned countIn ); + pvInfo ( const pvInfo & copyIn ); + ~pvInfo (); + double getScanPeriod () const; + const char *getName () const; + double getHopr () const; + double getLopr () const; + aitEnum getType () const; + excasIoType getIOType () const; + unsigned getElementCount () const; + void unlinkPV (); + exPV *createPV ( exServer & exCAS, + bool preCreateFlag, bool scanOn ); + void deletePV (); + +private: + const double scanPeriod; + const char * pName; + const double hopr; + const double lopr; + aitEnum type; + const excasIoType ioType; + const unsigned elementCount; + exPV * pPV; +}; + +// +// pvEntry +// +// o entry in the string hash table for the pvInfo +// o Since there may be aliases then we may end up +// with several of this class all referencing +// the same pv info class (justification +// for this breaking out into a seperate class +// from pvInfo) +// +class pvEntry // X aCC 655 + : public stringId, public tsSLNode < pvEntry > { +public: + pvEntry ( pvInfo &infoIn, exServer & casIn, + const char * pAliasName ); + ~pvEntry(); + pvInfo & getInfo() const { return this->info; } + void destroy (); + +private: + pvInfo & info; + exServer & cas; +}; + + +// +// exPV +// +class exPV : public casPV, public epicsTimerNotify, + public tsSLNode < exPV > { +public: + exPV ( pvInfo & setup, bool preCreateFlag, bool scanOn ); + virtual ~exPV(); + + void show ( unsigned level ) const; + + // + // Called by the server libary each time that it wishes to + // subscribe for PV the server tool via postEvent() below. + // + caStatus interestRegister (); + + // + // called by the server library each time that it wishes to + // remove its subscription for PV value change events + // from the server tool via caServerPostEvents() + // + void interestDelete (); + + aitEnum bestExternalType () const; + + // + // chCreate() is called each time that a PV is attached to + // by a client. The server tool must create a casChannel object + // (or a derived class) each time that this routine is called + // + // If the operation must complete asynchronously then return + // the status code S_casApp_asyncCompletion and then + // create the casChannel object at some time in the future + // + //casChannel *createChannel (); + + // + // This gets called when the pv gets a new value + // + caStatus update ( smartConstGDDPointer pValue ); + + // + // Gets called when we add noise to the current value + // + virtual void scan () = 0; + + // + // If no one is watching scan the PV with 10.0 + // times the specified period + // + double getScanPeriod (); + + caStatus read ( const casCtx &, gdd & protoIn ); + + caStatus readNoCtx ( smartGDDPointer pProtoIn ); + + caStatus write ( const casCtx &, const gdd & value ); + + void destroy (); + + const pvInfo & getPVInfo (); + + const char * getName() const; + + static void initFT(); + + casChannel * createChannel ( const casCtx &ctx, + const char * const pUserName, + const char * const pHostName ); + +protected: + smartConstGDDPointer pValue; + epicsTimer & timer; + pvInfo & info; + bool interest; + bool preCreate; + bool scanOn; + static epicsTime currentTime; + + virtual caStatus updateValue ( smartConstGDDPointer pValue ) = 0; + +private: + + // + // scan timer expire + // + expireStatus expire ( const epicsTime & currentTime ); + + // + // Std PV Attribute fetch support + // + gddAppFuncTableStatus getPrecision(gdd &value); + gddAppFuncTableStatus getHighLimit(gdd &value); + gddAppFuncTableStatus getLowLimit(gdd &value); + gddAppFuncTableStatus getUnits(gdd &value); + gddAppFuncTableStatus getValue(gdd &value); + gddAppFuncTableStatus getEnums(gdd &value); + + // + // static + // + static gddAppFuncTable ft; + static char hasBeenInitialized; +}; + +// +// exScalarPV +// +class exScalarPV : public exPV { +public: + exScalarPV ( pvInfo &setup, bool preCreateFlag, bool scanOnIn ) : + exPV ( setup, preCreateFlag, scanOnIn) {} + void scan(); +private: + caStatus updateValue (smartConstGDDPointer pValue); +}; + +// +// exVectorPV +// +class exVectorPV : public exPV { +public: + exVectorPV ( pvInfo &setup, bool preCreateFlag, bool scanOnIn ) : + exPV ( setup, preCreateFlag, scanOnIn) {} + void scan(); + + unsigned maxDimension() const; + aitIndex maxBound (unsigned dimension) const; + +private: + caStatus updateValue (smartConstGDDPointer pValue); +}; + +// +// exServer +// +class exServer : public caServer { +public: + exServer ( const char * const pvPrefix, + unsigned aliasCount, bool scanOn ); + ~exServer (); + void show ( unsigned level ) const; + pvExistReturn pvExistTest ( const casCtx &, const char * pPVName ); + pvAttachReturn pvAttach ( const casCtx &, const char * pPVName ); + void installAliasName ( pvInfo & info, const char * pAliasName ); + inline void removeAliasName ( pvEntry & entry ); + void removeIO(); +private: + resTable < pvEntry, stringId > stringResTbl; + unsigned simultAsychIOCount; + bool scanOn; + + // + // list of pre-created PVs + // + static pvInfo pvList[]; + static const unsigned pvListNElem; + + // + // on-the-fly PVs + // + static pvInfo bill; + static pvInfo billy; + static pvInfo bloaty; + static pvInfo boot; + static pvInfo booty; +}; + +// +// exAsyncPV +// +class exAsyncPV : public exScalarPV { +public: + exAsyncPV ( pvInfo &setup, bool preCreateFlag, bool scanOnIn ); + caStatus read ( const casCtx & ctxIn, gdd & protoIn ); + caStatus write ( const casCtx & ctxIn, const gdd & value ); + void removeIO(); +private: + unsigned simultAsychIOCount; +}; + +// +// exChannel +// +class exChannel : public casChannel{ +public: + exChannel ( const casCtx & ctxIn ); + void setOwner ( const char * const pUserName, + const char * const pHostName ); + bool readAccess () const; + bool writeAccess () const; +private: +}; + +// +// exAsyncWriteIO +// +class exAsyncWriteIO : public casAsyncWriteIO, public epicsTimerNotify { +public: + exAsyncWriteIO ( const casCtx & ctxIn, exAsyncPV & pvIn, const gdd & valueIn ); + ~exAsyncWriteIO (); +private: + exAsyncPV & pv; + epicsTimer & timer; + smartConstGDDPointer pValue; + expireStatus expire ( const epicsTime & currentTime ); +}; + +// +// exAsyncReadIO +// +class exAsyncReadIO : public casAsyncReadIO, public epicsTimerNotify { +public: + exAsyncReadIO ( const casCtx & ctxIn, exAsyncPV & pvIn, gdd & protoIn ); + virtual ~exAsyncReadIO (); +private: + exAsyncPV & pv; + epicsTimer & timer; + smartGDDPointer pProto; + expireStatus expire ( const epicsTime & currentTime ); +}; + +// +// exAsyncExistIO +// (PV exist async IO) +// +class exAsyncExistIO : public casAsyncPVExistIO, public epicsTimerNotify { +public: + exAsyncExistIO ( const pvInfo & pviIn, const casCtx & ctxIn, + exServer & casIn ); + virtual ~exAsyncExistIO (); +private: + const pvInfo & pvi; + epicsTimer & timer; + exServer & cas; + expireStatus expire ( const epicsTime & currentTime ); +}; + + +// +// exAsyncCreateIO +// (PV create async IO) +// +class exAsyncCreateIO : public casAsyncPVAttachIO, public epicsTimerNotify { +public: + exAsyncCreateIO ( pvInfo & pviIn, exServer & casIn, + const casCtx & ctxIn, bool scanOnIn ); + virtual ~exAsyncCreateIO (); +private: + pvInfo & pvi; + epicsTimer & timer; + exServer & cas; + bool scanOn; + expireStatus expire ( const epicsTime & currentTime ); +}; + +inline pvInfo::pvInfo ( double scanPeriodIn, const char *pNameIn, + aitFloat32 hoprIn, aitFloat32 loprIn, + aitEnum typeIn, excasIoType ioTypeIn, + unsigned countIn ) : + + scanPeriod ( scanPeriodIn ), pName ( pNameIn ), + hopr ( hoprIn ), lopr ( loprIn ), type ( typeIn ), + ioType ( ioTypeIn ), elementCount ( countIn ), + pPV ( 0 ) +{ +} + +// +// for use when MSVC++ will not build a default copy constructor +// for this class +// +inline pvInfo::pvInfo ( const pvInfo & copyIn ) : + + scanPeriod ( copyIn.scanPeriod ), pName ( copyIn.pName ), + hopr ( copyIn.hopr ), lopr ( copyIn.lopr ), type ( copyIn.type ), + ioType ( copyIn.ioType ), elementCount ( copyIn.elementCount ), + pPV ( copyIn.pPV ) +{ +} + +inline pvInfo::~pvInfo () +{ + // + // GDD cleanup gets rid of GDD's that are in use + // by the PV before the file scope destructer for + // this class runs here so this does not seem to + // be a good idea + // + //if ( this->pPV != NULL ) { + // delete this->pPV; + //} +} + +inline void pvInfo::deletePV () +{ + if ( this->pPV != NULL ) { + delete this->pPV; + } +} + +inline double pvInfo::getScanPeriod () const +{ + return this->scanPeriod; +} + +inline const char *pvInfo::getName () const +{ + return this->pName; +} + +inline double pvInfo::getHopr () const +{ + return this->hopr; +} + +inline double pvInfo::getLopr () const +{ + return this->lopr; +} + +inline aitEnum pvInfo::getType () const +{ + return this->type; +} + +inline excasIoType pvInfo::getIOType () const +{ + return this->ioType; +} + +inline unsigned pvInfo::getElementCount () const +{ + return this->elementCount; +} + +inline void pvInfo::unlinkPV () +{ + this->pPV = NULL; +} + +inline pvEntry::pvEntry ( pvInfo & infoIn, exServer & casIn, + const char * pAliasName ) : + stringId ( pAliasName ), info ( infoIn ), cas ( casIn ) +{ + assert ( this->stringId::resourceName() != NULL ); +} + +inline pvEntry::~pvEntry () +{ + this->cas.removeAliasName ( *this ); +} + +inline void pvEntry::destroy () +{ + delete this; +} + +inline void exServer::removeAliasName ( pvEntry & entry ) +{ + pvEntry * pE; + pE = this->stringResTbl.remove ( entry ); + assert ( pE == &entry ); +} + +inline double exPV::getScanPeriod () +{ + double curPeriod = this->info.getScanPeriod (); + if ( ! this->interest ) { + curPeriod *= 10.0L; + } + return curPeriod; +} + +inline caStatus exPV::readNoCtx ( smartGDDPointer pProtoIn ) +{ + return this->ft.read ( *this, *pProtoIn ); +} + +inline const pvInfo & exPV::getPVInfo () +{ + return this->info; +} + +inline const char * exPV::getName () const +{ + return this->info.getName(); +} + +inline void exServer::removeIO() +{ + if ( this->simultAsychIOCount > 0u ) { + this->simultAsychIOCount--; + } + else { + fprintf ( stderr, + "simultAsychIOCount underflow?\n" ); + } +} + +inline exAsyncPV::exAsyncPV ( pvInfo & setup, bool preCreateFlag, + bool scanOnIn ) : + exScalarPV ( setup, preCreateFlag, scanOnIn ), + simultAsychIOCount ( 0u ) +{ +} + +inline void exAsyncPV::removeIO () +{ + if ( this->simultAsychIOCount > 0u ) { + this->simultAsychIOCount--; + } + else { + fprintf ( stderr, "inconsistent simultAsychIOCount?\n" ); + } +} + +inline exChannel::exChannel ( const casCtx & ctxIn ) : + casChannel(ctxIn) +{ +} + diff --git a/src/makeBaseApp/top/caServerApp/exVectorPV.cc b/src/makeBaseApp/top/caServerApp/exVectorPV.cc new file mode 100644 index 000000000..829f8a258 --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/exVectorPV.cc @@ -0,0 +1,274 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "exServer.h" +#include "gddApps.h" + +#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 + +// +// special gddDestructor guarantees same form of new and delete +// +class exVecDestructor: public gddDestructor { + virtual void run (void *); +}; + +// +// exVectorPV::maxDimension() +// +unsigned exVectorPV::maxDimension() const +{ + return 1u; +} + +// +// exVectorPV::maxBound() +// +aitIndex exVectorPV::maxBound (unsigned dimension) const // X aCC 361 +{ + if (dimension==0u) { + return this->info.getElementCount(); + } + else { + return 0u; + } +} + +// +// exVectorPV::scan +// +void exVectorPV::scan() +{ + caStatus status; + double radians; + smartGDDPointer pDD; + aitFloat32 *pF, *pFE; + const aitFloat32 *pCF; + float newValue; + float limit; + exVecDestructor *pDest; + int gddStatus; + + // + // 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 = epicsTime::getCurrent(); + + pDD = new gddAtomic (gddAppType_value, aitEnumFloat64, + 1u, this->info.getElementCount()); + if ( ! pDD.valid () ) { + return; + } + + // + // smart pointer class manages reference count after this point + // + gddStatus = pDD->unreference(); + assert (!gddStatus); + + // + // allocate array buffer + // + pF = new aitFloat32 [this->info.getElementCount()]; + if (!pF) { + return; + } + + pDest = new exVecDestructor; + if (!pDest) { + delete [] pF; + return; + } + + // + // install the buffer into the DD + // (do this before we increment pF) + // + pDD->putRef(pF, pDest); + + // + // double check for reasonable bounds on the + // current value + // + pCF=NULL; + if ( this->pValue.valid () ) { + if (this->pValue->dimension()==1u) { + const gddBounds *pB = this->pValue->getBounds(); + if (pB[0u].size()==this->info.getElementCount()) { + pCF = *this->pValue; + } + } + } + + pFE = &pF[this->info.getElementCount()]; + while (pFinfo.getHopr(); + newValue = tsMin (newValue, limit); + limit = (float) this->info.getLopr(); + newValue = tsMax (newValue, limit); + *(pF++) = newValue; + } + + status = this->update (pDD); + if (status!=S_casApp_success) { + errMessage (status, "vector scan update failed\n"); + } +} + +// +// 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(smartConstGDDPointer pValueIn) +{ + gddStatus gdds; + smartGDDPointer pNewValue; + exVecDestructor *pDest; + unsigned i; + + if ( ! pValueIn.valid () ) { + return S_casApp_undefined; + } + + // + // Check bounds of incoming request + // (and see if we are replacing all elements - + // replaceOk==true) + // + // Perhaps much of this is unnecessary since the + // server lib checks the bounds of all requests + // + if (pValueIn->isAtomic()) { + if (pValueIn->dimension()!=1u) { + return S_casApp_badDimension; + } + const gddBounds* pb = pValueIn->getBounds(); + if (pb[0u].first()!=0u) { + return S_casApp_outOfBounds; + } + else if (pb[0u].size()>this->info.getElementCount()) { + return S_casApp_outOfBounds; + } + } + else if (!pValueIn->isScalar()) { + // + // no containers + // + return S_casApp_outOfBounds; + } + + aitFloat64 *pF; + int gddStatus; + + // + // 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, aitEnumFloat64, + 1u, this->info.getElementCount()); + if ( ! pNewValue.valid () ) { + return S_casApp_noMemory; + } + + // + // smart pointer class takes care of the reference count + // from here down + // + gddStatus = pNewValue->unreference(); + assert (!gddStatus); + + // + // allocate array buffer + // + pF = new aitFloat64 [this->info.getElementCount()]; + if (!pF) { + return S_casApp_noMemory; + } + + // + // Install (and initialize) array buffer + // if no old values exist + // + unsigned count = this->info.getElementCount(); + for ( i = 0u; i < count; i++ ) { + pF[i] = 0.0f; + } + + pDest = new exVecDestructor; + if (!pDest) { + delete [] pF; + return S_casApp_noMemory; + } + + // + // install the buffer into the DD + // (do this before we increment pF) + // + pNewValue->putRef(pF, pDest); + + // + // copy in the values that they are writing + // + gdds = pNewValue->put( & (*pValueIn) ); + if (gdds) { + return S_cas_noConvert; + } + + this->pValue = pNewValue; + + return S_casApp_success; +} + +// +// exVecDestructor::run() +// +// special gddDestructor guarantees same form of new and delete +// +void exVecDestructor::run (void *pUntyped) +{ + aitFloat32 *pf = (aitFloat32 *) pUntyped; + delete [] pf; +} diff --git a/src/makeBaseApp/top/caServerApp/main.cc b/src/makeBaseApp/top/caServerApp/main.cc new file mode 100644 index 000000000..538041ac1 --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/main.cc @@ -0,0 +1,110 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "envDefs.h" + +#include "exServer.h" +#include "fdManager.h" + +// +// main() +// (example single threaded ca server tool main loop) +// +extern int main (int argc, const char **argv) +{ + epicsTime begin (epicsTime::getCurrent()); + exServer *pCAS; + unsigned debugLevel = 0u; + double executionTime; + char pvPrefix[128] = ""; + unsigned aliasCount = 1u; + unsigned scanOnAsUnsignedInt = true; + char arraySize[64] = ""; + bool scanOn; + bool forever = true; + int i; + + for ( i = 1; i < argc; i++ ) { + if (sscanf(argv[i], "-d %u", &debugLevel)==1) { + continue; + } + if (sscanf(argv[i],"-t %lf", &executionTime)==1) { + forever = false; + continue; + } + if (sscanf(argv[i],"-p %127s", pvPrefix)==1) { + continue; + } + if (sscanf(argv[i],"-c %u", &aliasCount)==1) { + continue; + } + if (sscanf(argv[i],"-s %u", &scanOnAsUnsignedInt)==1) { + continue; + } + if (sscanf(argv[i],"-a %63s", arraySize)==1) { + continue; + } + printf ("\"%s\"?\n", argv[i]); + printf ( + "usage: %s [-d -t -p " + "-c -s<1=scan on (default), 0=scan off]> " + "-a]\n", + argv[0]); + + return (1); + } + + if ( arraySize[0] != '\0' ) { + epicsEnvSet ( "EPICS_CA_MAX_ARRAY_BYTES", arraySize ); + } + + if (scanOnAsUnsignedInt) { + scanOn = true; + } + else { + scanOn = false; + } + + try { + pCAS = new exServer ( pvPrefix, aliasCount, scanOn ); + } + catch ( ... ) { + errlogPrintf ( "Server initialization error\n" ); + errlogFlush (); + return (-1); + } + + pCAS->setDebugLevel(debugLevel); + + if ( forever ) { + // + // loop here forever + // + while (true) { + fileDescriptorManager.process(1000.0); + } + } + else { + double delay = epicsTime::getCurrent() - begin; + // + // loop here untill the specified execution time + // expires + // + while ( delay < executionTime ) { + fileDescriptorManager.process ( delay ); + delay = epicsTime::getCurrent() - begin; + } + } + pCAS->show(2u); + delete pCAS; + errlogFlush (); + return (0); +} + diff --git a/src/makeBaseApp/top/caServerApp/test.adl b/src/makeBaseApp/top/caServerApp/test.adl new file mode 100644 index 000000000..6086a3be5 --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/test.adl @@ -0,0 +1,844 @@ +file { + name="test.dl" +} +display { + magic="305419896" + majv="2" + mnrv="4" + ndyng="0" + npc="5" + nstr="8" + ndynamic="12" + nplot="0" + nrd="0" + nes="0" + nkd="0" + object { + x="0" + y="0" + width="421" + height="306" + } + clr="0" + bclr="1" + nwords_dspy="1106" + nwords_sta="28" + nwords_cmap="36" + nwords_crules="106" + odyng="306" + osta="278" + odynamic="306" + oplot="1106" + ord="1106" + oes="1106" + okd="1106" + opc="58" + ostr="88" + ocmap="136" + ocrules="172" + style="solid" + fill="outline" + width="0" + clrmod="static" + vismod="static" + clrrule="alarm" + pv="" + cmap="" +} +"<>" { + ncolors="8" + dl_color { + r="255" + g="255" + b="255" + inten="255" + blink="off" + RISCpad="128" + } + dl_color { + r="0" + g="0" + b="0" + inten="0" + blink="off" + RISCpad="75" + } + dl_color { + r="255" + g="0" + b="0" + inten="255" + blink="off" + RISCpad="-14684" + } + dl_color { + r="255" + g="0" + b="0" + inten="255" + blink="on" + RISCpad="14744" + } + dl_color { + r="255" + g="255" + b="0" + inten="255" + blink="off" + RISCpad="-16536" + } + dl_color { + r="255" + g="255" + b="0" + inten="255" + blink="on" + RISCpad="-15536" + } + dl_color { + r="0" + g="0" + b="255" + inten="255" + blink="off" + RISCpad="-28408" + } + dl_color { + r="0" + g="0" + b="255" + inten="255" + blink="on" + RISCpad="0" + } +} +"<>" { + nrules="1" + dl_color_rule { + name="alarm" + info[0] { + chan="$(C).SEVR" + value="MAJOR" + connector="use" + comparator="equals" + clr="2" + RISCpad="0" + } + info[1] { + chan="$(C).SEVR" + value="MINOR" + connector="use" + comparator="equals" + clr="4" + RISCpad="127" + } + info[2] { + chan="$(C).SEVR" + value="INFO" + connector="use" + comparator="equals" + clr="6" + RISCpad="44" + } + info[3] { + chan="" + value="" + connector="use" + comparator="equals" + clr="1" + RISCpad="-128" + } + info[4] { + chan="" + value="" + connector="use" + comparator="equals" + clr="1" + RISCpad="-1" + } + info[5] { + chan="" + value="" + connector="use" + comparator="equals" + clr="1" + RISCpad="-104" + } + info[6] { + chan="" + value="" + connector="use" + comparator="equals" + clr="1" + RISCpad="-1" + } + info[7] { + chan="" + value="" + connector="use" + comparator="equals" + clr="1" + RISCpad="8" + } + info[8] { + chan="" + value="" + connector="use" + comparator="equals" + clr="1" + RISCpad="120" + } + info[9] { + chan="" + value="" + connector="use" + comparator="equals" + clr="1" + RISCpad="1" + } + info[10] { + chan="" + value="" + connector="use" + comparator="equals" + clr="1" + RISCpad="7" + } + info[11] { + chan="" + value="" + connector="use" + comparator="equals" + clr="1" + RISCpad="19" + } + info[12] { + chan="" + value="" + connector="use" + comparator="equals" + clr="1" + RISCpad="48" + } + info[13] { + chan="" + value="" + connector="use" + comparator="equals" + clr="1" + RISCpad="28" + } + info[14] { + chan="" + value="" + connector="use" + comparator="equals" + clr="1" + RISCpad="-88" + } + info[15] { + chan="" + value="" + connector="use" + comparator="equals" + clr="1" + RISCpad="0" + } + fg_enable="on" + bg_enable="on" + default_fg="0" + default_bg="1" + } +} +"<>" { + attr { + clr="0" + style="solid" + fill="outline" + width="0" + } +} +"text" { + object { + x="44" + y="16" + width="104" + height="14" + groupid="0" + } + textix="Sync" + align="horiz. left" + RISC_pad="0" +} +"text" { + object { + x="260" + y="13" + width="92" + height="17" + groupid="0" + } + textix="Async" + align="horiz. left" + RISC_pad="0" +} +"indicator" { + object { + x="15" + y="88" + width="170" + height="22" + groupid="0" + } + monitor { + chan="fred" + clr="0" + bclr="1" + label="limits" + clrmod="static" + rulechan[0] = "" + rulechan[1] = "" + rulechan[2] = "" + rulechan[3] = "" + rulechan[4] = "" + rulechan[5] = "" + rulechan[6] = "" + rulechan[7] = "" + rulechan[8] = "" + rulechan[9] = "" + rulechan[10] = "" + rulechan[11] = "" + rulechan[12] = "" + rulechan[13] = "" + rulechan[14] = "" + rulechan[15] = "" + clrrule="alarm" + clrargs="" + rulecolorbg="0" + rulecolorfg="0" + hdl="0" + ldl="0" + prec="-1" + newunits="" + units="none" + decorate="none" + convertFunc="" + convertParams="" + } + direction="down" + RISC_pad="0" +} +"text update" { + object { + x="16" + y="133" + width="169" + height="17" + groupid="0" + } + monitor { + chan="fred" + clr="0" + bclr="1" + label="none" + clrmod="static" + rulechan[0] = "" + rulechan[1] = "" + rulechan[2] = "" + rulechan[3] = "" + rulechan[4] = "" + rulechan[5] = "" + rulechan[6] = "" + rulechan[7] = "" + rulechan[8] = "" + rulechan[9] = "" + rulechan[10] = "" + rulechan[11] = "" + rulechan[12] = "" + rulechan[13] = "" + rulechan[14] = "" + rulechan[15] = "" + clrrule="alarm" + clrargs="" + rulecolorbg="0" + rulecolorfg="0" + hdl="0" + ldl="0" + prec="-1" + newunits="" + units="append" + decorate="none" + convertFunc="" + convertParams="" + } + align="horiz. left" + format="decimal" +} +"valuator" { + object { + x="15" + y="43" + width="168" + height="26" + groupid="0" + } + control { + chan="fred" + clr="0" + bclr="1" + label="limits" + clrmod="static" + rulechan[0] = "" + rulechan[1] = "" + rulechan[2] = "" + rulechan[3] = "" + rulechan[4] = "" + rulechan[5] = "" + rulechan[6] = "" + rulechan[7] = "" + rulechan[8] = "" + rulechan[9] = "" + rulechan[10] = "" + rulechan[11] = "" + rulechan[12] = "" + rulechan[13] = "" + rulechan[14] = "" + rulechan[15] = "" + clrrule="alarm" + clrargs="" + rulecolorbg="0" + rulecolorfg="0" + hdl="0" + ldl="0" + prec="-1" + newunits="" + units="none" + decorate="none" + convertFunc="" + convertParams="" + } + direction="down" + gain="coarse" + sendMode="send on motion" + increment="0" +} +"indicator" { + object { + x="215" + y="81" + width="170" + height="30" + groupid="0" + } + monitor { + chan="freddy" + clr="0" + bclr="1" + label="limits" + clrmod="static" + rulechan[0] = "" + rulechan[1] = "" + rulechan[2] = "" + rulechan[3] = "" + rulechan[4] = "" + rulechan[5] = "" + rulechan[6] = "" + rulechan[7] = "" + rulechan[8] = "" + rulechan[9] = "" + rulechan[10] = "" + rulechan[11] = "" + rulechan[12] = "" + rulechan[13] = "" + rulechan[14] = "" + rulechan[15] = "" + clrrule="alarm" + clrargs="" + rulecolorbg="0" + rulecolorfg="0" + hdl="0" + ldl="0" + prec="-1" + newunits="" + units="none" + decorate="none" + convertFunc="" + convertParams="" + } + direction="down" + RISC_pad="0" +} +"text update" { + object { + x="216" + y="133" + width="171" + height="18" + groupid="0" + } + monitor { + chan="freddy" + clr="0" + bclr="1" + label="none" + clrmod="static" + rulechan[0] = "" + rulechan[1] = "" + rulechan[2] = "" + rulechan[3] = "" + rulechan[4] = "" + rulechan[5] = "" + rulechan[6] = "" + rulechan[7] = "" + rulechan[8] = "" + rulechan[9] = "" + rulechan[10] = "" + rulechan[11] = "" + rulechan[12] = "" + rulechan[13] = "" + rulechan[14] = "" + rulechan[15] = "" + clrrule="alarm" + clrargs="" + rulecolorbg="0" + rulecolorfg="0" + hdl="0" + ldl="0" + prec="-1" + newunits="" + units="append" + decorate="none" + convertFunc="" + convertParams="" + } + align="horiz. left" + format="decimal" +} +"valuator" { + object { + x="215" + y="43" + width="168" + height="28" + groupid="0" + } + control { + chan="freddy" + clr="0" + bclr="1" + label="limits" + clrmod="static" + rulechan[0] = "" + rulechan[1] = "" + rulechan[2] = "" + rulechan[3] = "" + rulechan[4] = "" + rulechan[5] = "" + rulechan[6] = "" + rulechan[7] = "" + rulechan[8] = "" + rulechan[9] = "" + rulechan[10] = "" + rulechan[11] = "" + rulechan[12] = "" + rulechan[13] = "" + rulechan[14] = "" + rulechan[15] = "" + clrrule="alarm" + clrargs="" + rulecolorbg="0" + rulecolorfg="0" + hdl="0" + ldl="0" + prec="-1" + newunits="" + units="none" + decorate="none" + convertFunc="" + convertParams="" + } + direction="down" + gain="coarse" + sendMode="send on motion" + increment="0" +} +"indicator" { + object { + x="16" + y="225" + width="171" + height="19" + groupid="0" + } + monitor { + chan="jane" + clr="0" + bclr="1" + label="limits" + clrmod="static" + rulechan[0] = "" + rulechan[1] = "" + rulechan[2] = "" + rulechan[3] = "" + rulechan[4] = "" + rulechan[5] = "" + rulechan[6] = "" + rulechan[7] = "" + rulechan[8] = "" + rulechan[9] = "" + rulechan[10] = "" + rulechan[11] = "" + rulechan[12] = "" + rulechan[13] = "" + rulechan[14] = "" + rulechan[15] = "" + clrrule="alarm" + clrargs="" + rulecolorbg="0" + rulecolorfg="0" + hdl="0" + ldl="0" + prec="-1" + newunits="" + units="none" + decorate="none" + convertFunc="" + convertParams="" + } + direction="down" + RISC_pad="0" +} +"text update" { + object { + x="17" + y="259" + width="170" + height="20" + groupid="0" + } + monitor { + chan="jane" + clr="0" + bclr="1" + label="none" + clrmod="static" + rulechan[0] = "" + rulechan[1] = "" + rulechan[2] = "" + rulechan[3] = "" + rulechan[4] = "" + rulechan[5] = "" + rulechan[6] = "" + rulechan[7] = "" + rulechan[8] = "" + rulechan[9] = "" + rulechan[10] = "" + rulechan[11] = "" + rulechan[12] = "" + rulechan[13] = "" + rulechan[14] = "" + rulechan[15] = "" + clrrule="alarm" + clrargs="" + rulecolorbg="0" + rulecolorfg="0" + hdl="0" + ldl="0" + prec="-1" + newunits="" + units="append" + decorate="none" + convertFunc="" + convertParams="" + } + align="horiz. left" + format="decimal" +} +"valuator" { + object { + x="15" + y="187" + width="170" + height="19" + groupid="0" + } + control { + chan="jane" + clr="0" + bclr="1" + label="limits" + clrmod="static" + rulechan[0] = "" + rulechan[1] = "" + rulechan[2] = "" + rulechan[3] = "" + rulechan[4] = "" + rulechan[5] = "" + rulechan[6] = "" + rulechan[7] = "" + rulechan[8] = "" + rulechan[9] = "" + rulechan[10] = "" + rulechan[11] = "" + rulechan[12] = "" + rulechan[13] = "" + rulechan[14] = "" + rulechan[15] = "" + clrrule="alarm" + clrargs="" + rulecolorbg="0" + rulecolorfg="0" + hdl="0" + ldl="0" + prec="-1" + newunits="" + units="none" + decorate="none" + convertFunc="" + convertParams="" + } + direction="down" + gain="coarse" + sendMode="send on motion" + increment="0" +} +"indicator" { + object { + x="219" + y="218" + width="173" + height="23" + groupid="0" + } + monitor { + chan="janet" + clr="0" + bclr="1" + label="limits" + clrmod="static" + rulechan[0] = "" + rulechan[1] = "" + rulechan[2] = "" + rulechan[3] = "" + rulechan[4] = "" + rulechan[5] = "" + rulechan[6] = "" + rulechan[7] = "" + rulechan[8] = "" + rulechan[9] = "" + rulechan[10] = "" + rulechan[11] = "" + rulechan[12] = "" + rulechan[13] = "" + rulechan[14] = "" + rulechan[15] = "" + clrrule="alarm" + clrargs="" + rulecolorbg="0" + rulecolorfg="0" + hdl="0" + ldl="0" + prec="-1" + newunits="" + units="none" + decorate="none" + convertFunc="" + convertParams="" + } + direction="down" + RISC_pad="0" +} +"text update" { + object { + x="220" + y="257" + width="174" + height="20" + groupid="0" + } + monitor { + chan="janet" + clr="0" + bclr="1" + label="none" + clrmod="static" + rulechan[0] = "" + rulechan[1] = "" + rulechan[2] = "" + rulechan[3] = "" + rulechan[4] = "" + rulechan[5] = "" + rulechan[6] = "" + rulechan[7] = "" + rulechan[8] = "" + rulechan[9] = "" + rulechan[10] = "" + rulechan[11] = "" + rulechan[12] = "" + rulechan[13] = "" + rulechan[14] = "" + rulechan[15] = "" + clrrule="alarm" + clrargs="" + rulecolorbg="0" + rulecolorfg="0" + hdl="0" + ldl="0" + prec="-1" + newunits="" + units="append" + decorate="none" + convertFunc="" + convertParams="" + } + align="horiz. left" + format="decimal" +} +"valuator" { + object { + x="219" + y="188" + width="171" + height="21" + groupid="0" + } + control { + chan="janet" + clr="0" + bclr="1" + label="limits" + clrmod="static" + rulechan[0] = "" + rulechan[1] = "" + rulechan[2] = "" + rulechan[3] = "" + rulechan[4] = "" + rulechan[5] = "" + rulechan[6] = "" + rulechan[7] = "" + rulechan[8] = "" + rulechan[9] = "" + rulechan[10] = "" + rulechan[11] = "" + rulechan[12] = "" + rulechan[13] = "" + rulechan[14] = "" + rulechan[15] = "" + clrrule="alarm" + clrargs="" + rulecolorbg="0" + rulecolorfg="0" + hdl="0" + ldl="0" + prec="-1" + newunits="" + units="none" + decorate="none" + convertFunc="" + convertParams="" + } + direction="down" + gain="coarse" + sendMode="send on motion" + increment="0" +} diff --git a/src/makeBaseApp/top/caServerApp/vxEntry.cc b/src/makeBaseApp/top/caServerApp/vxEntry.cc new file mode 100644 index 000000000..f1ac906ae --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/vxEntry.cc @@ -0,0 +1,80 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +// +// $Id$ +// Author: Jeff HIll (LANL) +// +// + +#include +#include + +#include "exServer.h" + +// +// so we can call this from the vxWorks shell +// +extern "C" { + +exServer *pExampleCAS; + +// +// excas () +// (vxWorks example server entry point) +// +int excas (unsigned debugLevel, unsigned delaySec) +{ + epicsTime begin(epicsTime::getCurrent()); + exServer *pCAS; + + pCAS = new exServer(32u,5u,500u); + if (!pCAS) { + return (-1); + } + + pCAS->setDebugLevel(debugLevel); + pExampleCAS = pCAS; + + if (delaySec==0u) { + // + // loop here forever + // + while (1) { + taskDelay(10); + } + } + else { + epicsTime total( ((float)delaySec) ); + epicsTime delay(epicsTime::getCurrent() - begin); + // + // loop here untill the specified execution time + // expires + // + while (delay < total) { + taskDelay(10); + delay = epicsTime::getCurrent() - begin; + } + } + pCAS->show(debugLevel); + pExampleCAS = NULL; + delete pCAS; + return 0; +} + +int excasShow(unsigned level) +{ + if (pExampleCAS!=NULL) { + pExampleCAS->show(level); + } + return 0; +} + +} // extern "C" + diff --git a/src/makeBaseApp/top/exampleApp/src/Makefile b/src/makeBaseApp/top/exampleApp/src/Makefile index 513605f15..dbd7143f4 100644 --- a/src/makeBaseApp/top/exampleApp/src/Makefile +++ b/src/makeBaseApp/top/exampleApp/src/Makefile @@ -5,55 +5,37 @@ include $(TOP)/configure/CONFIG # ADD MACRO DEFINITIONS AFTER THIS LINE #============================= -#USR_CFLAGS += - -PROD_HOST += caExample -caExample_SRCS += caExample.c -caExample_LIBS += ca -caExample_LIBS += Com - -PROD_HOST += caMonitor -caMonitor_SRCS += caMonitor.c -caMonitor_LIBS += ca -caMonitor_LIBS += Com - -ca_DIR = $(EPICS_BASE_LIB) -Com_DIR = $(EPICS_BASE_LIB) - - -#============================= - # xxxRecord.h will be created from xxxRecord.dbd DBDINC += xxxRecord # .dbd will be created from Include.dbd -DBD += example.dbd +DBD += _APPNAME_.dbd #============================= -PROD_IOC = example +PROD_IOC = _APPNAME_ -example_SRCS += xxxRecord.c -example_SRCS += devXxxSoft.c -example_SRCS += dbSubExample.c +_APPNAME__SRCS += xxxRecord.c +_APPNAME__SRCS += devXxxSoft.c +_APPNAME__SRCS += dbSubExample.c # _registerRecordDeviceDriver.cpp will be created from .dbd -example_SRCS += example_registerRecordDeviceDriver.cpp -example_SRCS_DEFAULT += exampleMain.cpp -example_SRCS_vxWorks += -nil- +_APPNAME__SRCS += _APPNAME__registerRecordDeviceDriver.cpp +_APPNAME__SRCS_DEFAULT += _APPNAME_Main.cpp +_APPNAME__SRCS_vxWorks += -nil- #The following adds support from base/src/vxWorks -example_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary +_APPNAME__OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary #NOTES: -# 1)It is not possible to build sncExample both as a component of example +# 1)It is not possible to build sncExample both as a component of _APPNAME_ # and standalone. You must choose only one. # 2)To build sncExample SNCSEQ must be defined in /configure/RELEASE -#The following builds sncExample as a component of example -#example_SRCS += sncExample.stt -#example_LIBS += seq -#example_LIBS += pv +#The following builds sncExample as a component of _APPNAME_ +#_APPNAME__SRCS += sncExample.stt +#_APPNAME__LIBS += seq +#_APPNAME__LIBS += pv #seq_DIR = $(SNCSEQ_LIB) #pv_DIR = $(SNCSEQ_LIB) @@ -77,23 +59,23 @@ example_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary #sncExample_LIBS += Com # Use win32 object libs for registered support -example_LIBS_win32 += recIocObj -example_LIBS_win32 += softDevIocObj -example_LIBS_win32 += testDevIocObj -example_LIBS_DEFAULT += recIoc -example_LIBS_DEFAULT += softDevIoc -example_LIBS_DEFAULT += testDevIoc +_APPNAME__LIBS_win32 += recIocObj +_APPNAME__LIBS_win32 += softDevIocObj +_APPNAME__LIBS_win32 += testDevIocObj +_APPNAME__LIBS_DEFAULT += recIoc +_APPNAME__LIBS_DEFAULT += softDevIoc +_APPNAME__LIBS_DEFAULT += testDevIoc -example_LIBS += iocsh -example_LIBS += miscIoc -example_LIBS += rsrvIoc -example_LIBS += dbtoolsIoc -example_LIBS += asIoc -example_LIBS += dbIoc -example_LIBS += registryIoc -example_LIBS += dbStaticIoc -example_LIBS += ca -example_LIBS += Com +_APPNAME__LIBS += iocsh +_APPNAME__LIBS += miscIoc +_APPNAME__LIBS += rsrvIoc +_APPNAME__LIBS += dbtoolsIoc +_APPNAME__LIBS += asIoc +_APPNAME__LIBS += dbIoc +_APPNAME__LIBS += registryIoc +_APPNAME__LIBS += dbStaticIoc +_APPNAME__LIBS += ca +_APPNAME__LIBS += Com #=========================== diff --git a/src/makeBaseApp/top/exampleApp/src/exampleInclude.dbd b/src/makeBaseApp/top/exampleApp/src/_APPNAME_Include.dbd similarity index 100% rename from src/makeBaseApp/top/exampleApp/src/exampleInclude.dbd rename to src/makeBaseApp/top/exampleApp/src/_APPNAME_Include.dbd diff --git a/src/makeBaseApp/top/exampleApp/src/exampleMain.cpp b/src/makeBaseApp/top/exampleApp/src/_APPNAME_Main.cpp similarity index 100% rename from src/makeBaseApp/top/exampleApp/src/exampleMain.cpp rename to src/makeBaseApp/top/exampleApp/src/_APPNAME_Main.cpp diff --git a/src/makeBaseApp/top/exampleApp/src/dbSubExample.c b/src/makeBaseApp/top/exampleApp/src/dbSubExample.c index 486d99c72..2afd9f15e 100644 --- a/src/makeBaseApp/top/exampleApp/src/dbSubExample.c +++ b/src/makeBaseApp/top/exampleApp/src/dbSubExample.c @@ -5,12 +5,14 @@ typedef long (*processMethod)(subRecord *precord); long mySubInit(subRecord *precord,processMethod process) { - printf("%s mySubInit process %p\n",precord->name, (void*) process); + printf("Record %s called mySubInit(%#p, %#p)\n", + precord->name, precord, (void*) process); return(0); } long mySubProcess(subRecord *precord) { - printf("%s mySubProcess\n",precord->name); + printf("Record %s called mySubProcess(%#p)\n", + precord->name, precord); return(0); } diff --git a/src/makeBaseApp/top/exampleBoot/ioc/README@Common b/src/makeBaseApp/top/exampleBoot/ioc/README@Common index 7ae9368b7..13d5f4884 100644 --- a/src/makeBaseApp/top/exampleBoot/ioc/README@Common +++ b/src/makeBaseApp/top/exampleBoot/ioc/README@Common @@ -1,3 +1,7 @@ To start the ioc from this directory execute the command - ../../bin/_ARCH_/example st.cmd + ../../bin/_ARCH_/ st.cmd + +You may need to change the name of the .dbd file given in +st.cmd's dbLoadDatabase command before starting the ioc. + diff --git a/src/makeBaseApp/top/simpleApp/src/Makefile b/src/makeBaseApp/top/simpleApp/src/Makefile index a5ac91cc8..dd892d525 100644 --- a/src/makeBaseApp/top/simpleApp/src/Makefile +++ b/src/makeBaseApp/top/simpleApp/src/Makefile @@ -3,73 +3,56 @@ TOP=../.. include $(TOP)/configure/CONFIG #---------------------------------------- # ADD MACRO DEFINITIONS AFTER THIS LINE +#============================= -#=======Create Host Programs======= -ca_DIR = $(EPICS_BASE_LIB) -Com_DIR = $(EPICS_BASE_LIB) +#_APPNAME_.dbd will be created from _APPNAME_Include.dbd +DBD += _APPNAME_.dbd -#USR_CFLAGS += +#_APPNAME__registerRecordDeviceDriver.cpp will be created from _APPNAME_.dbd +_APPNAME__SRCS += _APPNAME__registerRecordDeviceDriver.cpp -# For Each Host Product define the following:1 -#PROD_HOST = -#_SRCS += xxx.c -#... -#_LIBS += ca -#_LIBS += Com +#============================= -#========Database Products================ +PROD_IOC = _APPNAME_ -# xxxRecord.h will be created from xxxRecord.dbd -#DBDINC += xxxRecord +## Build and add other local sources like this: +#_APPNAME__SRCS += xxx.c xxx.cpp xxx.stt -#.dbd will be created from Include.dbd -#DBD += .dbd +## If _APPNAME_ is to be built for both vxWorks and for the host then +_APPNAME__SRCS_DEFAULT += _APPNAME_Main.cpp +_APPNAME__SRCS_vxWorks += -nil- -#=======Create an IOC program===== - -#PROD_IOC = - -#_SRCS += xxx.c # .cpp .stt etc -#... -#_registerRecordDeviceDriver.cpp will be created from .dbd -#_SRCS += _registerRecordDeviceDriver.cpp - -## If is to be built for both vxWorks and for the host then -#_SRCS_DEFAULT += Main.cpp -#_SRCS_vxWorks += -nil- - -#The following adds support from base/src/vxWorks -#_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary +## The following adds support from base/src/vxWorks +_APPNAME__OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary ##Define all possible libraries ## Use win32 object libs for registered support -#_LIBS_win32 += recIocObj -#_LIBS_win32 += softDevIocObj -#_LIBS_win32 += testDevIocObj -#_LIBS_DEFAULT += recIoc -#_LIBS_DEFAULT += softDevIoc -#_LIBS_DEFAULT += testDevIoc +_APPNAME__LIBS_win32 += recIocObj +_APPNAME__LIBS_win32 += softDevIocObj +_APPNAME__LIBS_win32 += testDevIocObj +_APPNAME__LIBS_DEFAULT += recIoc +_APPNAME__LIBS_DEFAULT += softDevIoc +_APPNAME__LIBS_DEFAULT += testDevIoc -#_LIBS += iocsh -#_LIBS += miscIoc -#_LIBS += rsrvIoc -#_LIBS += dbtoolsIoc -#_LIBS += asIoc -#_LIBS += dbIoc -#_LIBS += registryIoc -#_LIBS += dbStaticIoc -#_LIBS += ca -#_LIBS += Com +_APPNAME__LIBS += iocsh +_APPNAME__LIBS += miscIoc +_APPNAME__LIBS += rsrvIoc +_APPNAME__LIBS += dbtoolsIoc +_APPNAME__LIBS += asIoc +_APPNAME__LIBS += dbIoc +_APPNAME__LIBS += registryIoc +_APPNAME__LIBS += dbStaticIoc +_APPNAME__LIBS += ca +_APPNAME__LIBS += Com -## If sequence programs are being built -#_LIBS += seq -#_LIBS += pv +## For sequence programs, use +#_APPNAME__SRCS += sncExample.stt +#_APPNAME__LIBS += seq +#_APPNAME__LIBS += pv #seq_DIR = $(SNCSEQ_LIB) #pv_DIR = $(SNCSEQ_LIB) - include $(TOP)/configure/RULES #---------------------------------------- # ADD RULES AFTER THIS LINE - diff --git a/src/makeBaseApp/top/simpleApp/src/_APPNAME_Include.dbd b/src/makeBaseApp/top/simpleApp/src/_APPNAME_Include.dbd new file mode 100644 index 000000000..ec6ea3f38 --- /dev/null +++ b/src/makeBaseApp/top/simpleApp/src/_APPNAME_Include.dbd @@ -0,0 +1 @@ +include "base.dbd" diff --git a/src/makeBaseApp/top/simpleApp/src/_APPNAME_Main.cpp b/src/makeBaseApp/top/simpleApp/src/_APPNAME_Main.cpp new file mode 100644 index 000000000..0e4c7a455 --- /dev/null +++ b/src/makeBaseApp/top/simpleApp/src/_APPNAME_Main.cpp @@ -0,0 +1,21 @@ +/* exampleMain.c */ +/* Author: Marty Kraimer Date: 17MAR2000 */ + +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "iocsh.h" + +int main(int argc,char *argv[]) +{ + if(argc>=2) { + iocsh(argv[1]); + epicsThreadSleep(.2); + } + iocsh(NULL); + return(0); +} diff --git a/src/makeBaseApp/top/simpleApp/src/base.dbd b/src/makeBaseApp/top/simpleApp/src/base.dbd new file mode 100644 index 000000000..42cc92803 --- /dev/null +++ b/src/makeBaseApp/top/simpleApp/src/base.dbd @@ -0,0 +1,50 @@ +include "menuGlobal.dbd" +include "menuConvert.dbd" +include "aiRecord.dbd" +include "aoRecord.dbd" +include "biRecord.dbd" +include "boRecord.dbd" +include "calcRecord.dbd" +include "calcoutRecord.dbd" +include "compressRecord.dbd" +include "dfanoutRecord.dbd" +include "eventRecord.dbd" +include "fanoutRecord.dbd" +include "longinRecord.dbd" +include "longoutRecord.dbd" +include "mbbiRecord.dbd" +include "mbbiDirectRecord.dbd" +include "mbboRecord.dbd" +include "mbboDirectRecord.dbd" +include "permissiveRecord.dbd" +include "selRecord.dbd" +include "seqRecord.dbd" +include "stateRecord.dbd" +include "stringinRecord.dbd" +include "stringoutRecord.dbd" +include "subRecord.dbd" +include "subArrayRecord.dbd" +include "waveformRecord.dbd" +device(ai,CONSTANT,devAiSoft,"Soft Channel") +device(ai,CONSTANT,devAiSoftRaw,"Raw Soft Channel") +device(ao,CONSTANT,devAoSoft,"Soft Channel") +device(ao,CONSTANT,devAoSoftRaw,"Raw Soft Channel") +device(bi,CONSTANT,devBiSoft,"Soft Channel") +device(bi,CONSTANT,devBiSoftRaw,"Raw Soft Channel") +device(bo,CONSTANT,devBoSoft,"Soft Channel") +device(bo,CONSTANT,devBoSoftRaw,"Raw Soft Channel") +device(event,CONSTANT,devEventSoft,"Soft Channel") +device(longin,CONSTANT,devLiSoft,"Soft Channel") +device(longout,CONSTANT,devLoSoft,"Soft Channel") +device(mbbi,CONSTANT,devMbbiSoft,"Soft Channel") +device(mbbi,CONSTANT,devMbbiSoftRaw,"Raw Soft Channel") +device(mbbiDirect,CONSTANT,devMbbiDirectSoft,"Soft Channel") +device(mbbiDirect,CONSTANT,devMbbiDirectSoftRaw,"Raw Soft Channel") +device(mbbo,CONSTANT,devMbboSoft,"Soft Channel") +device(mbbo,CONSTANT,devMbboSoftRaw,"Raw Soft Channel") +device(mbboDirect,CONSTANT,devMbboDirectSoft,"Soft Channel") +device(mbboDirect,CONSTANT,devMbboDirectSoftRaw,"Raw Soft Channel") +device(stringin,CONSTANT,devSiSoft,"Soft Channel") +device(stringout,CONSTANT,devSoSoft,"Soft Channel") +device(subArray,CONSTANT,devSASoft,"Soft Channel") +device(waveform,CONSTANT,devWfSoft,"Soft Channel")