Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions RELEASE_NOTES
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ Release Notes for iocStats

## Unreleased

* Changes by Andrew Johnson:
- iocStats now uses the typed device support interfaces introduced in
Base 7.0 when built against a Base that provides them, but is still
compatible with EPICS Base 3.15 and later. This removes the `DEVSUPFUN`
casts, so the code now builds cleanly with newer C/C++ compilers.

## iocStats-R4-0-1:

### Bugfixes
Expand Down
2 changes: 1 addition & 1 deletion devIocStats/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ INC += devIocStatsOSD.h

LIBRARY_IOC = devIocStats

USR_CFLAGS += -DUSE_TYPED_RSET
USR_CFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET

devIocStats_LIBS = $(EPICS_BASE_IOC_LIBS)
devIocStats_SYS_LIBS_solaris = kstat
Expand Down
83 changes: 60 additions & 23 deletions devIocStats/devIocStatsAnalog.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,16 +135,21 @@
(EPICS_VERSION_INT == VERSION_INT(3, 16, 2, 0)) || \
(EPICS_VERSION_INT >= VERSION_INT(7, 0, 2, 0))

struct aStats {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN read_write;
DEVSUPFUN special_linconv;
};
typedef struct aStats aStats;
#ifndef HAS_aidset
typedef struct aidset {
dset common;
long (*read_ai)(struct aiRecord *prec);
long (*special_linconv)(struct aiRecord *prec, int after);
} aidset;
#endif

#ifndef HAS_aodset
typedef struct aodset {
dset common;
long (*write_ao)(struct aoRecord *prec);
long (*special_linconv)(struct aoRecord *prec, int after);
} aodset;
#endif

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dumb question, why are this definitions needed? I can't find any calls to write_ao or read_ai.

@anjohnson anjohnson Jun 22, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are typedefs for the record-specific device support entry tables (dsets) that were first published for these record types in their generated *Record.h headers in EPICS 7.0.4. Older versions of EPICS didn't allow the *Record.dbd files to add anything to the generated header, so the record types defined their dsets in the *Record.c implementation code and required device support layers to define them identically or in a compatible way (the struct aStats that I replaced here). However that isn't good modern coding practice, and newer C/C++ compilers are getting more strict and rejecting duplicate definitions like that. They also don't like our old DEVSUPFUN typedef, requiring that all function pointers fully define the function being called.

