/*************************************************************************\ * 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-EPICS that is included with this distribution. \*************************************************************************/ // // Example EPICS CA server // #include "exServer.h" #include "gddApps.h" #include "dbMapper.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 ( exServer & casIn, pvInfo & setup, bool preCreateFlag, bool scanOnIn ) : cas ( casIn ), timer ( cas.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; } } /* * Events that can be posted are: * #define DBE_VALUE (1<<0) * #define DBE_ARCHIVE (1<<1) * #define DBE_LOG DBE_ARCHIVE * #define DBE_ALARM (1<<2) * #define DBE_PROPERTY (1<<3) * see /src/db/dbEvent.c */ // // exPV::update() // caStatus exPV::update ( const gdd & valueIn, bool processEvent, bool updateValue ) { # if DEBUG printf("Setting %s too:\n", this->info.getName().string()); valueIn.dump(); # endif aitInt16 sta = 0, sev = 0; if ( this->pValue.valid() ) // check the validity this->pValue->getStatSevr(sta, sev); // fetch previous values if ( updateValue ) { //----------------------------------------------------------------------------- updateValue caStatus status = this->updateValue ( valueIn ); if ( status || ( ! this->pValue.valid() ) ) { return status; } } if ( !processEvent || !(this->pValue.valid()) ) return S_casApp_success; //----------------------------------------------------------------------------- processEvent caServer * pCAS = this->getCAS(); if ( this->interest == true && pCAS != NULL ) { casEventMask monitor_mask; //monitor from aiRecord.c aiRecord_monitor: // alarms aitInt16 nsta, nsev; this->pValue->getStatSevr(nsta, nsev); // fetch previous values if ( (nsta != sta) || (nsev != sev) ) monitor_mask |= pCAS->alarmEventMask(); if ( (this->pValue)->isScalar() ) { double delta; double value; this->pValue->get(value); // monitoring delta = this->info.mlst - value; if (delta < 0.0) delta = - delta; if ( delta > this->info.mdel) { monitor_mask |= pCAS->valueEventMask(); this->info.mlst = value; } //archiving delta = this->info.alst - value; if (delta < 0.0) delta = - delta; if ( delta > this->info.adel ) { monitor_mask |= pCAS->logEventMask(); this->info.alst = value; } } else monitor_mask |= pCAS->valueEventMask(); if ( monitor_mask.eventsSelected() ) this->postEvent ( monitor_mask, *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 () { if ( ! this->getCAS() ) { 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. // // questo č un limite visto dal punto di vista del RECORD // perche dovremmo associare un sacco di apptype che non esistono... // perciņ dovremmo abbandonare questa interfaccia per passare ai record exPV::ft.installReadFunc ("value", &exPV::getValue); exPV::ft.installReadFunc ("precision", &exPV::getPrecision); exPV::ft.installReadFunc ("units", &exPV::getUnits); exPV::ft.installReadFunc ("enums", &exPV::getEnums); 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::getHighAlarm); exPV::ft.installReadFunc ("alarmLow", &exPV::getLowAlarm); exPV::ft.installReadFunc ("alarmHighWarning", &exPV::getHighWarning); exPV::ft.installReadFunc ("alarmLowWarning", &exPV::getLowWarning); // possiamo registrare anche le altre funzioni che si trovano nel mapper.. // gdd/gddAppTale.h, per il momento ci interessano "ackt" e "acks" per la simulazione degli allarmi exPV::ft.installReadFunc ("ackt", &exPV::getAckt); exPV::ft.installReadFunc ("acks", &exPV::getAcks); // exPV::ft.installReadFunc ("timeStamp", &exPV::getTimeStamp); exPV::hasBeenInitialized = 1; } caStatus exPV::getAckt (gdd & prec) { prec.put(info.ackt); return S_cas_success; } caStatus exPV::getAcks (gdd & prec) { // printf("exPV::getAcks\n"); prec.put(info.acks); return S_cas_success; } /*caStatus exPV::getTimeStamp (gdd & prec) { prec.put(info.timestamp); return S_cas_success; } */ // // exPV::getPrecision() // caStatus exPV::getPrecision ( gdd & prec ) { prec.put(info.prec); 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; } // high alarm -> HIHI field caStatus exPV::getHighAlarm ( gdd & value ) { value.put(info.getHihi()); return S_cas_success; } // low alarm -> LOLO field caStatus exPV::getLowAlarm ( gdd & value ) { value.put(info.getLolo()); return S_cas_success; } // high warning alarm -> HIGH field caStatus exPV::getHighWarning ( gdd & value ) { value.put(info.getHigh()); return S_cas_success; } // low warning alarm -> LOW field caStatus exPV::getLowWarning ( gdd & value ) { value.put(info.getLow()); return S_cas_success; } // // exPV::getUnits() // caStatus exPV::getUnits( gdd & units ) { // aitString str("furlongs", aitStrRefConstImortal); // units.put(str); units.put(info.getUnits()); 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) // // CALLED by CA mentre update e chiamata anche dal thread automatico di EPICS caStatus exPV::write ( const casCtx &, const gdd & valueIn ) { /* apptype checking is required : array type do not have * alarms (at least I have to check also this). * * exPV::write -> exPV::update -> exPV::updateValue (exScalarPV or exVectorPV) * meglio qui.. * ca_array_put can only *put* base application type and acks/ackt */ //printf("exPV::write application type %d [ACKT = %d .app, ACKS = %d .app]\n", /* printf("exPV::write application type %d [ACKT = %d .app %hd, ACKS = %d .app %hd]\n", valueIn.applicationType(), DBR_PUT_ACKT, gddDbrToAit[DBR_PUT_ACKT].app, DBR_PUT_ACKS, gddDbrToAit[DBR_PUT_ACKS].app ); */ // printf("exPV::app %d \n", valueIn.applicationType() ); //do not work ! :-| :-( very sad if ( valueIn.applicationType() == gddDbrToAit[DBR_PUT_ACKT].app) { return this->putAckt( valueIn ); } else if (valueIn.applicationType() == gddDbrToAit[DBR_PUT_ACKS].app) { //else if (valueIn.applicationType() == 20) { return this->putAcks( valueIn ); } else return this->update ( valueIn, true, true ); } // FROM dbAccess.c //static long putAckt(DBADDR *paddr, const unsigned short *pbuffer, long nRequest, long no_elements, long offset) caStatus exPV::putAckt ( const gdd & valueIn ) { //dbCommon *precord = paddr->precord; aitUint16 ack_value; valueIn.get(ack_value); //if (*pbuffer == precord->ackt) return 0; if (ack_value == this->info.ackt) return 0; // 0 is success this->info.ackt = (aitEnum)ack_value; //db_post_events(precord, &precord->ackt, DBE_VALUE | DBE_ALARM); // I do not know how to do when not in a record if (!this->info.ackt && (this->info.acks > (aitEnum) this->pValue->getSevr()) ) { this->info.acks = (aitEnum)this->pValue->getSevr(); //db_post_events(precord, &precord->acks, DBE_VALUE | DBE_ALARM); } //do a post event to people monitoring alarm changes... //db_post_events(precord, NULL, DBE_ALARM); caServer * pCAS = this->getCAS(); casEventMask select ( pCAS->alarmEventMask()); this->postEvent ( select, *this->pValue ); return 0; } // FROM dbAccess.c //static long putAcks(DBADDR *paddr, const unsigned short *pbuffer, long nRequest, long no_elements, long offset) caStatus exPV::putAcks ( const gdd & valueIn ) { //dbCommon *precord = paddr->precord; aitUint16 ack_value; valueIn.get(ack_value); //if (*pbuffer >= precord->acks) { if (ack_value >= this->info.acks ) { this->info.acks = (aitEnum) 0; //db_post_events(precord, NULL, DBE_ALARM); caServer * pCAS = this->getCAS(); casEventMask select ( pCAS->alarmEventMask()); this->postEvent ( select, *this->pValue ); // db_post_events(precord, &precord->acks, DBE_VALUE | DBE_ALARM); // wait to implement records } return 0; // return success } // // 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; }