Files
ISTTOK/epics/css/sys-mng-opi/CSS/MARTe/Interfaces/EPICSLib/exPV.cpp
2019-10-21 16:02:55 +01:00

535 lines
13 KiB
C++

/*************************************************************************\
* 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> 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;
}