The EPICS 7.0.4 release made it possible for device supports to not have to provide or use their own dset definitions, and the device support routines inside Base are much shorter as a result. However for external modules like iocStats to still build against older EPICS versions the devices have to provide a compatible definition, which is only used when compiling with the old EPICS versions (they didn't define the HAS_aidset or HAS_aodset macros). If you look at the include/aiRecord.h file in EPICS 7.0.4 or later you'll see the same definitions as above.

The read_ai() and write_ao() members of the aidset and aodset structures are function pointers (not functions), and get called from their record type's process() routine through the prec->dset field. The prec->dset field actually points to the appropriate devAiStats or devAoStats tables defined below, so the calls in the record type actually go to the ai_read() and ao_write() functions as given in those tables.


struct pvtArea {
int index;
Expand Down Expand Up @@ -178,15 +183,15 @@ struct scanInfo {
typedef struct scanInfo scanInfo;

static long ai_init(int pass);
static long ai_init_record(aiRecord *);
static long ai_init_record(struct dbCommon *pcommon);
static long ai_read(aiRecord *);
static long ai_ioint_info(int cmd, aiRecord *pr, IOSCANPVT *iopvt);
static long ai_ioint_info(int cmd, struct dbCommon *pcommon, IOSCANPVT *iopvt);

static long ai_clusts_init(int pass);
static long ai_clusts_init_record(aiRecord *);
static long ai_clusts_init_record(struct dbCommon *pcommon);
static long ai_clusts_read(aiRecord *);

static long ao_init_record(aoRecord *pr);
static long ao_init_record(struct dbCommon *pcommon);
static long ao_write(aoRecord *);

static void statsFreeBytes(double *);
Expand Down Expand Up @@ -283,13 +288,41 @@ static validGetParms statsGetParms[] = {
{"cbHighQueueOverruns", statsCbHighQOverruns, QUEUE_TYPE},
{NULL, NULL, 0}};

aStats devAiStats = {6, NULL, ai_init, ai_init_record, ai_ioint_info,
ai_read, NULL};
aidset devAiStats = {
{
6,
NULL,
ai_init,
ai_init_record,
ai_ioint_info
},
ai_read,
NULL
};
epicsExportAddress(dset, devAiStats);
aStats devAoStats = {6, NULL, NULL, ao_init_record, NULL, ao_write, NULL};
aodset devAoStats = {
{
6,
NULL,
NULL,
ao_init_record,
NULL
},
ao_write,
NULL
};
epicsExportAddress(dset, devAoStats);
aStats devAiClusts = {
6, NULL, ai_clusts_init, ai_clusts_init_record, NULL, ai_clusts_read, NULL};
aidset devAiClusts = {
{
6,
NULL,
ai_clusts_init,
ai_clusts_init_record,
NULL
},
ai_clusts_read,
NULL
};
epicsExportAddress(dset, devAiClusts);

static memInfo meminfo = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
Expand Down Expand Up @@ -480,7 +513,8 @@ static long ai_init(int pass) {
return 0;
}

static long ai_clusts_init_record(aiRecord *pr) {
static long ai_clusts_init_record(struct dbCommon *pcommon) {
aiRecord *pr = (aiRecord *) pcommon;
int elem = 0, size = 0, pool = 0, parms = 0;
char *parm;
pvtClustArea *pvt = NULL;
Expand Down Expand Up @@ -512,7 +546,8 @@ static long ai_clusts_init_record(aiRecord *pr) {
return 0;
}

static long ai_init_record(aiRecord *pr) {
static long ai_init_record(struct dbCommon *pcommon) {
aiRecord *pr = (aiRecord *) pcommon;
int i;
char *parm;
pvtArea *pvt = NULL;
Expand Down Expand Up @@ -543,7 +578,8 @@ static long ai_init_record(aiRecord *pr) {
return 0;
}

static long ao_init_record(aoRecord *pr) {
static long ao_init_record(struct dbCommon *pcommon) {
aoRecord *pr = (aoRecord *) pcommon;
int type;
char *parm;
pvtArea *pvt = NULL;
Expand Down Expand Up @@ -577,7 +613,8 @@ static long ao_init_record(aoRecord *pr) {
return 2;
}

static long ai_ioint_info(int cmd, aiRecord *pr, IOSCANPVT *iopvt) {
static long ai_ioint_info(int cmd, struct dbCommon *pcommon, IOSCANPVT *iopvt) {
aiRecord *pr = (aiRecord *) pcommon;
pvtArea *pvt = (pvtArea *)pr->dpvt;

if (!pvt)
Expand Down
67 changes: 46 additions & 21 deletions devIocStats/devIocStatsString.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,12 @@

#define MAX_NAME_SIZE (MAX_STRING_SIZE - 1)

struct sStats {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN read_stringin;
};
typedef struct sStats sStats;
#ifndef HAS_stringindset
typedef struct stringindset {
dset common;
long (*read_stringin)(struct stringinRecord *prec);
} stringindset;
#endif

struct pvtArea {
int index;
Expand All @@ -137,11 +134,11 @@ struct validGetStrParms {
typedef struct validGetStrParms validGetStrParms;

static long stringin_init(int pass);
static long stringin_init_record(stringinRecord *);
static long stringin_init_record(struct dbCommon *pcommon);
static long stringin_read(stringinRecord *);
static long envvar_init_record(stringinRecord *);
static long envvar_init_record(struct dbCommon *pcommon);
static long envvar_read(stringinRecord *);
static long epics_init_record(stringinRecord *);
static long epics_init_record(struct dbCommon *pcommon);
static long epics_read(stringinRecord *);

static void statsSScript1(char *);
Expand Down Expand Up @@ -185,11 +182,36 @@ static validGetStrParms statsGetStrParms[] = {
{"pwd2", statsPwd2, STATIC_TYPE},
{NULL, NULL, 0}};

sStats devStringinStats = {
5, NULL, stringin_init, stringin_init_record, NULL, stringin_read};
sStats devStringinEnvVar = {5, NULL, NULL, envvar_init_record,
NULL, envvar_read};
sStats devStringinEpics = {5, NULL, NULL, epics_init_record, NULL, epics_read};
stringindset devStringinStats = {
{
5,
NULL,
stringin_init,
stringin_init_record,
NULL
},
stringin_read
};
stringindset devStringinEnvVar = {
{
5,
NULL,
NULL,
envvar_init_record,
NULL
},
envvar_read
};
stringindset devStringinEpics = {
{
5,
NULL,
NULL,
epics_init_record,
NULL
},
epics_read
};
epicsExportAddress(dset, devStringinStats);
epicsExportAddress(dset, devStringinEnvVar);
epicsExportAddress(dset, devStringinEpics);
Expand All @@ -215,7 +237,8 @@ static long stringin_init(int pass) {
return 0;
}

static long stringin_init_record(stringinRecord *pr) {
static long stringin_init_record(struct dbCommon *pcommon) {
stringinRecord *pr = (stringinRecord *) pcommon;
int i;
char *parm;
pvtArea *pvt = NULL;
Expand All @@ -242,7 +265,8 @@ static long stringin_init_record(stringinRecord *pr) {
return 0; /* success */
}

static long envvar_init_record(stringinRecord *pr) {
static long envvar_init_record(struct dbCommon *pcommon) {
stringinRecord *pr = (stringinRecord *) pcommon;
if (pr->inp.type != INST_IO) {
recGblRecordError(S_db_badField, (void *)pr,
"devStringinEnvVar (init_record) Illegal INP field");
Expand All @@ -257,11 +281,12 @@ static long envvar_init_record(stringinRecord *pr) {
return 0; /* success */
}

static long epics_init_record(stringinRecord *pr) {
static long epics_init_record(struct dbCommon *pcommon) {
stringinRecord *pr = (stringinRecord *) pcommon;
long status;
const ENV_PARAM **ppParam = env_param_list;

status = envvar_init_record(pr);
status = envvar_init_record(pcommon);
if (status)
return status;

Expand Down
32 changes: 19 additions & 13 deletions devIocStats/devIocStatsWaveform.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,12 @@

#include "devIocStats.h"

struct wStats {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN read_waveform;
};
typedef struct wStats wStats;
#ifndef HAS_wfdset
typedef struct wfdset {
dset common;
long (*read_wf)(struct waveformRecord *prec);
} wfdset;
#endif

struct pvtArea {
int index;
Expand All @@ -101,7 +98,7 @@ struct validGetWfmParms {
typedef struct validGetWfmParms validGetWfmParms;

static long waveform_init(int pass);
static long waveform_init_record(waveformRecord *);
static long waveform_init_record(struct dbCommon *pcommon);
static long waveform_read(waveformRecord *);

static void statsSScript(char *, size_t);
Expand All @@ -116,8 +113,16 @@ static validGetWfmParms statsGetWfmParms[] = {
{"pwd", statsPwd, STATIC_TYPE},
{NULL, NULL, 0}};

wStats devWaveformStats = {
5, NULL, waveform_init, waveform_init_record, NULL, waveform_read};
wfdset devWaveformStats = {
{
5,
NULL,
waveform_init,
waveform_init_record,
NULL
},
waveform_read
};
epicsExportAddress(dset, devWaveformStats);

/* ---------------------------------------------------------------------- */
Expand All @@ -131,7 +136,8 @@ static long waveform_init(int pass) {
return 0;
}

static long waveform_init_record(waveformRecord *pr) {
static long waveform_init_record(struct dbCommon *pcommon) {
waveformRecord *pr = (waveformRecord *) pcommon;
int i;
char *parm;
pvtArea *pvt = NULL;
Expand Down
Loading