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; }