diff --git a/src/cas/example/simple/Makefile b/src/cas/example/simple/Makefile new file mode 100644 index 000000000..d2a6e1302 --- /dev/null +++ b/src/cas/example/simple/Makefile @@ -0,0 +1,7 @@ + +TOP=../../../.. + +include $(TOP)/config/CONFIG_BASE + +include $(TOP)/config/RULES_ARCHS + diff --git a/src/cas/example/simple/Makefile.Unix b/src/cas/example/simple/Makefile.Unix new file mode 100644 index 000000000..bc5e63fc7 --- /dev/null +++ b/src/cas/example/simple/Makefile.Unix @@ -0,0 +1,94 @@ + +CAS = ../../.. +TOP = $(CAS)/../.. + +include $(TOP)/config/CONFIG_BASE + +CXXCMPLR = STRICT + +USR_INCLUDES = +USR_LDLIBS = -lcas -lca -lCom -lgdd + +USR_LDFLAGS = + +DEPLIBS_BASE = $(EPICS_BASE_LIB) +DEPLIBSWOCAS = $(DEPLIBS_BASE)/libca.a $(DEPLIBS_BASE)/libCom.a \ + $(DEPLIBS_BASE)/libgdd.a +DEPLIBS = $(DEPLIBS_BASE)/libcas.a $(DEPLIBSWOCAS) + +SRCS.cc += ../main.cc +SRCS.cc += ../exServer.cc +SRCS.cc += ../exPV.cc +SRCS.cc += ../exSyncPV.cc +SRCS.cc += ../exAsyncPV.cc +SRCS.cc += ../exChannel.cc +SRCS.cc += ../templInst.cc + +OBJS += main.o +OBJS += exServer.o +OBJS += exPV.o +OBJS += exSyncPV.o +OBJS += exAsyncPV.o +OBJS += exChannel.o +OBJS += templInst.o + +PROD += excas + +BUILDDIR = $(CAS)/build/singleThread/O.sun4/ + +GLOMBOBJ += $(BUILDDIR)caServer.o +GLOMBOBJ += $(BUILDDIR)caServerI.o +GLOMBOBJ += $(BUILDDIR)casCoreClient.o +GLOMBOBJ += $(BUILDDIR)casClient.o +GLOMBOBJ += $(BUILDDIR)casDGClient.o +GLOMBOBJ += $(BUILDDIR)casStrmClient.o +GLOMBOBJ += $(BUILDDIR)casPV.o +GLOMBOBJ += $(BUILDDIR)casPVI.o +GLOMBOBJ += $(BUILDDIR)casChannel.o +GLOMBOBJ += $(BUILDDIR)casChannelI.o +GLOMBOBJ += $(BUILDDIR)casClientMon.o +GLOMBOBJ += $(BUILDDIR)casChanDelEv.o +GLOMBOBJ += $(BUILDDIR)casMsgIO.o +GLOMBOBJ += $(BUILDDIR)casAsyncIO.o +GLOMBOBJ += $(BUILDDIR)casAsyncIOI.o +GLOMBOBJ += $(BUILDDIR)casEventSys.o +GLOMBOBJ += $(BUILDDIR)casMonitor.o +GLOMBOBJ += $(BUILDDIR)casMonEvent.o +GLOMBOBJ += $(BUILDDIR)outBuf.o +GLOMBOBJ += $(BUILDDIR)inBuf.o +GLOMBOBJ += $(BUILDDIR)casEventMask.o +GLOMBOBJ += $(BUILDDIR)ioBlocked.o + +GLOMBOBJ += $(BUILDDIR)caServerOS.o +GLOMBOBJ += $(BUILDDIR)casDGOS.o +GLOMBOBJ += $(BUILDDIR)casStreamOS.o + +GLOMBOBJ += $(BUILDDIR)caServerIO.o +GLOMBOBJ += $(BUILDDIR)casDGIO.o +GLOMBOBJ += $(BUILDDIR)casStreamIO.o +GLOMBOBJ += $(BUILDDIR)sigPipeIgnore.o + +include $(TOP)/config/RULES.Unix + +excas: $(OBJS) $(DEPLIBS) + $(LINK.cc) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS) + +# +# fast link (without waiting for library build) +# +fexcas: $(OBJS) $(GLOMBOBJ) $(DEPLIBSWOCAS) + $(LINK.cc) -o $@ $(OBJS) $(GLOMBOBJ) $(DEPLIBSWOCAS) + +# +# build with purify +# +pexcas: $(OBJS) $(DEPLIBS) + $(PURIFY) $(LINK.cc) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS) + +clean:: + @$(RM) excas + @$(RM) fexcas + @$(RM) pexcas + @$(RM) -rf Templates.DB + @$(RM) core + diff --git a/src/cas/example/simple/Makefile.Vx.WorkInProgress b/src/cas/example/simple/Makefile.Vx.WorkInProgress new file mode 100644 index 000000000..e85297480 --- /dev/null +++ b/src/cas/example/simple/Makefile.Vx.WorkInProgress @@ -0,0 +1,38 @@ + +CAS = ../../.. +TOP = $(CAS)/../.. + +include $(TOP)/config/CONFIG_BASE + +CXXCMPLR = STRICT + +USR_INCLUDES = +USR_LDFLAGS = + +DEPLIBS_BASE = $(EPICS_BASE_LIB) +DEPLIBS = $(DEPLIBS_BASE)/libcas.a $(DEPLIBSWOCAS) + +SRCS.cc += ../vxEntry.cc +SRCS.cc += ../exServer.cc +SRCS.cc += ../exPV.cc +SRCS.cc += ../exSyncPV.cc +SRCS.cc += ../exAsyncPV.cc +SRCS.cc += ../exChannel.cc + +LIBOBJS += vxEntry.o +LIBOBJS += exServer.o +LIBOBJS += exPV.o +LIBOBJS += exSyncPV.o +LIBOBJS += exAsyncPV.o +LIBOBJS += exChannel.o + +LIBNAME = libexserver.o +include $(TOP)/config/RULES.Vx + +excas: $(OBJS) $(DEPLIBS) + $(LINK.cc) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS) + +clean:: + @$(RM) -rf Templates.DB + @$(RM) core + diff --git a/src/cas/example/simple/README b/src/cas/example/simple/README new file mode 100644 index 000000000..1ab19153f --- /dev/null +++ b/src/cas/example/simple/README @@ -0,0 +1,13 @@ + +This directory contains 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/cas/example/simple/exAsyncPV.cc b/src/cas/example/simple/exAsyncPV.cc new file mode 100644 index 000000000..63dcc9720 --- /dev/null +++ b/src/cas/example/simple/exAsyncPV.cc @@ -0,0 +1,95 @@ + +// +// Example EPICS CA server +// + +#include + +// +// exAsyncPV::maxSimultAsyncOps() +// (virtual replacement for the default) +// +unsigned exAsyncPV::maxSimultAsyncOps () const +{ + return 500u; +} + +// +// exAsyncPV::read() +// (virtual replacement for the default) +// +caStatus exAsyncPV::read (const casCtx &ctx, gdd &valueIn) +{ + exAsyncReadIO *pIO; + + 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, gdd &valueIn) +{ + exAsyncWriteIO *pIO; + + pIO = new exAsyncWriteIO(ctx, *this, valueIn); + if (!pIO) { + return S_casApp_noMemory; + } + + return S_casApp_asyncCompletion; +} + +// +// exAsyncWriteIO::expire() +// (a virtual function that runs when the base timer expires) +// +void exAsyncWriteIO::expire() +{ + caStatus status; + status = this->pv.update(this->value); + this->postIOCompletion (status); +} + +// +// exAsyncWriteIO::name() +// +const char *exAsyncWriteIO::name() const +{ + return "exAsyncWriteIO"; +} + +// +// exAsyncReadIO::expire() +// (a virtual function that runs when the base timer expires) +// +void exAsyncReadIO::expire() +{ + caStatus status; + + // + // map between the prototype in and the + // current value + // + status = exServer::read(this->pv, this->proto); + + // + // post IO completion + // + this->postIOCompletion(status, this->proto); +} + +// +// exAsyncReadIO::name() +// +const char *exAsyncReadIO::name() const +{ + return "exAsyncReadIO"; +} + diff --git a/src/cas/example/simple/exChannel.cc b/src/cas/example/simple/exChannel.cc new file mode 100644 index 000000000..18802d574 --- /dev/null +++ b/src/cas/example/simple/exChannel.cc @@ -0,0 +1,7 @@ + +// +// Example EPICS CA server +// + +#include + diff --git a/src/cas/example/simple/exPV.cc b/src/cas/example/simple/exPV.cc new file mode 100644 index 000000000..86a588064 --- /dev/null +++ b/src/cas/example/simple/exPV.cc @@ -0,0 +1,394 @@ +// +// Example EPICS CA server +// + +#include +#include + +const double myPI = 3.14159265358979323846; + +osiTime exPV::currentTime; + +// +// exPV::exPV() +// +exPV::exPV (const casCtx &ctxIn, const pvInfo &setup) : + pValue(NULL), + pScanTimer(NULL), + info(setup), + casPV(ctxIn, setup.getName().string()), + interest(aitFalse) +{ + // + // load initial value + // + this->scanPV(); +} + +// +// exPV::~exPV() +// +exPV::~exPV() +{ + if (this->pScanTimer) { + delete this->pScanTimer; + this->pScanTimer = NULL; + } + if (this->pValue) { + this->pValue->unreference(); + this->pValue = NULL; + } +} + +// +// exPV::scanPV(); +// +void exPV::scanPV() +{ + caStatus status; + double radians; + gdd *pDD; + float newValue; + float limit; + caServer *pCAS = this->getCAS(); + + if (!pCAS) { + return; + } + + // + // 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; + pDD->setStat (epicsAlarmNone); + pDD->setSevr (epicsSevNone); + status = this->update (*pDD); + if (status) { + errMessage (status, "scan update failed\n"); + } + + pDD->unreference(); +} + +// +// exScanTimer::expire () +// +void exScanTimer::expire () +{ + pv.scanPV(); +} + +// +// exScanTimer::again() +// +osiBool exScanTimer::again() const +{ + return osiTrue; +} + +// +// exScanTimer::delay() +// +const osiTime exScanTimer::delay() const +{ + return pv.getScanRate(); +} + +// +// exScanTimer::name() +// +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() +// +aitEnum exPV::bestExternalType() const +{ + return aitEnumFloat32; +} + +// +// exPV::interestRegister() +// +caStatus exPV::interestRegister() +{ + caServer *pCAS = this->getCAS(); + + if (!pCAS) { + return S_casApp_success; + } + + if (!this->pScanTimer) { + this->pScanTimer = new exScanTimer + (this->info.getScanRate(), *this); + if (!this->pScanTimer) { + errPrintf (S_cas_noMemory, __FILE__, __LINE__, + "Scan init for %s failed\n", + this->info.getName().string()); + return S_cas_noMemory; + } + } + + this->interest = aitTrue; + + return S_casApp_success; +} + +// +// exPV::interestDelete() +// +void exPV::interestDelete() +{ + if (this->pScanTimer) { + delete this->pScanTimer; + this->pScanTimer = NULL; + } + this->interest = aitFalse; +} + +// +// exPV::show() +// +void exPV::show(unsigned level) +{ + if (level>1u) { + if (this->pValue) { + printf("exPV: cond=%d\n", this->pValue->getStat()); + printf("exPV: sevr=%d\n", this->pValue->getSevr()); + printf("exPV: value=%f\n", (double) *this->pValue); + } + printf("exPV: interest=%d\n", this->interest); + printf("exPV: pScanTimer=%x\n", (unsigned) this->pScanTimer); + } +} + +// +// exPV::getStatus() +// +caStatus exPV::getStatus(gdd &value) +{ + if (this->pValue) { + value.put(this->pValue->getStat()); + } + else { + value.put((aitUint16)epicsAlarmUDF); + } + return S_cas_success; +} + +// +// exPV::getSeverity() +// +caStatus exPV::getSeverity(gdd &value) +{ + if (this->pValue) { + value.put(this->pValue->getSevr()); + } + else { + value.put((aitUint16)epicsSevInvalid); + } + return S_cas_success; +} + +// +// exPV::getTS() +// +inline aitTimeStamp exPV::getTS() +{ + aitTimeStamp ts; + if (this->pValue) { + this->pValue->getTimeStamp(&ts); + } + else { + osiTime cur(osiTime::getCurrent()); + cur.get(ts.tv_sec, ts.tv_nsec); + } + return ts; +} + +// +// exPV::getSeconds() +// +caStatus exPV::getSeconds(gdd &value) +{ + aitUint32 sec (this->getTS().tv_sec); + value.put(sec); + return S_cas_success; +} + +// +// exPV::getNanoseconds() +// +caStatus exPV::getNanoseconds(gdd &value) +{ + aitUint32 nsec (this->getTS().tv_nsec); + value.put(nsec); + return S_cas_success; +} + +// +// 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) +{ + static aitString str("@#$%"); + units.put(str); + return S_cas_success; +} + +// +// exPV::getEnums() +// +caStatus exPV::getEnums(gdd &) +{ + return S_cas_noConvert; +} + +// +// exPV::getValue() +// +caStatus exPV::getValue(gdd &value) +{ + caStatus status; + + if (this->pValue) { + 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; +} + diff --git a/src/cas/example/simple/exServer.cc b/src/cas/example/simple/exServer.cc new file mode 100644 index 000000000..6f3baed63 --- /dev/null +++ b/src/cas/example/simple/exServer.cc @@ -0,0 +1,161 @@ +// +// fileDescriptorManager.process(delay); +// (the name of the global symbol has leaked in here) +// + +// +// Example EPICS CA server +// + +#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) +}; + +// +// static data for exServer +// +gddAppFuncTable exServer::ft; + +// +// exServer::exServer() +// +exServer::exServer(unsigned pvMaxNameLength, unsigned pvCountEstimate, + unsigned maxSimultaneousIO) : + caServer(pvMaxNameLength, pvCountEstimate, maxSimultaneousIO) +{ + ft.installReadFunc("status",exPV::getStatus); + ft.installReadFunc("severity",exPV::getSeverity); + ft.installReadFunc("seconds",exPV::getSeconds); + ft.installReadFunc("nanoseconds",exPV::getNanoseconds); + ft.installReadFunc("precision",exPV::getPrecision); + ft.installReadFunc("graphicHigh",exPV::getHighLimit); + ft.installReadFunc("graphicLow",exPV::getLowLimit); + ft.installReadFunc("controlHigh",exPV::getHighLimit); + ft.installReadFunc("controlLow",exPV::getLowLimit); + ft.installReadFunc("alarmHigh",exPV::getHighLimit); + ft.installReadFunc("alarmLow",exPV::getLowLimit); + ft.installReadFunc("alarmHighWarning",exPV::getHighLimit); + ft.installReadFunc("alarmLowWarning",exPV::getLowLimit); + ft.installReadFunc("units",exPV::getUnits); + ft.installReadFunc("value",exPV::getValue); + ft.installReadFunc("enums",exPV::getEnums); +} + +// +// exServer::pvExistTest() +// +pvExistReturn exServer::pvExistTest(const casCtx &ctxIn, const char *pPVName) +{ + const pvInfo *pPVI; + + pPVI = exServer::findPV(pPVName); + if (pPVI) { + if (pPVI->getIOType()==excasIoAsync) { + exAsyncExistIO *pIO; + pIO = new exAsyncExistIO(*pPVI, ctxIn); + if (pIO) { + return pvExistReturn(S_casApp_asyncCompletion); + } + else { + return pvExistReturn(S_casApp_noMemory); + } + } + + const char *pName = pPVI->getName(); + return pvExistReturn(S_casApp_success, pName); + } + + return pvExistReturn(S_casApp_pvNotFound); +} + +// +// findPV() +// +const pvInfo *exServer::findPV(const char *pName) +{ + const pvInfo *pPVI; + const pvInfo *pPVAfter = + &exServer::pvList[NELEMENTS(exServer::pvList)]; + + for (pPVI = exServer::pvList; pPVI < pPVAfter; pPVI++) { + if (strcmp (pName, pPVI->getName().string()) == '\0') { + return pPVI; + } + } + return NULL; +} + +// +// exServer::createPV() +// +casPV *exServer::createPV (const casCtx &ctxIn, const char *pPVName) +{ + const pvInfo *pInfo; + + 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; + } +} + +// +// exServer::show() +// +void exServer::show (unsigned level) +{ + // + // server tool specific show code goes here + // + + // + // print information about ca server libarary + // internals + // + this->caServer::show(level); +} + +// +// this is a noop that postpones the timer expiration +// destroy so the exAsyncIO class will hang around until the +// casAsyncIO::destroy() is called +// +void exOSITimer::destroy() +{ +} + +// +// exAsyncExistIO::expire() +// (a virtual function that runs when the base timer expires) +// +void exAsyncExistIO::expire() +{ + const char *pName = pvi.getName(); + + // + // post IO completion + // + this->postIOCompletion (pvExistReturn(S_cas_success, pName)); +} + +// +// exAsyncExistIO::name() +// +const char *exAsyncExistIO::name() const +{ + return "exAsyncExistIO"; +} + diff --git a/src/cas/example/simple/exServer.h b/src/cas/example/simple/exServer.h new file mode 100644 index 000000000..8cc71a634 --- /dev/null +++ b/src/cas/example/simple/exServer.h @@ -0,0 +1,390 @@ +// +// Example EPICS CA server +// + +// +// 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 +// +#include +#include +#include +#include + +#ifndef max +#define max(A,B) ((A)<(B)?(B):(A)) +#endif + +#ifndef min +#define min(A,B) ((A)>(B)?(B):(A)) +#endif + +#ifndef NELEMENTS +# define NELEMENTS(A) (sizeof(A)/sizeof(A[0])) +#endif + +#define LOCAL static + +// +// info about all pv in this server +// +enum excasIoType {excasIoSync, excasIoAsync}; + +class pvInfo { +public: + pvInfo (double scanRateIn, const char *pName, + aitFloat32 hoprIn, aitFloat32 loprIn, + excasIoType ioTypeIn) : + scanRate(scanRateIn), name(pName), hopr(hoprIn), + lopr(loprIn), ioType(ioTypeIn) + { + } + + // + // for use when MSVC++ will not build a defualt copy constructor + // for this class + // + pvInfo (const pvInfo ©In) : + scanRate(copyIn.scanRate), name(copyIn.name), + hopr(copyIn.hopr), lopr(copyIn.lopr), + ioType(copyIn.ioType) + { + } + + const double getScanRate () const { return this->scanRate; } + const aitString &getName () const { return this->name; } + const double getHopr () const { return this->hopr; } + const double getLopr () const { return this->lopr; } + const excasIoType getIOType () const { return this->ioType; } +private: + const double scanRate; + const aitString name; + const double hopr; + const double lopr; + const excasIoType ioType; +}; + +class exPV; + + +// +// exScanTimer +// +class exScanTimer : public osiTimer { +public: + exScanTimer (double delayIn, exPV &pvIn) : + pv(pvIn), osiTimer(delayIn) {} + void expire (); + osiBool again() const; + const osiTime delay() const; + const char *name() const; +private: + exPV &pv; +}; + +// +// exPV +// +class exPV : public casPV { + +public: + exPV (const casCtx &ctxIn, const pvInfo &setup); + virtual ~exPV(); + + void scanPV(); + + void show(unsigned level); + + // + // 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 (); + + caStatus update (gdd &value); + + // + // Std PV Attribute fetch support + // + gddAppFuncTableStatus getStatus(gdd &value); + gddAppFuncTableStatus getSeverity(gdd &value); + gddAppFuncTableStatus getSeconds(gdd &value); + gddAppFuncTableStatus getNanoseconds(gdd &value); + gddAppFuncTableStatus getPrecision(gdd &value); + gddAppFuncTableStatus getHighLimit(gdd &value); + gddAppFuncTableStatus getLowLimit(gdd &value); + gddAppFuncTableStatus getUnits(gdd &value); + gddAppFuncTableStatus getValue(gdd &value); + gddAppFuncTableStatus getEnums(gdd &value); + + // + // + // + aitTimeStamp getTS(); + + const float getScanRate() + { + return this->info.getScanRate(); + } +protected: + gdd *pValue; + exScanTimer *pScanTimer; + const pvInfo & info; + aitBool interest; +private: + static osiTime currentTime; +}; + +// +// exServer +// +class exServer : public caServer { +public: + exServer(unsigned pvMaxNameLength, unsigned pvCountEstimate=0x3ff, + unsigned maxSimultaneousIO=1u); + void show (unsigned level); + pvExistReturn pvExistTest (const casCtx &ctxIn, const char *pPVName); + casPV *createPV (const casCtx &ctxIn, const char *pPVName); + + static const pvInfo *findPV(const char *pName); + + static gddAppFuncTableStatus read(exPV &pv, gdd &value) + { + return exServer::ft.read(pv, value); + } +private: + static const pvInfo pvList[]; + 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 { +public: + // + // exAsyncPV() + // + exAsyncPV (const casCtx &ctxIn, const pvInfo &setup) : + exPV (ctxIn, setup) {} + + // + // read + // + caStatus read(const casCtx &ctxIn, gdd &value); + + // + // write + // + caStatus write(const casCtx &ctxIn, gdd &value); + + unsigned maxSimultAsyncOps () const; +private: +}; + +// +// exChannel +// +class exChannel : public casChannel{ +public: + exChannel(const casCtx &ctxIn) : casChannel(ctxIn) {} + + //void setOwner(const char *pUserName, const char *pHostName){}; + + // + // called when the first client begins to monitor the PV + // + caStatus interestRegister () + { + return S_cas_success; + } + + // + // called when the last client stops monitoring the PV + // + void interestDelete () {} + + // + // the following are encouraged to change during an channel's + // lifetime + // + aitBool readAccess () const {return aitTrue;}; + aitBool writeAccess () const {return aitTrue;}; + +private: +}; + +// +// exOSITimer +// +// a special version of osiTimer which is only to be used +// within an exAsyncIO. The destroy() method is replaced +// so that the timer destroy() will not destroy the +// exAsyncIO until the casAsyncIO has completed +// +class exOSITimer : public osiTimer { +public: + exOSITimer() : osiTimer(osiTime(0.010)) {} // 10 mSec + + // + // this is a noop that postpones the timer expiration + // destroy so this object will hang around until the + // casAsyncIO::destroy() is called + // + void destroy(); +}; + +// +// exAsyncWriteIO +// +class exAsyncWriteIO : public casAsyncWriteIO, public exOSITimer { +public: + // + // exAsyncWriteIO() + // + exAsyncWriteIO(const casCtx &ctxIn, exAsyncPV &pvIn, gdd &valueIn) : + casAsyncWriteIO(ctxIn), pv(pvIn), value(valueIn) + { + this->value.reference(); + } + + ~exAsyncWriteIO() + { + this->value.unreference(); + } + + // + // expire() + // (a virtual function that runs when the base timer expires) + // see exAsyncPV.cc + // + void expire(); + + const char *name() const; + +private: + exAsyncPV &pv; + gdd &value; +}; + +// +// exAsyncReadIO +// +class exAsyncReadIO : public casAsyncReadIO, public exOSITimer { +public: + // + // exAsyncReadIO() + // + exAsyncReadIO(const casCtx &ctxIn, exAsyncPV &pvIn, gdd &protoIn) : + casAsyncReadIO(ctxIn), pv(pvIn), proto(protoIn) + { + this->proto.reference(); + } + + ~exAsyncReadIO() + { + this->proto.unreference(); + } + + // + // expire() + // (a virtual function that runs when the base timer expires) + // see exAsyncPV.cc + // + void expire(); + + const char *name() const; + +private: + exAsyncPV &pv; + gdd &proto; +}; + +// +// exAsyncExistIO +// (PV exist async IO) +// +class exAsyncExistIO : public casAsyncPVExistIO, public exOSITimer { +public: + // + // exAsyncExistIO() + // + exAsyncExistIO(const pvInfo &pviIn, const casCtx &ctxIn) : + casAsyncPVExistIO(ctxIn), pvi(pviIn) {} + + // + // expire() + // (a virtual function that runs when the base timer expires) + // see exServer.cc + // + void expire(); + + const char *name() const; +private: + const pvInfo &pvi; +}; + + diff --git a/src/cas/example/simple/exSyncPV.cc b/src/cas/example/simple/exSyncPV.cc new file mode 100644 index 000000000..b974aa724 --- /dev/null +++ b/src/cas/example/simple/exSyncPV.cc @@ -0,0 +1,38 @@ + +// +// 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/main.cc b/src/cas/example/simple/main.cc new file mode 100644 index 000000000..a973b036f --- /dev/null +++ b/src/cas/example/simple/main.cc @@ -0,0 +1,62 @@ + +#include +#include + +// +// main() +// (example single threaded ca server tool main loop) +// +int main (int argc, const char **argv) +{ + osiTime begin(osiTime::getCurrent()); + exServer *pCAS; + unsigned debugLevel = 0u; + float executionTime; + aitBool forever = aitTrue; + int i; + + pCAS = new exServer(32u,5u,500u); + if (!pCAS) { + return (-1); + } + + for (i=1; i -t\n", + argv[0]); + return (1); + } + + pCAS->setDebugLevel(debugLevel); + + if (forever) { + osiTime delay(1000u,0u); + // + // loop here forever + // + while (aitTrue) { + fileDescriptorManager.process(delay); + } + } + else { + osiTime total(executionTime); + osiTime delay(osiTime::getCurrent() - begin); + // + // loop here untime the specified execution time + // expires + // + while (delay < total) { + fileDescriptorManager.process(delay); + delay = osiTime::getCurrent() - begin; + } + } + delete pCAS; + return (0); +} + diff --git a/src/cas/example/simple/templInst.cc b/src/cas/example/simple/templInst.cc new file mode 100644 index 000000000..88999c7be --- /dev/null +++ b/src/cas/example/simple/templInst.cc @@ -0,0 +1,26 @@ + +// +// $Id$ +// +// Explcit instantiation of template instances used by the +// example server +// +// + +#include "exServer.h" +#include "gddAppFuncTable.cc" + +// +// Sun C++ 4.1 still appears to be lacking support in this area +// +#if !defined(__SUNPRO_CC) + // + // From Stroustrups's "The C++ Programming Language" + // Appendix A: r.14.9 + // + // This explicitly instantiates the template class's + // member functions into "templInst.o" + // + template class gddAppFuncTable ; +#endif + diff --git a/src/cas/example/simple/test.adl b/src/cas/example/simple/test.adl new file mode 100644 index 000000000..cd6b79b46 --- /dev/null +++ b/src/cas/example/simple/test.adl @@ -0,0 +1,846 @@ +file { + name="test.dl" +} +display { + magic="305419896" + majv="2" + mnrv="2" + 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" + RISC_pad1="0" + RISC_pad2="0" + 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="0" + 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="0" + 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="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="0" + 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="0" + 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="0" + 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="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="0" + 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="0" + 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="0" + 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="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="0" + 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="0" + 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="0" + 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="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="0" + newunits="" + units="none" + decorate="none" + convertFunc="" + convertParams="" + } + direction="down" + gain="coarse" + sendMode="send on motion" + increment="0" +} diff --git a/src/cas/example/simple/vxEntry.cc b/src/cas/example/simple/vxEntry.cc new file mode 100644 index 000000000..a214fa7e0 --- /dev/null +++ b/src/cas/example/simple/vxEntry.cc @@ -0,0 +1,73 @@ +// +// $Id$ +// Author: Jeff HIll (LANL) +// +// $Log$ +// Revision 1.2 1996/09/16 18:22:09 jhill +// added cvs log entries +// +// + +#include +#include + +// +// 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) +{ + osiTime begin(osiTime::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 { + osiTime total( ((float)delaySec) ); + osiTime delay(osiTime::getCurrent() - begin); + // + // loop here untill the specified execution time + // expires + // + while (delay < total) { + taskDelay(10); + delay = osiTime::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" +