150 likes | 261 Views
Field Support Ideas. Tech-talk, 19 July 2007. Good Afternoon,
E N D
Tech-talk, 19 July 2007 Good Afternoon, I have now for the <n>th time explained to some desperate EPICS newbie all the hoops one has to go through in order to create an array field for a new record type. This is starting to get on my nerves, the more so because I can't really tell them to go away and RTFM…. I dearly hope that with 3.15 there will be a dbd syntax for creating array fields. Ideally there should be a way to specify another field whose value at startup time determines the maximum number of elements. Life would be almost perfect if in addition the runtime number of elements could be specified in a similar way. It could be as simple as just one more (possible) property for a field value like, for instance, field(BLA,DBF_UCHAR) { ... array(NELM,NORD) } which might optionally allow a literal number for the first argument (for a fixed size array) and where the second argument is optional (for arrays that don't change their number of elements at runtime). This would be a tremendously useful addition and would come at very low cost. It is even backward compatible. Frankly, I am amazed that something like this hasn't been done a long time ago. Cheers Ben (Franksen)
How do we solve this problem? …start by looking at the record support
Record support entry table typedef struct rset { long number; long (*report)(FILE fp, struct dbCommon * pRec); long (*init)(void); long (*init_record)( struct dbCommon *, int pass ); long (*process)( struct dbCommon * ); long (*special)( const struct dbAddr *paddr, int after ); long (*get_value)( struct dbCommon *, struct valueDes *p ); long (*cvt_dbaddr)( struct dbAddr * paddr ); long (*get_array_info)( const struct dbAddr *paddr, long *no_elements, long *offset); long (*put_array_info)( const struct dbAddr *paddr, long nRequest ); long (*get_units)(const struct dbAddr *paddr, char *units); long (*get_precision)(const struct dbAddr *paddr, long *precision); long (*get_enum_str)(const struct dbAddr *paddr, char * pbuffer); long (*get_enum_strs)(const struct dbAddr *paddr, struct dbr_enumStrs *p); long (*put_enum_str)(const struct dbAddr *paddr, const char * pbuffer); long (*get_graphic_double)(const struct dbAddr *paddr, struct dbr_grDouble *pgd); long (*get_control_double)(const struct dbAddr *paddr, struct dbr_ctrlDouble *pcd); long (*get_alarm_double)(const struct dbAddr *paddr,struct dbr_alDouble *p); } rset;
Record support entry table typedef struct rset { long number; long (*report)(FILE fp, struct dbCommon * pRec); long (*init)(void); long (*init_record)( struct dbCommon *, int pass ); long (*process)( struct dbCommon * ); long (*special)( const struct dbAddr *paddr, int after ); long (*get_value)( struct dbCommon *, struct valueDes *p ); long (*cvt_dbaddr)( struct dbAddr * paddr ); long (*get_array_info)( const struct dbAddr *paddr, long *no_elements, long *offset); long (*put_array_info)( const struct dbAddr *paddr, long nRequest ); long (*get_units)(const struct dbAddr *paddr, char *units); long (*get_precision)(const struct dbAddr *paddr, long *precision); long (*get_enum_str)(const struct dbAddr *paddr, char * pbuffer); long (*get_enum_strs)(const struct dbAddr *paddr, struct dbr_enumStrs *p); long (*put_enum_str)(const struct dbAddr *paddr, const char * pbuffer); long (*get_graphic_double)(const struct dbAddr *paddr, struct dbr_grDouble *pgd); long (*get_control_double)(const struct dbAddr *paddr, struct dbr_ctrlDouble *pcd); long (*get_alarm_double)(const struct dbAddr *paddr, struct dbr_alDouble *p); } rset; All the highlighted routines take a field pointer, rather a record pointer.
Field Support • Provides default handling for common field type behaviours. • Methods provided by a field support entry table, similar to record support. • It is independent of the record type, and can take parameters passed in the dbd file. • Important to remember that parameters are per record type, not per record instance. • At this point, I think field support should only access other fields in the current record, but not other records. • In the current implementation, everything handled by offsets – types are checked at initialisation.
Progress so far • Made all the changes required in base to support field support. • Provided some helper routines to make field support easy to write. • Written a default field support implementation that does all the “normal” things you would expect, and could replace most of the cvt_dbaddr, get_array_info, put_array_info, get_units get_precision, get_graphic_double, get_control_double and get_alarm double routines in base.
Field support entry table typedef struct { long number; long (*report)(FILE fp, struct dbFldDes * dbFldDes); long (*init)(void); void * (*init_field)( struct dbBase * dbbase, struct dbFldDes *); long (*init_record)( struct dbCommon *, struct dbFldDes * dbFldDes, int pass ); long (*special)( const struct dbAddr *paddr, int after ); long (*cvt_dbaddr)( struct dbAddr * paddr ); long (*get_array_info)( const struct dbAddr *paddr, long *no_elements, long *offset); long (*put_array_info)( const struct dbAddr *paddr, long nRequest ); long (*get_units)(const struct dbAddr *paddr, char *units); long (*get_precision)(const struct dbAddr *paddr, long *precision); long (*get_enum_str)(const struct dbAddr *paddr, char * pbuffer); long (*get_enum_strs)(const struct dbAddr *paddr, struct dbr_enumStrs *p); long (*put_enum_str)(const struct dbAddr *paddr, const char * pbuffer); long (*get_graphic_double)(const struct dbAddr *paddr, struct dbr_grDouble *pgd); long (*get_control_double)(const struct dbAddr *paddr, struct dbr_ctrlDouble *pcd); long (*get_alarm_double)(const struct dbAddr *paddr,struct dbr_alDouble *p); } fldSup_SET;
Field support processing • If field support is defined and there is an appropriate entry in the field support table the routine is called. • If field support is not defined, or if there is no entry in the field support table then the record support routine is called. • If the field routine returns with failure then the record support routine is also called. • If both field and record support routines report failure, then recGbl default routines are called, if necessary.
field(BLA,DBF_NOACCESS) { … special(SPC_DBADDR) fldSup(fldSupDefault_fset) fldSup_type(UCHAR) fldSup_nelm(NELM) fldSup_nord(NORD) extra("void * val") } This is the equivalent of Ben Franksen’s example in the tech-talk posting. fldSup_ prefix is ugly for something which is really a sub structure, but it doesn’t break the dbd file syntax and so existing applications (e.g. vdct) don’t have to change. It’s a bit more verbose than Ben’s, but probably acceptable. Example of field support in a dbd file
field(VAL,DBF_NOACCESS) { prompt("Value") asl(ASL0) special(SPC_DBADDR) fldSup(fldSupDefault_fset) fldSup_bptr(BPTR) fldSup_type(FTVL) fldSup_nelm(NELM) fldSup_nord(NORD) fldSup_prec(PREC) fldSup_units(furlongs) fldSup_hopr(HOPR) fldSup_lopr(LOPR) fldSup_drvh(HOPR) fldSup_drvl(LOPR) fldSup_hihi(HOPR) fldSup_high(HOPR) fldSup_low(LOPR) fldSup_lolo(LOPR) pp(TRUE) extra("void * val") } Parameters can either be other fields in the record (e.g. PREC) or constants (e.g. furlongs). If you leave out fldSup_ declarations then the corresponding field support routine returns S_db_noRSET and defaults are used. A more complete example
dbStatic field support helper routines fldSupParamId fldSupParamAdd( fldSupParamId pFldSup, char * name, char * value ); long fldSupParamFree(fldSupParamId pFldSup); char * fldSupParamGet(fldSupParamId pFldSup, const char * name ); long fldSupParamNth( fldSupParamId pFldSup, const unsigned int n, char ** name, char ** value); • These just maintain a linked list of name-value pairs, so they can regurgitated or queried at a later time. • Not to be accessed after runtime initialisation
Field support structures • Field support structures stored in dbFldDes: • Field support entry table. • Name-value pairs generated by dbStatic and available at initialisation. • Pointer to a private runtime structure • dbFldDes can be accessed at run-time through dbAddr
Field support runtime helpers • dbFldLookup( dbFldDes * pFldDes, char * name, type, int offset ); • Used at initialisation to check types and to get the record offset of other fields in the record. • void * dbFldSupPvt( DBADDR dbAddr ); • Returns field support private structure. • void * dbFldPtr( DBADDR dbAddr, offset ); • Returns a pointer to another field in a record given an offset. (prossibly a macro).
Pros and Cons • Cons: • Fairly significant change • May be some more (or less) additional overhead if used. • Pros • Can provide simple support for arrays. • Backwards compatible – can be done now. • Can possibly provide support for dynamic strings. • Makes it more probable that better meta-data support is provided because it is easier to do. • Avoids a big switch statement in support routines. • Status • At present this has been submitted to base, but should only be viewed as a prototype implementation.