V4 Data Interface
From EPICSWIKI
Revision as of 22:23, 27 July 2005 by KayKasemir (talk | contribs)
Issue
I think the following data interface is easier to understand than the data access approach.
Am I wrong, because Data Access is really much easier?
Am I right, but the approach shown here will never work?
Would it work, but performance would be unacceptable?
Data Interface Classes
// Description of basic data character
enum EpicsDataCharacter
{
Octet, // Raw bits,known to a server/client pair, but no one else
Bool,
Integer, // Discrete
Real, // Floating-point
String, // UTF-8
Enum, // >=0 Integer with state names
Struct,
TimeStamp // For efficiency?
};
// Full description of the data
class EpicsDataDescriptor
{
public:
virtual EpicsDataCharacter getType() = 0;
virtual int getNumDims() = 0; // Scalar, vector: 1, matrix: >1
virtual int getSize(int dim) = 0; // Scalar: 1
virtual int getBitsize() = 0; // bits of integer or real
virtual bool isSigned() = 0; // Integer might be unsigned
};
// Access to the data. Two options:
// 1) Via basic types of the language, contents will differ for Java, C++.
// (shown here)
// 2) Locked types:
// C++ will probably define the full set
// int_8_t, int16_t, ...
// and Java can only handle a sub-set.
class EpicsDataReader : public EpicsDataDescriptor
{
public:
// Get data as type XX as best as possible.
// Example: getDouble returns true for Bool (d=0.0, 1.0), Integer, Real, Enum.
virtual bool getDouble(double &d) = 0;
virtual bool getArrayDouble(int element, double &d) = 0;
virtual bool getMatrixDouble(int dim, int element, double &d) = 0;
virtual bool getInt(int &d) = 0;
virtual bool getString(char *buf, int buf_len) = 0;
virtual bool getCstring(const char *&c_ptr) = 0;
/// many more, at least one for each supported type.
/// For arrays, it could include per-element access
/// as well as accessors that copy pieces into client buffers.
};
// "Property" has a name and data reader interface
class EpicsProperty : public EpicsDataReader
{
public:
virtual const char *getName() = 0;
};
// A catalog of properties
class EpicsPropertyCatalog
{
public:
virtual int getNumProperties() = 0;
virtual EpicsProperty *findProperty(const char *name) = 0;
virtual EpicsProperty *getProperty(int index) = 0;
/// Could also support name <-> ID conversions and
/// access via numeric ID.
};
C++ Implementation
// On the first read, skip to the 'Usage Example' below.
// This code isn't the greatest, it simply here to get
// example code that actually compiles & runs.
#include <string>
using std::string;
#include <string.h>
class EpicsPropertyBase : public EpicsProperty
{
public:
EpicsPropertyBase(const char *name)
{ this->name = name; }
// DataReader
bool getDouble(double &d)
{
int i;
if (getInt(i))
{
d = i;
return true;
}
return false;
}
bool getArrayDouble(int element, double &d)
{ return (element == 0) ? getDouble(d) : false; }
bool getMatrixDouble(int dim, int element, double &d)
{ return (dim == 0) ? getArrayDouble(element, d) : false; }
bool getInt(int &i)
{
double d;
if (getDouble(d))
{
i = (int)d;
return true;
}
return false;
}
bool getString(char *buf, int buf_len)
{
const char *ptr;
if (getCstring(ptr))
{
strncpy(buf, ptr, buf_len-1);
return true;
}
return false;
}
bool getCstring(const char *&c_ptr)
{
return false;
}
// EpicsProperty
const char *getName()
{ return name.c_str(); }
private:
string name;
};
class EpicsDoubleProperty : public EpicsPropertyBase
{
public:
EpicsDoubleProperty(const char *name, const double *p)
: EpicsPropertyBase(name)
{ this->p = p; }
// EpicsDataDescriptor
EpicsDataCharacter getType()
{ return Real; }
int getNumDims()
{ return 1; }
int getSize(int dim)
{ return dim==1 ? 1 : 0; }
int getBitsize()
{ return sizeof(double)*8; }
bool isSigned()
{ return true; }
// EpicsDataReader
bool getDouble(double &d)
{ d = *p; }
private:
const double *p;
};
class EpicsStringProperty : public EpicsPropertyBase
{
public:
EpicsStringProperty(const char *name, const string *p)
: EpicsPropertyBase(name)
{ this->p = p; }
// EpicsDataDescriptor
EpicsDataCharacter getType()
{ return String; }
int getNumDims()
{ return 1; }
int getSize(int dim)
{ return dim==1 ? p->length() : 0; }
int getBitsize()
{ return p->length()*8; }
bool isSigned()
{ return false; }
// EpicsDataReader
virtual bool getCstring(const char *&c_ptr)
{
c_ptr = p->c_str();
return true;
}
private:
const string *p;
};
Example for data known at compile-time
// This is the code that provides a property catalog.
// Again skip on the first read.
//
// All of this could be generated from DBD
class AiRecord : public EpicsPropertyCatalog
{
public:
AiRecord();
// Unsure if database code with pointer to the record
// should still directly access the data,
// or if the data should be protected/private.
double value;
string units;
// EpicsPropertyCatalog
int getNumProperties();
EpicsProperty *findProperty(const char *name);
EpicsProperty *getProperty(int index);
private:
EpicsProperty *property[2];
};
AiRecord::AiRecord()
{
property[0] = new EpicsDoubleProperty("value", &this->value);
property[1] = new EpicsStringProperty("units", &this->units);
}
int AiRecord::getNumProperties()
{ return sizeof(property)/sizeof(EpicsProperty *); }
EpicsProperty *AiRecord::findProperty(const char *name)
{ // could support hashed numeric IDs instead of strings
int i;
for (i=0; i<getNumProperties(); ++i)
if (!strcmp(property[i]->getName(), name))
return property[i];
return 0;
}
EpicsProperty *AiRecord::getProperty(int index)
{
return (index >=0 && index <sizeof(property)/sizeof(EpicsProperty *)) ?
property[index] : 0;
}
Usage Example
#include <stdio.h>
int main()
{
AiRecord fred;
// Record/device support might have direct access to the fields:
fred.value = 42.3;
fred.units = "a.u.";
// Other code uses PropertyCatalog
EpicsPropertyCatalog *pc = &fred;
// It can access known properties in chosen order
EpicsProperty *vp = pc->findProperty("value");
double d;
if (vp && vp->getDouble(d))
printf("Value = %.2f\n", d);
else
printf("Cannot get value as double\n");
// Or inspect unknown property catalogs
int i;
for (i=0; i<pc->getNumProperties(); ++i)
{
printf("%-10s = ", pc->getProperty(i)->getName());
switch (pc->getProperty(i)->getType())
{
case String:
const char *s;
if (pc->getProperty(i)->getCstring(s))
printf("'%s'\n", s);
else
printf("<cannot get string>\n");
break;
case Bool:
case Integer:
case Real:
double d;
if (pc->getProperty(i)->getDouble(d))
printf("%.2f\n", d);
else
printf("<cannot get double>\n");
break;
default:
printf("<unhandled type>\n");
}
}
return 0;
}