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

1397 lines
51 KiB
C++

/*
* Copyright 2011 EFDA | European Fusion Development Agreement
*
* Licensed under the EUPL, Version 1.1 or - as soon they
will be approved by the European Commission - subsequent
versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the
Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in
writing, software distributed under the Licence is
distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied.
* See the Licence for the specific language governing
permissions and limitations under the Licence.
*
* $Id$
*
**/
#include "EPICSHandler.h"
#include "Atomic.h"
#include "CDBExtended.h"
// EPICS version
#include "nciu.h"
#include "caProto.h"
#include "epicsVersion.h"
#include "epicsRelease.h"
// EPICS FileDescriptor Manager
#include <fdmgr.h>
#include <fdManager.h>
// EPICS pCAS GDD stuff
#include "gddAppFuncTable.h"
#include "smartGDDPointer.h"
#include "gddApps.h"
#include "aitTypes.h"
#include "FString.h"
#include "BasicTypes.h" // BasicTypeDescriptor
#include "Sleep.h"
#define DEFAULT_EPICS_DBG 0
#define DEFAULT_EPICS_HOPR 10.0
#define DEFAULT_EPICS_LOPR -10.0
#define DEFAULT_EPICS_LEN 1
#define DEFAULT_EPICS_PREC 4
//#define DEFAULT_EPICS_SCAN 1.0
#define DEFAULT_EPICS_SCAN 0
#define DEFAULT_EPICS_EGU ""
#define DEFAULT_EPICS_SYNC "excasIoSync"
#define DEFAULT_EPICS_ASYNC "excasIoAsync"
#define DEFAULT_EPICS_SCANON true
#define DEFAULT_EPICS_ASYNCSCAN true
#define DEFAULT_EPICS_ASYNCDELAY 0.1
#define DEFAULT_EPICS_MAXSIMULTASYNCIO 1000u
#define DEFAULT_EPICS_HYST 0.0
#define DEFAULT_EPICS_ADEL 0.0
#define DEFAULT_EPICS_MDEL 0.0
#define DEFAULT_EPICS_CPUMASK 0xFFFF
#define DEFAULT_EVENT_CPUMASK 0xFFFF
#define DEFAULT_BUFFER_SIZE 0xFFFF
#define DEFAULT_BUFFER_ALIGN 4
const char * EPICSHandler::tf_strings[] = { "false", "true" };
const int32 EPICSHandler::tf_values[] = { false, true };
/*
typedef enum {
aitEnumInvalid=0,
aitEnumInt8,
aitEnumUint8,
aitEnumInt16,
aitEnumUint16,
aitEnumEnum16,
aitEnumInt32,
aitEnumUint32,
aitEnumFloat32,
aitEnumFloat64,
aitEnumFixedString,
aitEnumString,
aitEnumContainer
} aitEnum;
*/
// TODO
//see base-3-14-11/src/gdd/aitTypes.c:41 aitName
//see base-3-14-11/src/gdd/aitTypes.h:131 ...
//aitSize aitName aitPrintf aitScanf
const char * EPICSHandler::aitEnum_strings[] = {
"aitEnumInvalid",
"aitEnumInt8",
"aitEnumUint8",
"aitEnumInt16",
"aitEnumUint16",
"aitEnumEnum16",
"aitEnumInt32",
"aitEnumUint32",
"aitEnumFloat32",
"aitEnumFloat64",
"aitEnumFixedString",
"aitEnumString",
"aitEnumContainer",
0 };
// the following must match epics/base/include/menuAlarmSevr.h
#include "menuAlarmSevr.h"
const char * EPICSHandler::menuAlarmSevr_strings[] = {
"NO_ALARM",
"MINOR",
"MAJOR",
"INVALID",
0 };
// the following must match epics/base/include/menuAlarmStat.h
#include "menuAlarmStat.h"
const char * EPICSHandler::menuAlarmStat_strings[] = {
"NO_ALARM",
"READ",
"WRITE",
"HIHI",
"HIGH",
"LOLO",
"LOW",
"STATE",
"COS",
"COMM",
"TIMEOUT",
"HWLIMIT",
"CALC",
"SCAN",
"LINK",
"SOFT",
"BAD_SUB",
"UDF",
"DISABLE",
"SIMM",
"READ_ACCESS",
"WRITE_ACCESS",
0 };
// the following must match epics/base/include/menuScan.h
#include "menuScan.h"
const char * EPICSHandler::menuScan_strings[] = {
"Passive",
"Event",
"I/O Intr",
"10 second",
"5 second",
"2 second",
"1 second",
".5 second",
".2 second",
".1 second",
0 };
const float EPICSHandler::menuScan_values[] = {
-1.0, -1.0, -1.0,
10.0, 5.0, 2.0, 1.0,
0.5, 0.2, 0.1,
0 };
// the following must match epics/base/include/menuYesNo.h
#include "menuYesNo.h"
const char * EPICSHandler::menuYesNo_strings[] = {
"NO",
"YES",
0 };
/*
enum excasIoType { excasIoSync, excasIoAsync };
*/
const char * EPICSHandler::excasIoType_strings[] = {
DEFAULT_EPICS_SYNC,
DEFAULT_EPICS_ASYNC
};
const int EPICSHandler::menu_values[] = {
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23
};
bool EPICSHandler::ObjectLoadSetup( ConfigurationDataBase &info,StreamInterface *err) {
// we use an Extended CDB in order to read double, strings and etc..
CDBExtended cdb(info);
int32 _i32;
if ( !GCNamedObject::ObjectLoadSetup(info,err) ) {
AssertErrorCondition(InitialisationError,
"EPICSHandler::ObjectLoadSetup: ?!?!: GCNamedObject::ObjectLoadSetup Failed");
return False;
}
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: Loading Process Variables",
Name());
// read the common prefix of all Process Variables (is not mandatory
if ( !cdb.ReadFString(pvPrefix, "PREFIX", "") ) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: PREFIX not specified. Assuming no prefix.",
Name());
}
if ( !cdb.ReadOptions(_i32, "scanOn", tf_strings, tf_values, DEFAULT_EPICS_SCANON) ) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: scanOn not specified. Assuming %d.",
Name(), DEFAULT_EPICS_SCANON);
}
scanOn = _i32;
if ( !cdb.ReadOptions(_i32, "asyncScan", tf_strings, tf_values, DEFAULT_EPICS_ASYNCSCAN) ) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: asyncScan not specified. Assuming %d.",
Name(), DEFAULT_EPICS_ASYNCSCAN);
}
asyncScan = _i32;
if ( !cdb.ReadDouble(asyncDelay, "asyncDelay", DEFAULT_EPICS_ASYNCDELAY) ) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: asyncDelay not specified. Assuming %f.",
Name(), DEFAULT_EPICS_ASYNCDELAY);
}
if ( !cdb.ReadInt32( _i32, "maxSimultAsyncIO", (int32)DEFAULT_EPICS_MAXSIMULTASYNCIO) ) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: maxSimultAsyncIO not specified. Assuming %d.",
Name(), DEFAULT_EPICS_MAXSIMULTASYNCIO);
}
maxSimultAsyncIO = _i32;
if ( !cdb.ReadInt32( _i32, "debugLevel", (int32)DEFAULT_EPICS_DBG) ) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: debugLevel not specified. Assuming %d.",
Name(), DEFAULT_EPICS_DBG);
}
debugLevel = _i32;
//PublishSubscriber Interface(TODO) configuration options
if ( !cdb.ReadInt32(cpuMask,"RunOnCPU", DEFAULT_EPICS_CPUMASK) ) {
AssertErrorCondition(Information,
"EPICShandler ::ObjectLoadSetup: %s: RunOnCPU not specified. Assuming %d.",
Name(), cpuMask);
}
if ( !cdb.ReadInt32(cpuMask_event,"RunOnCPU_event", DEFAULT_EVENT_CPUMASK) ) {
AssertErrorCondition(Information,
"EPICShandler ::ObjectLoadSetup: %s: RunOnCPU_event not specified. Assuming %d.",
Name(), cpuMask_event);
}
//buffers number, buffer must be allocated before event's thread allocation
if ( !cdb.ReadInt32(buffer_size,"BufferSize", DEFAULT_BUFFER_SIZE) ) {
AssertErrorCondition(Information,
"EPICShandler ::ObjectLoadSetup: %s: BufferSize not specified. Assuming %08x.",
Name(), buffer_size);
}
if ( !cdb.ReadInt32(buffer_align,"BufferAlign", DEFAULT_BUFFER_ALIGN) ) {
AssertErrorCondition(Information,
"EPICShandler ::ObjectLoadSetup: %s: BufferAlign not specified. Assuming %08x.",
Name(), buffer_align);
}
//create the array of Process Variable descriptors (pvInfo)
if ( !cdb->Move("ProcessVariable") ) {
AssertErrorCondition(InitialisationError,
"EPICSHandler::ObjectLoadSetup: %s: No Process Variable specified. Fatal error!",
Name());
return False;
}
numberOfPVs = cdb->NumberOfChildren();
if ( numberOfPVs == 0 ) {
AssertErrorCondition(InitialisationError,
"EPICSHandler::ObjectLoadSetup: %s: Number of signals is zero. Fatal error!",
Name());
return False;
}
pvList = new pvInfo* [numberOfPVs];
if ( pvList == NULL ) {
AssertErrorCondition(InitialisationError,
"EPICSHandler::ObjectLoadSetup: %s: Failed to allocate space for %d ProcessVariables. Fatal error!",
Name(), numberOfPVs);
return False;
}
//create the pCAS here and then attach the PV's
pCAS = new exServer( scanOn != 0, asyncScan== 0,
asyncDelay, maxSimultAsyncIO );
if ( !pCAS ) {
AssertErrorCondition(InitialisationError,
"EPICSHandler::ObjectLoadSetup: %s: Failed to allocate space for pCAS. Fatal error!",
Name() );
return False;
}
pCAS->setDebugLevel(debugLevel);
//read process variables properties loop
//TODO every PV must have it's own custom ObjectLoadSetup
//TODO at least do that for the DBR/DBF types
int i=0;
for(i=0; i<numberOfPVs; i++) {
cdb->MoveToChildren(i);
FString pvName;
if ( !cdb.ReadFString(pvName, "NAME") ) {
AssertErrorCondition(InitialisationError,
"EPICSHandler::ObjectLoadSetup: %s: NAME not specified. Fatal error!",
Name());
return False;
}
// ((char *)(pvName.Buffer()))[pvName.Size()] = 0;
FString pvType;
aitEnum aitType;
if ( !cdb.ReadFString(pvType, "TYPE") ) {
AssertErrorCondition(InitialisationError,
"EPICSHandler::ObjectLoadSetup: %s: TYPE not specified. Fatal error!",
Name());
return False;
}
// convert to ait type
aitType = ConvertToaitEnum( pvType.Buffer() );
FString pvSync;
excasIoType excasSync;
if ( !cdb.ReadFString(pvSync, "SYNC", DEFAULT_EPICS_SYNC) ) {
AssertErrorCondition(InitialisationError,
"EPICSHandler::ObjectLoadSetup: %s: TYPE not specified. Assuming %s",
Name(), DEFAULT_EPICS_SYNC);
}
// convert to excasIoType
excasSync = ConvertToexcasIoType( pvSync.Buffer() );
unsigned pvElements;
if ( !cdb.ReadInt32(_i32, "LEN", (int32)DEFAULT_EPICS_LEN) ) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: LEN not specidfied. Assuming %d",
Name(), pvElements);
}
pvElements = _i32;
/* previous implementation
double pvScanPeriod;
if ( !cdb.ReadDouble(pvScanPeriod, "SCAN", DEFAULT_EPICS_SCAN)) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: SCAN not specified. Assuming %f",
Name(), DEFAULT_EPICS_SCAN);
}
*/
if( !cdb.ReadOptions(_i32, "SCAN", menuScan_strings, menu_values, DEFAULT_EPICS_SCAN) ) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: SCAN not specified. Assuming \"%s\"",
Name(), menuScan_strings[DEFAULT_EPICS_SCAN]);
}
float pvScanPeriod = menuScan_values[_i32];
FString pvUnits;
if ( !cdb.ReadFString(pvUnits, "EGU", DEFAULT_EPICS_EGU) ) {
AssertErrorCondition(InitialisationError,
"EPICSHandler::ObjectLoadSetup: %s: EGU not specified. Assuming \"%s\"",
Name(), DEFAULT_EPICS_EGU);
}
// ((char *)(pvUnits.Buffer()))[pvUnits.Size()] = 0;
// graphical limits "High/Low OPerational Range"
double pvHopr;
if(!cdb.ReadDouble(pvHopr, "HOPR", DEFAULT_EPICS_HOPR)) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: HOPR not specified. Assuming %f",
Name(), DEFAULT_EPICS_HOPR);
}
double pvLopr;
if ( !cdb.ReadDouble(pvLopr, "LOPR", DEFAULT_EPICS_LOPR)) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: LOPR not specified. Assuming %f",
Name(), DEFAULT_EPICS_LOPR);
}
// alarm limits
// the policy is to set alarms to OPerational Ranges if not specified
double pvHihi; //Hihi Alarm Limit
if(!cdb.ReadDouble(pvHihi, "HIHI", pvHopr)) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: HIHI not specified. Assuming %f",
Name(), pvHopr);
}
double pvHigh; //High Alarm Limit
if(!cdb.ReadDouble(pvHigh, "HIGH", pvHopr)) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: HIGH not specified. Assuming %f",
Name(), pvHopr);
}
double pvLow; //Low Alarm Limit
if ( !cdb.ReadDouble(pvLow, "LOW", pvLopr)) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: LOW not specified. Assuming %f",
Name(), pvLopr);
}
double pvLolo; //Lolo Alarm Limit
if ( !cdb.ReadDouble(pvLolo, "LOLO", pvLopr)) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: LOLO not specified. Assuming %f",
Name(), pvLopr);
}
// menu alarm severity
int32 pvHhsv; //Hihi Alarm Severity
if( !cdb.ReadOptions(pvHhsv, "HHSV", menuAlarmSevr_strings, menu_values, 0) ) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: HHSV not specified. Assuming %s",
Name(), menuAlarmSevr_strings[pvHhsv] );
}
int32 pvHsv; //High Alarm Severity
if( !cdb.ReadOptions(pvHsv, "HSV", menuAlarmSevr_strings, menu_values, 0) ) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: HSV not specified. Assuming %s",
Name(), menuAlarmSevr_strings[pvHsv] );
}
int32 pvLsv; //Low Alarm Severity
if( !cdb.ReadOptions(pvLsv, "LSV", menuAlarmSevr_strings, menu_values, 0) ) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: LSV not specified. Assuming %s",
Name(), menuAlarmSevr_strings[pvLsv] );
}
int32 pvLlsv; //Lolo Alarm Severity
if( !cdb.ReadOptions(pvLlsv, "LLSV", menuAlarmSevr_strings, menu_values, 0) ) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: LLSV not specified. Assuming %s",
Name(), menuAlarmSevr_strings[pvLlsv] );
}
// hysteresis/deadband values
double pvHyst; // Hysteresis Alarm deadband
if ( !cdb.ReadDouble(pvHyst, "HYST", DEFAULT_EPICS_HYST)) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: HYST not specified. Assuming %f",
Name(), pvHyst);
}
double pvAdel; // Archive deadband
if ( !cdb.ReadDouble(pvAdel, "ADEL", DEFAULT_EPICS_ADEL)) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: ADEL not specified. Assuming %f",
Name(), pvAdel);
}
double pvMdel; // Monitor deadband
if ( !cdb.ReadDouble(pvMdel, "MDEL", DEFAULT_EPICS_MDEL)) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: MDEL not specified. Assuming %f",
Name(), pvMdel);
}
// read precision (realted only to ai/ao records)
int32 pvPrec;
if ( !cdb.ReadInt32(pvPrec, "PREC", DEFAULT_EPICS_PREC) ) {
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: PREC not specidfied. Assuming %d",
Name(), pvPrec);
}
//--------------------------------------------------------------------- end of EPICS stuff
// we have to allocate the memory for the pv's name (TODO modify pvInfo or dealloc)
//FString * pvNameObj = new FString(pvName); //TODO deallocate this space -> DONE
//FString * pvUnitsObj = new FString(pvUnits); //TODO deallocate this space -> DONE
// create the pv's descriptor pvInfo
pvInfo *pPVI = new pvInfo(pvScanPeriod, // scan period
pvName.Buffer(), // name of the pv (will be copied)
pvUnits.Buffer(), // units of the pv (will be copied)
(aitFloat32) pvHopr, (aitFloat32) pvLopr, // HOPR and LOPR, gr=graphical
pvHihi, pvHigh, pvLow, pvLolo,
aitType, excasSync, pvElements);
if ( !pPVI ) {
AssertErrorCondition(InitialisationError,
"EPICSHandler::ObjectLoadSetup: %s: cannot create pvInfo structure for %s. Fatal error!",
Name(), pvName.Buffer() );
return False;
}
// create the real process variable
exPV * pPV = pPVI->createPV(*pCAS, true, scanOn, asyncDelay);
if (!pPV) {
AssertErrorCondition(InitialisationError,
"EPICSHandler::ObjectLoadSetup: %s: cannot create exPV structure for %s. Fatal error!",
Name(), pPVI->getName() );
return False;
}
// Install canonical (root) name
char pvAlias[256];
const char * const pNameFmtStr = "%.100s%.20s";
sprintf(pvAlias, pNameFmtStr, pvPrefix.Buffer(), pPVI->getName());
pCAS->installAliasName(*pPVI, pvAlias);
// add the process variable to the list by copy and delete the current object
pvList[i] = pPVI; // copy the process variable to the list (by pointer)
//--------------------------------------------------------------------- end of EPICS stuff
cdb->MoveToFather();
} // PVs loop
cdb->MoveToFather();
// Publish Subscribe Interface(TODO) stuff
if ( subList )
delete subList;
subList = new subscriber [numberOfPVs];
if ( !subList ) {
AssertErrorCondition(InitialisationError,
"EPICSHandler::ObjectLoadSetup: %s: cannot create subList. Fatal error!",
Name() );
return False;
}
// TODO is this the correct way to initialize the array?!
memset ( subList, 0, (numberOfPVs * sizeof(subscriber)) );
// preparing the buffering infrastructure - this part will be moved in the abstract class
// in the Process Variable loop we can calculate the minimum size required to work
unsigned _size = buffer_size % buffer_align;
if ( _size ) {
buffer_size += buffer_align - _size;
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: circular buffer resized to %x (aligned)",
Name(), buffer_size);
}
// check if buffer was previously allocated
if ( buffer_ptr )
delete [] buffer_ptr;
buffer_ptr = new char [buffer_size];
if ( !buffer_ptr ) {
AssertErrorCondition(InitialisationError,
"EPICSHandler::ObjectLoadSetup: %s: cannot allocate space for the circular buffer. Fatal error!",
Name() );
return False;
}
// set head and tail and free bytes
buffer_head = (cbHeader * ) buffer_ptr;
buffer_tail = (cbHeader * ) buffer_ptr;
buffer_free = buffer_size;
// ***** circular buffer rules *****
// head \E8 sempre in una posizione libera (quando e full?)
// tail \E8 sempre nel primo elemento da mangiare tranne quando il ciruclar buffer e vuoto
// check if the threads are already running: kill them and then reload them
// TODO
// in a single MARTe instance must exists only one instance of the "callback" thread
// but you must register one instance of "callback_event" for every instance of this class
// create the asynchronous EPICS thread --> from EPICS to MARTe
callback_finalize++;
threadID = Threads::BeginThread( (ThreadFunctionType) callback,
pCAS, THREADS_DEFAULT_STACKSIZE,
"EPICSHandler", XH_NotHandled, cpuMask);
if ( !threadID ) {
AssertErrorCondition(InitialisationError,
"EPICSHandler::ObjectLoadSetup: %s: cannot create EpicsThreadCallback. Fatal error!",
Name() );
return False;
}
/* AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: Service started with tid 0x%08x.",
Name(), threadID);
*/
// create the asynchronous MARTe thread --> from MARTe to EPICS
callback_event_finalize++;
threadID_event = Threads::BeginThread( (ThreadFunctionType) callback_event,
this, THREADS_DEFAULT_STACKSIZE,
"EPICSHandler_events", XH_NotHandled, cpuMask_event);
if ( !threadID_event ) {
AssertErrorCondition(InitialisationError,
"EPICSHandler::ObjectLoadSetup: %s: cannot create eventBufferCallback. Fatal error!",
Name() );
return False;
}
/* AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: Service started with tid 0x%08x.",
Name(), threadID_event);
*/
setup_complete = true;
AssertErrorCondition(Information,
"EPICSHandler::ObjectLoadSetup: %s: Loaded %d Process Variables.",
Name(), numberOfPVs);
return True;
}
//----------------------------------------------------------------------------- end ObjectLoadSetup
// TODO use CDBExtended::ReadOptions instead
aitEnum EPICSHandler::ConvertToaitEnum(const char * s) {
int i;
for (i=0; i<(sizeof(aitEnum_strings)/sizeof(char*)); i++ ) {
if ( strcmp(aitEnum_strings[i], s)==0 )
//if ( strcasecmp(aitEnum_string[i], s)==0 )
return (aitEnum)i;
}
return aitEnumInvalid; //on error returns 0 (aitEnumInvalid)
}
//----------------------------------------------------------------------------- end ConvertToaitEnum
// TODO use CDBExtended::ReadOptions instead
excasIoType EPICSHandler::ConvertToexcasIoType(const char * s) {
int i;
for (i=0; i<(sizeof(excasIoType_strings)/sizeof(char*)); i++ ) {
if ( strcmp(excasIoType_strings[i], s)==0 )
//if ( strcasecmp(aitEnum_string[i], s)==0 )
return (excasIoType)i;
}
return excasIoSync; //on error returns 0 (excasIoSync)
}
//----------------------------------------------------------------------------- end ConvertToexcasIoType
int EPICSHandler::callback_finalize = 0;
void EPICSHandler::callback (void * args) {
//osiTime delay(1000u, 0u);
// TODO ?
while( callback_finalize )
fileDescriptorManager.process(0.1); // double seconds (timeout)
}
//----------------------------------------------------------------------------- end EPICSCallback
#define SYNCHRONIZING
int EPICSHandler::callback_event_finalize= 0;
void EPICSHandler::callback_event (void * args) {
unsigned id;
unsigned timestamp;
int _ret;
EPICSHandler * THIS = static_cast<EPICSHandler *>(args);
epicsTimeStamp current = epicsTime::getCurrent();
while( callback_event_finalize ) {
// if there are no subscribers wait a bit and continue loop
if ( !(THIS->subListSize) ) {
SleepMsec(100);
continue;
}
// collect data from the producer
_ret = THIS->get ( id, THIS->subBuffer, THIS->subSize, timestamp);
if ( _ret == 0 ) // no data, no problem
continue;
if ( _ret == -1 ) { // error
CStaticAssertErrorCondition(FatalError,
"EPICSHandler::callback_event: thread: get returns %d",
_ret );
// TODO read buffer_err to write which kind of error happens
break; }
// check id
if ( !(id < THIS->numberOfPVs) ) { // error again
CStaticAssertErrorCondition(Warning,
"EPICSHandler::callback_event: thread: id %d not in PVs range (0..%d)",
id, THIS->numberOfPVs);
continue; }
// conversion is done by EPICS side (i.e. GDD)
smartGDDPointer pDD;
switch ( (THIS->subList[id].type).Type() ) {
case BTDTInteger:
//TODO switch on flag
switch ( (THIS->subList[id].type).BitSize() ) {
/* not supported by the DDB
* case 8:
if ( (THIS->subList[id].count) == 1 ) // scalar
pDD = new gddScalar (gddAppType_value, aitEnumInt8);
else // --------------------------- vector
pDD = new gddAtomic (gddAppType_value, aitEnumInt8, 1u, THIS->subList[id].count);
break;
case 16:
if ( (THIS->subList[id].count) == 1 ) // scalar
pDD = new gddScalar (gddAppType_value, aitEnumInt16);
else // --------------------------- vector
pDD = new gddAtomic (gddAppType_value, aitEnumInt16, 1u, THIS->subList[id].count);
break;
*/
case 32:
switch ( (THIS->subList[id].type).Flags() ) {
case BTDSTUnsigned:
if ( (THIS->subList[id].count) == 1 ) // scalar
pDD = new gddScalar (gddAppType_value, aitEnumUint32);
else // --------------------------- vector
pDD = new gddAtomic (gddAppType_value, aitEnumUint32, 1u, THIS->subList[id].count);
break;
case BTDSTNone:
if ( (THIS->subList[id].count) == 1 ) // scalar
pDD = new gddScalar (gddAppType_value, aitEnumInt32);
else // --------------------------- vector
pDD = new gddAtomic (gddAppType_value, aitEnumInt32, 1u, THIS->subList[id].count);
break;
default:
BString bsbuf;
CStaticAssertErrorCondition(Warning,
"EPICSHandler::callback_event: thread: BTDTInteger, %s BasicTypeDescriptor not supported for id %d (%s) (32bit)",
(THIS->subList[id].type).ConvertToString(bsbuf), id,
(THIS->subList[id].count > 1) ? "array" : "scalar" );
}
break;
default:
// in this case it is EPICS that does not support Int64 (MARTe supports it already)
BString bsbuf;
CStaticAssertErrorCondition(Warning,
"EPICSHandler::callback_event: thread: BTDTInteger, %s BasicTypeDescriptor not supported for id %d (%s)",
(THIS->subList[id].type).ConvertToString(bsbuf), id,
(THIS->subList[id].count > 1) ? "array" : "scalar" );
}
break;
case BTDTFloat:
switch ( (THIS->subList[id].type).BitSize() ) {
case 32:
if ( (THIS->subList[id].count) == 1 ) // scalar
pDD = new gddScalar (gddAppType_value, aitEnumFloat32);
else // --------------------------- vector
pDD = new gddAtomic (gddAppType_value, aitEnumFloat32, 1u, THIS->subList[id].count);
break;
case 64:
if ( (THIS->subList[id].count) == 1 ) // scalar
pDD = new gddScalar (gddAppType_value, aitEnumFloat64);
else // --------------------------- vector
pDD = new gddAtomic (gddAppType_value, aitEnumFloat64, 1u, THIS->subList[id].count);
break;
default:
BString bsbuf;
CStaticAssertErrorCondition(Warning,
"EPICSHandler::callback_event: thread: BTDTFloat, %s BasicTypeDescriptor not supported for id %d (%s)",
(THIS->subList[id].type).ConvertToString(bsbuf), id,
(THIS->subList[id].count > 1) ? "array" : "scalar" );
}
break;
default:
// error not supported by the driver
BString bsbuf;
CStaticAssertErrorCondition(Warning,
"EPICSHandler::callback_event: thread: %s BasicTypeDescriptor not supported for id %d (%s)",
(THIS->subList[id].type).ConvertToString(bsbuf), id,
(THIS->subList[id].count > 1) ? "array" : "scalar" );
continue;
} //------------------------------------------------------------------- end switch ( (THIS->subList[id].type).Type() )
if ( !pDD.valid() ) {
CStaticAssertErrorCondition(Warning,
"EPICSHandler::callback_event: thread: pDD not valid cannot continue (id is %d, ->count is %d)",
id, THIS->subList[id].count);
continue;
}
gddStatus gdds= pDD->unreference ();
if ( gdds ) {// assert ( ! gddStatus );
CStaticAssertErrorCondition(Warning,
"EPICSHandler::callback_event: thread: assert (!gddStatus) cannot continue"
);
continue;
}
// copy data scalar or array
switch ( (THIS->subList[id].type).Type() ) {
case BTDTInteger:
switch ( (THIS->subList[id].type).BitSize() ) {
/* THE FOLLOWINGS ARE NOT SUPPORTED BY the DDB :-(
* case 8:
if ( (THIS->subList[id].count) == 1 ) // scalar
pDD->put( (aitInt8) * (aitInt8 *)(THIS->subBuffer) );
else { //-------------------------- vector
aitInt8 * pI8 = new aitInt8 [THIS->subList[id].count];
if ( !pI8 )
goto assert_allocation_error;
// create destructor
int8Destructor * pBTDFd = new int8Destructor;
if ( !pBTDFd ) {
delete [] pI8;
goto assert_destructor_error;
}
// set the data
pDD->putRef (pI8, pBTDFd);
// copy array data
memcpy(pI8, THIS->subBuffer, THIS->subList[id].size);
}
break;
case 16:
if ( (THIS->subList[id].count) == 1 ) // scalar
pDD->put( (aitInt16) * (aitInt16 *)(THIS->subBuffer) );
else { //-------------------------- vector
aitInt16 * pI16 = new aitInt16 [THIS->subList[id].count];
if ( !pI16 )
goto assert_allocation_error;
// create destructor
int16Destructor * pBTDFd = new int16Destructor;
if ( !pBTDFd ) {
delete [] pI16;
goto assert_destructor_error;
}
// set the data
pDD->putRef (pI16, pBTDFd);
// copy array data
memcpy(pI16, THIS->subBuffer, THIS->subList[id].size);
}
break;
*/
case 32:
switch ( (THIS->subList[id].type).Flags() ) {
case BTDSTUnsigned:
if ( (THIS->subList[id].count) == 1 ) // scalar
pDD->put( (aitUint32) * (aitUint32 *)(THIS->subBuffer) );
else { //-------------------------- vector
aitUint32 * pI32 = new aitUint32 [THIS->subList[id].count];
if ( !pI32 )
goto assert_allocation_error;
// create destructor
int32Destructor * pBTDFd = new int32Destructor;
if ( !pBTDFd ) {
delete [] pI32;
goto assert_destructor_error;
}
// set the data
pDD->putRef (pI32, pBTDFd);
// copy array data
memcpy(pI32, THIS->subBuffer, THIS->subList[id].size);
}
break;
case BTDSTNone:
if ( (THIS->subList[id].count) == 1 ) // scalar
pDD->put( (aitInt32) * (aitInt32 *)(THIS->subBuffer) );
else { //-------------------------- vector
aitInt32 * pI32 = new aitInt32 [THIS->subList[id].count];
if ( !pI32 )
goto assert_allocation_error;
// create destructor
int32Destructor * pBTDFd = new int32Destructor;
if ( !pBTDFd ) {
delete [] pI32;
goto assert_destructor_error;
}
// set the data
pDD->putRef (pI32, pBTDFd);
// copy array data
memcpy(pI32, THIS->subBuffer, THIS->subList[id].size);
}
break;
//default:
}
break;
//default:
}
break;
case BTDTFloat:
switch ( (THIS->subList[id].type).BitSize() ) {
case 32:
if ( (THIS->subList[id].count) == 1 ) // scalar
pDD->put( (aitFloat32) * (aitFloat32 *)(THIS->subBuffer) );
else { //-------------------------- vector
aitFloat32 * pF32 = new aitFloat32 [THIS->subList[id].count];
if ( !pF32 )
goto assert_allocation_error;
// create destructor
float32Destructor * pBTDFd = new float32Destructor;
if ( !pBTDFd ) {
delete [] pF32;
goto assert_destructor_error;
}
// set the data
pDD->putRef (pF32, pBTDFd);
// copy array data
memcpy(pF32, THIS->subBuffer, THIS->subList[id].size);
}
break;
case 64:
if ( (THIS->subList[id].count) == 1 ) // scalar
pDD->put( (aitFloat64) * (aitFloat64 *)(THIS->subBuffer) );
else { //-------------------------- vector
aitFloat64 * pF64 = new aitFloat64 [THIS->subList[id].count];
if ( !pF64 )
goto assert_allocation_error;
// create destructor
float64Destructor * pBTDFd = new float64Destructor;
if ( !pBTDFd ) {
delete [] pF64;
goto assert_destructor_error;
}
// set the data
pDD->putRef (pF64, pBTDFd);
// copy array data
memcpy(pF64, THIS->subBuffer, THIS->subList[id].size);
}
break;
}
break;
default:
CStaticAssertErrorCondition(FatalError,
"EPICSHandler::callback_event: static method: %d This point cannot be reached",
id );
continue;
}
goto update;
assert_allocation_error: {
BString bsbuf;
CStaticAssertErrorCondition(Warning,
"EPICSHandler::callback_event: static method: assert array allocation error %s for id %d",
(THIS->subList[id].type).ConvertToString(bsbuf), id );
continue;
}
assert_destructor_error: {
BString bsbuf;
CStaticAssertErrorCondition(Warning,
"EPICSHandler::callback_event: static method: assert array destructor error %s for id %d",
(THIS->subList[id].type).ConvertToString(bsbuf), id );
continue;
}
update:
//convert to EPICS timestamp (DDB time is usec)
long nsec = current.nsec + ((timestamp % 1000000) *1000);
long sec = nsec / 1000000000;
if (current.nsec < nsec)
sec++;
sec += current.secPastEpoch;
//add timestamp
aitTimeStamp gddts ( sec , nsec);
pDD->setTimeStamp ( & gddts );
// update value!
//status = (pvList[id]->getPV())->update ( *pDD );
//if ( !(THIS->pvList[id]->getScanPeriod() > 00) )
(THIS->pvList[id]->getPV())->update ( *pDD,
(THIS->pvList[id]->getScanPeriod() > 00) ? false : true,
true );
} //----------------------------------------------------------------------- end while ( callback_event_finalize )
}//---------------------------------------------------------------------------- end MARTeCallback
bool EPICSHandler::subscribe ( const char * nameIn, BasicTypeDescriptor typeIn, int countIn, unsigned &idOut) {
if ( !setup_complete )
return False;
// qui e gia stato aggiunto sia sulla lista che sull'hash table..
// ottimo per cerarlo by name e tornare l'id della lista.. (come ottenere l'id?)
char pvAlias[256];
const char * const pNameFmtStr = "%.100s%.20s";
pvExistReturn _exist = (this->pCAS)->pvExistTest(nameIn);
if ( _exist.getStatus() == pverDoesNotExistHere ) {
AssertErrorCondition(FatalError,
"EPICSHandler::subscribe: %s: Process Variable %s does NOT exist in the lookup table",
Name(), nameIn );
return False;
}
idOut = -1;
for (int i=0; i<numberOfPVs; i++) {
sprintf(pvAlias, pNameFmtStr, pvPrefix.Buffer(), pvList[i]->getName() );
if ( strcmp(nameIn, pvAlias) == 0) {//string match
idOut = i;
break;
}
}
if (idOut == -1) // variable not found -> return false
return False;
// Process Variable already subscribed? -> error
if ( subList[idOut].count != 0 ) {
AssertErrorCondition(ParametersError,
"EPICSHandler::subscribe: %s: Process Variable %s already subscribed",
Name(), nameIn);
return False;
}
// if typeIn has no type (i.e. BTDTNone) has no sense to subscribe on it or also to write it
if ( typeIn == BTDTNone ) {
AssertErrorCondition(ParametersError,
"EPICSHandler::subscribe: %s: Process Variable %s type BTDTNone not valid!",
Name(), nameIn);
return False;
}
// TODO the PV creation must be done here or in another place? avrebbe senso crearla
// solo se qualcuno ci si iscrive..
// altrimenti la registri cmoe eusbscirber e ne registri il tipo e quindi il numeo di byte da copiare
//ricordiamoci che countin e il numero di elementi dell'array.
// se facciamo che la PV viene creata solo se sottoscritta allora non serve avere una variabile
// per sapere se e sottoscritta (una sottoscrizione per variabile) se \E8 creata allora ok
// check if the buffer has enough space to hold the data container (+ header)
int _size = (typeIn.ByteSize() * countIn);
if ( buffer_size < (sizeof(cbHeader) + _size) ) {
AssertErrorCondition(ParametersError,
"EPICSHandler::subscribe: %s: Process Variable %s circular buffer is too small!",
Name(), nameIn);
return False;
}
// check if the process variable has at least the number of elements of the DDB array
// otherwise signal an error (EPICS code checks for that, so we maintain the same)
if ( countIn > pvList[idOut]->getElementCount() ) {
AssertErrorCondition(ParametersError,
"EPICSHandler::subscribe: %s: Process Variable %s holds %d elements, less then %d required",
Name(), nameIn, pvList[idOut]->getElementCount(), countIn);
return False;
}
subList[idOut].size = _size;
subList[idOut].type = typeIn;
subList[idOut].count = countIn;
subList[idOut].statisticGet = 0;
subList[idOut].statisticPut = 0;
subListSize++;
// if the buffer was allocated but is too small delete and allocate
if (subSize < _size) {
if ( subBuffer ) // consider when subBuffer is 0 (never allocated)
delete subBuffer;
subSize =_size;
subBuffer = (char *) malloc (subSize);
}
return True;
}
//----------------------------------------------------------------------------- end subscribe
bool EPICSHandler::unsubscribe (unsigned idIn) {
if ( !(idIn < numberOfPVs) )
return False; // index out of bounds
// TODO vedi sopra se decidiamo di aggiungerla al server solo quando facciamo subscribe allora
// qui in unsubscribe lo dobbiamo togliere dal server (che sarebbe la migliore idea)
// per il momento il thread \E8 always running!
if ( subList[idIn].count <= 0 ) {
AssertErrorCondition(ParametersError,
"EPICSHandler::subscribe: %s: Process Variable id %d is not subscribed!",
idIn, Name() );
return False; // no one is subscribed
}
subList[idIn].type = BTDTNone;
subList[idIn].count = 0;
subListSize--;
return True;
}
//----------------------------------------------------------------------------- end unsubscribe
// REALTIME - put -> head
int EPICSHandler::put (unsigned idIn, void * bufferIn, unsigned timestamp) {
if ( !(idIn < numberOfPVs) )
return -1; // index out of bounds
if ( !bufferIn )
return -1; // buffer is not a valid pointer
int _size = subList[idIn].size + sizeof(cbHeader);
if ( buffer_free < _size )
return 0; // check if there is room for data
// if there is no room the user can retry many times
// copy data to circular buffer handling cross bounding
cbHeader _tmpHeader = { CB_HEADER_MAGIC, idIn, timestamp};
long offset = (char *) buffer_head - buffer_ptr;
int len = (buffer_size - offset);
// write the header and data in the circular buffer
if ( len < sizeof(cbHeader) ) {
memcpy ((char *)buffer_head, (char *)&_tmpHeader, len ); // fill last bytes of the buffer
buffer_head = (cbHeader *) buffer_ptr; // rewind the buffer
memcpy ((char *)buffer_head, (((char *)&_tmpHeader) + len), sizeof(cbHeader) - len);
buffer_head = (cbHeader *) (buffer_ptr + (sizeof(cbHeader) - len));
// now I can copy the data , for sure not sliced
memcpy((char*)buffer_head, bufferIn, subList[idIn].size);
buffer_head = (cbHeader *)((char *)buffer_head + subList[idIn].size); // always subList .size
}
else {
memcpy ((char *)buffer_head, (char *)&_tmpHeader, sizeof(cbHeader) ); // fill last bytes of the buffer
buffer_head = (cbHeader *) (((char *)buffer_head) + sizeof(cbHeader));
if ( (len - sizeof(cbHeader)) == 0) {
buffer_head = (cbHeader *)buffer_ptr; // rewind the buffer
len = subList[idIn].size;
}
else
len -= sizeof(cbHeader);
//check if buffer must be sliced
if ( len < subList[idIn].size ) {
memcpy ((char *)buffer_head, bufferIn, len ); // fill last bytes of the buffer
buffer_head = (cbHeader *)buffer_ptr; // rewind the buffer
memcpy ((char *)buffer_head, ((char *)bufferIn + len), (subList[idIn].size - len) );
buffer_head = (cbHeader *)(buffer_ptr + (subList[idIn].size - len) );
}
else {
memcpy((char*)buffer_head, bufferIn, subList[idIn].size);
buffer_head = (cbHeader *)((char *) buffer_head + subList[idIn].size);
}
}
//buffer_free -= _size;
Atomic::Sub(&buffer_free, _size);
subList[idIn].statisticPut++;
//synchronizza EPICS !! semaphore del get! (next version)
#ifdef SYNCHRONIZING
sem.Post();
#endif
return subList[idIn].size;
}
//----------------------------------------------------------------------------- end put
/*
* you can use get with sizeIn 0 to query about the status of the first buffer
* in the sense you want know the id to provide the correct buffer size
*
* temporaneamente facciamo che:
* idOut viene scritto dalla routine "get" bufferIn dev'essere valido
* sizeIn != 0
* l'idea e che esiste un'ulteriore routine che ti dice la size e l'id per il momento
* e quindi passi il giusto buffer (con la giusta size) altrimenti
* allochi un max buffer in subscribe.. or something similar.. cmq relato agli iscritti..
*
* return the amount of bytes written - TODO make the same in method put
*
* if size is less then the buffer just fill the buffer and move to the next buffer
*/
// REALTIME - get <- get
int EPICSHandler::get (unsigned &idOut, void * bufferIn, unsigned sizeIn, unsigned &timestamp) {
if ( !bufferIn ) // buffer cannot be zero
return -1;
if ( !sizeIn ) // size cannot be zero
return -1;
// if there is no data in the buffer return 0
if ( buffer_free == buffer_size )
#ifdef SYNCHRONIZING
sem.Wait(); //ok, but we can also check for timeout here..
if ( buffer_free == buffer_size )
return 0;
#else
{
idOut = -1;
return 0;
}
#endif
cbHeader _tmpHeader = {0, 0, 0};
long offset = (char *) buffer_tail - buffer_ptr;
int len = (buffer_size - offset);
int dataLen = 0;
// fragmented header or payload around the circular buffer
if (len < sizeof(cbHeader)) {
memcpy ( (char *)&_tmpHeader, (char *)buffer_tail, len ); // fill last bytes of the buffer
buffer_tail = (cbHeader *)buffer_ptr; // rewind the buffer
memcpy ( (((char *)&_tmpHeader) + len), (char *)buffer_tail , (sizeof(cbHeader) - len) );
buffer_tail = (cbHeader *)((char*)buffer_tail + (sizeof(cbHeader) - len) );
//check data validity.. i.e. check MAGIC and id validity
if ( (_tmpHeader.magic != CB_HEADER_MAGIC) )
return -1; // corrupted buffer do nothing (nor realign or any thing else)
if ( !(_tmpHeader.id < numberOfPVs) )
return -1; // corrupted id number cannot do nothing because cannot infere size
idOut = _tmpHeader.id;
timestamp = _tmpHeader.timestamp;
//check how much data can be copied
dataLen = (sizeIn < subList[idOut].size) ? sizeIn : subList[idOut].size;
// now I can copy the data , for sure not sliced
memcpy((char*)bufferIn, buffer_tail, dataLen);
buffer_tail = (cbHeader *) ((char *)buffer_tail + subList[idOut].size);
}
else { // here at least the header is all together
memcpy ((char *)&_tmpHeader, (char *)buffer_tail, sizeof(cbHeader) ); // fill last bytes of the buffer
// check data validity.. MAGIC and id validity
if ( (_tmpHeader.magic != CB_HEADER_MAGIC) )
return -1; // corrupted buffer do nothing (nor realign or any thing else)
if ( !(_tmpHeader.id < numberOfPVs) )
return -1; // corrupted id number cannot do nothing because cannot infere size
buffer_tail = (cbHeader *) ((char *)buffer_tail + sizeof(cbHeader));
idOut = _tmpHeader.id;
timestamp = _tmpHeader.timestamp;
//check how much data can be copied
dataLen = (sizeIn < subList[idOut].size) ? sizeIn : subList[idOut].size;
len -= sizeof(cbHeader);
if ( len == 0) { // data is at the beginning of the data buffer
memcpy(bufferIn, (char*)buffer_ptr, dataLen);
buffer_tail = (cbHeader *)(buffer_ptr + subList[idOut].size);
}
else if ( len >= subList[idOut].size ) { // the data is contiguous
memcpy(bufferIn, (char*)buffer_tail, dataLen);
buffer_tail = (cbHeader *)((char *)buffer_tail + subList[idOut].size);
}
else { // segmented data
memcpy (bufferIn, (char *)buffer_tail, (dataLen > len) ? len : dataLen ); // fill last bytes of the buffer
buffer_tail = (cbHeader *)buffer_ptr; // rewind the buffer
memcpy (((char*)bufferIn + len), (char *)buffer_tail, (dataLen > len) ? (dataLen - len) : 0);
buffer_tail = (cbHeader *)(buffer_ptr + (subList[idOut].size -len));
}
}
//buffer_free += (sizeof(cbHeader) + subList[_tmpHeader.id].size); // one instruction beacause we hope to be atomic
int _size = (sizeof(cbHeader) + subList[_tmpHeader.id].size); // one instruction beacause we hope to be atomic
/*
int32 * atomic_access = &buffer_free;
int32 c, d;
c = *atomic_access;
d = Atomic::Exchange(atomic_access, (c+_size));
if (c != d) {
// something goes wrong
d = Atomic::Exchange(atomic_access, (d+_size));
c = (c+_size);
if (c != d) {
// something goes wrong again
AssertErrorCondition(FatalError,
"EPICSHandler::get: Circular Buffer Fatal error TWICE ERROR");
}
else
AssertErrorCondition(FatalError,
"EPICSHandler::get: Circular Buffer Fatal error ONE ERROR");
}
*/
Atomic::Add(&buffer_free, _size);
subList[idOut].statisticGet++;
// comment non e proprio necessario, i.e. buffer_head/buffer_tail dovrebbero venir modificate in
// nello stesso istante di buffer_free in modo tale che questo check funzioni
/*
char* a = (char*) buffer_head;
int b = buffer_free;
if ( ((((char*)buffer_tail -buffer_ptr)
+ (buffer_size -b)) % buffer_size) !=
(a -buffer_ptr) )
AssertErrorCondition(FatalError,
"EPICSHandler::get: Circular Buffer Fatal error _ptr %p, _head %p, _tail %p, size %d, free%d",
buffer_ptr, a, buffer_tail, buffer_size, b);
*/
// TODO invalidate cbHeader buffer
//free the space and mark magic as not valid 0x0000
return dataLen;
}
//----------------------------------------------------------------------------- end get
// TODO why not writing a buffer walker? :-)
const char* EPICSHandler::css = "table.bltable {"
"margin: 1em 1em 1em 2em;"
"background: whitesmoke;"
"border-collapse: collapse;"
"}"
"table.bltable th, table.bltable td {"
"border: 1px silver solid;"
"padding: 0.2em;"
"}"
"table.bltable th {"
"background: gainsboro;"
"text-align: left;"
"}"
"table.bltable caption {"
"margin-left: inherit;"
"margin-right: inherit;"
"}";
// ----------------------------------------------------------------------------
#define TABLE_NEWROW hStream.Printf("<tr>\n")
#define TABLE_ENDROW hStream.Printf("</tr>\n")
bool EPICSHandler::ProcessHttpMessage( HttpStream &hStream )
{
hStream.SSPrintf("OutputHttpOtions.Content-Type","text/html");
hStream.keepAlive = False;
hStream.Printf("<html><head><title>%s</title>", Name());
hStream.Printf( "<style type=\"text/css\">\n" );
hStream.Printf("%s\n", css);
hStream.Printf( "</style></head><body>\n" );
//the following version numeration comes from
//epics/base-3-14-11/src/cas/generic/caServerI.cc
//EPICS/base-3-14-11/configure/CONFIG_BASE_VERSION
//epics/base-3-14-11/src/misc/epicsRelease.c
hStream.Printf("<h1> EPICSLib Version </h1>\n");
hStream.Printf("Channel Access V%s\n", CA_VERSION_STRING ( CA_MINOR_PROTOCOL_REVISION ) );
hStream.Printf("<BR>\n");
// hStream.Printf("revision @(#) %s\n", EPICS_VERSION_STRING );
hStream.Printf("%s\n", epicsReleaseVersion );
hStream.Printf("<BR>\n");
hStream.Printf("<h1> EPICSLib Parameters </h1>\n");
hStream.Printf("<table class=\"bltable\">\n");
TABLE_NEWROW; hStream.Printf("<td> PV prefix </td> <td>%s</td>\n", pvPrefix.Buffer() ); TABLE_ENDROW;
TABLE_NEWROW; hStream.Printf("<td> scan on </td> <td>%s</td>\n", (scanOn==true) ? "true" : "false"); TABLE_ENDROW;
TABLE_NEWROW; hStream.Printf("<td> async scan on </td> <td>%s</td>\n", (asyncScan==true) ? "true" : "false"); TABLE_ENDROW;
TABLE_NEWROW; hStream.Printf("<td> async delay </td> <td>%lf</td>\n", asyncDelay); TABLE_ENDROW;
TABLE_NEWROW; hStream.Printf("<td> max simultaneous async </td> <td>%d</td>\n", maxSimultAsyncIO); TABLE_ENDROW;
TABLE_NEWROW; hStream.Printf("<td> PV number </td> <td>%d</td>\n", numberOfPVs); TABLE_ENDROW;
hStream.Printf("</table>\n");
hStream.Printf("<BR>\n");
// per process variable description
hStream.Printf("<h1> Detailed Process Variables description </h1>\n");
hStream.Printf("<table class=\"bltable\">\n");
// table header
TABLE_NEWROW; hStream.Printf("<td>PV</td> <td>NAME</td> <td>EGU</td> <td>TYPE</td> <td>SYNC</td> "
"<td>HOPR</td> <td>LOPR</td> <td>HIHI</td> <td>HIGH</td> <td>LOW</td> <td>LOLO</td> "
"<td>LEN</td> <td>SCAN</td>\n");
hStream.Printf("<td>PREC</td>"
"<td>HHSV</td> <td>HSV</td> <td>LSV</td> <td>LLSV</td> "
"<td>ACKS</td> <td>ACKT</td>");
TABLE_ENDROW;
for (int pv=0; pv<numberOfPVs; pv++) {
// build the PV alias (prefix + name)
char pvAlias[256];
const char * const pNameFmtStr = "%.100s%.20s";
sprintf(pvAlias, pNameFmtStr, pvPrefix.Buffer(), pvList[pv]->getName());
// output the HTTP data
TABLE_NEWROW;
hStream.Printf("<td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> " // pvAlias, pvName, pvUnits, pvType, pvSync
"<td>%lf</td> <td>%lf</td> <td>%lf</td> <td>%lf</td> <td>%lf</td> <td>%lf</td> " //hopr, lopr, hihi, high, low, lolo
"<td>%d</td>\n",
pvAlias,
pvList[pv]->getName(), pvList[pv]->getUnits(),
aitEnum_strings[ (unsigned) pvList[pv]->getType() ],
excasIoType_strings[ (unsigned) pvList[pv]->getIOType() ],
pvList[pv]->getHopr(), pvList[pv]->getLopr(),
pvList[pv]->getHihi(), pvList[pv]->getHigh(),
pvList[pv]->getLow(), pvList[pv]->getLolo(),
pvList[pv]->getElementCount() );
int index = 0;
do {
if ( menuScan_values[index] == pvList[pv]->getScanPeriod() )
break;
index++;
} while (menuScan_values[index] != 0);
if (menuScan_values[index] == 0)
index = 0;
hStream.Printf ("<td>%s</td>\n", menuScan_strings[index]);
/* TABLE_ENDROW;
// additional information regarding the same PV
TABLE_NEWROW;*/
hStream.Printf("<td>%d </td>"
"<td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> " // - - hhsv hsv lsv llsv
"<td>%s</td> <td>%s</td>\n", // acks ackt
pvList[pv]->prec,
menuAlarmSevr_strings[ pvList[pv]->hhsv ], menuAlarmSevr_strings[ pvList[pv]->hsv ],
menuAlarmSevr_strings[ pvList[pv]->lsv ], menuAlarmSevr_strings[ pvList[pv]->llsv ],
menuAlarmSevr_strings[ pvList[pv]->acks ], menuYesNo_strings [pvList[pv]->ackt ] );
TABLE_ENDROW;
}
hStream.Printf("</table>\n");
hStream.Printf("<BR>\n");
// per process variable description
hStream.Printf("<h1> Subscriber table (num of subscriber %d)</h1>\n", subListSize);
hStream.Printf("<table class=\"bltable\">\n");
// table header
TABLE_NEWROW; hStream.Printf("<td>index</td> <td>elements</td> <td>element size</td> <td>DDB type</td> "
"<td>statistic Get</td> <td>statistic Put</td>"); TABLE_ENDROW;
for (int si=0; si<numberOfPVs; si++) {
BString bsbuf;
TABLE_NEWROW;
hStream.Printf("<td> %d</td> <td>%d </td><td> %d</td> <td>%s </td> <td>%d </td> <td>%d </td>",
si, subList[si].count, subList[si].size,
(subList[si].size == 0) ? "not assigned" : (subList[si].type).ConvertToString(bsbuf),
subList[si].statisticGet, subList[si].statisticPut);
TABLE_ENDROW;
}
hStream.Printf("</table>\n");
hStream.Printf("<BR>\n");
hStream.Printf("</body></html>");
hStream.WriteReplyHeader(True);
return True;
}
//----------------------------------------------------------------------------- end ProcessHttpMessage
OBJECTLOADREGISTER(EPICSHandler,"$Id: EPICSHandler.cpp,v 1.1 2011/06/17 14:19:12 abarb Exp $")