Merge branch '7.0' into PSI-7.0

This commit is contained in:
2023-06-22 16:36:32 +02:00
45 changed files with 1060 additions and 187 deletions
+1 -1
View File
@@ -73,7 +73,7 @@ epicsExportAddress(int, dbAccessDebugPUTF);
DB_LOAD_RECORDS_HOOK_ROUTINE dbLoadRecordsHook = NULL;
static short mapDBFToDBR[DBF_NTYPES] = {
static const short mapDBFToDBR[DBF_NTYPES] = {
/* DBF_STRING => */ DBR_STRING,
/* DBF_CHAR => */ DBR_CHAR,
/* DBF_UCHAR => */ DBR_UCHAR,
+13
View File
@@ -410,8 +410,21 @@ DBCORE_API long dbLoadLinkArray(struct link *, short dbrType, void *pbuffer,
DBCORE_API long dbGetNelements(const struct link *plink, long *pnElements);
DBCORE_API int dbIsLinkConnected(const struct link *plink); /* 0 or 1 */
DBCORE_API int dbGetLinkDBFtype(const struct link *plink);
/** \brief Fetch current value from link.
* \param dbrType Database DBR code
* \param pbuffer Destination buffer
* \param nRequest If !NULL. Caller initializes with number of elements requested,
* On success, set to number of elements written to pbuffer.
* \return 0 on success
*
* When called with `nRequest==NULL`, treated as a request for one (1)
* element.
*
* see lset::getValue
*/
DBCORE_API long dbTryGetLink(struct link *, short dbrType, void *pbuffer,
long *nRequest);
/** see dbTryGetLink() */
DBCORE_API long dbGetLink(struct link *, short dbrType, void *pbuffer,
long *options, long *nRequest);
DBCORE_API long dbGetControlLimits(const struct link *plink, double *low,
+11 -6
View File
@@ -39,12 +39,17 @@ extern "C" {
* will adjust automatically, it just compares field sizes.
*/
union native_value {
epicsInt8 dbf_char;
epicsInt16 dbf_short;
epicsEnum16 dbf_enum;
epicsInt32 dbf_long;
epicsFloat32 dbf_float;
epicsFloat64 dbf_double;
epicsInt8 dbf_char;
epicsUInt8 dbf_uchar;
epicsInt16 dbf_short;
epicsUInt16 dbf_ushort;
epicsEnum16 dbf_enum;
epicsInt32 dbf_long;
epicsUInt32 dbf_ulong;
epicsInt64 dbf_int64;
epicsUInt64 dbf_uint64;
epicsFloat32 dbf_float;
epicsFloat64 dbf_double;
#ifdef DB_EVENT_LOG_STRINGS
char dbf_string[MAX_STRING_SIZE];
#endif
+1 -1
View File
@@ -75,7 +75,7 @@ DBCORE_API void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
epicsEnum16 sevr);
DBCORE_API int recGblSetSevrMsg(void *precord, epicsEnum16 new_stat,
epicsEnum16 new_sevr,
const char *msg, ...) EPICS_PRINTF_STYLE(4,5);
EPICS_PRINTF_FMT(const char *msg), ...) EPICS_PRINTF_STYLE(4,5);
DBCORE_API int recGblSetSevrVMsg(void *precord, epicsEnum16 new_stat,
epicsEnum16 new_sevr,
const char *msg, va_list args);
@@ -139,14 +139,16 @@ char** dbCompleteRecord(const char *cword)
if(!prefix.empty() || !suggestions.empty()) {
ret = (char**)malloc(sizeof(*ret)*(2u + suggestions.size()));
ret[0] = prefix.dup();
size_t n=1u;
for(suggestions_t::iterator it(suggestions.begin()), end(suggestions.end());
it!=end; ++it)
{
ret[n++] = it->dup();
if(ret) {
ret[0] = prefix.dup();
size_t n=1u;
for(suggestions_t::iterator it(suggestions.begin()), end(suggestions.end());
it!=end; ++it)
{
ret[n++] = it->dup();
}
ret[n] = NULL;
}
ret[n] = NULL;
}
return ret;
@@ -9,6 +9,7 @@
\*************************************************************************/
#include "iocsh.h"
#include "errSymTbl.h"
#include "dbStaticIocRegister.h"
#include "dbStaticLib.h"
@@ -217,6 +218,40 @@ static void dbReportDeviceConfigCallFunc(const iocshArgBuf *args)
dbReportDeviceConfig(*iocshPpdbbase,stdout);
}
static const iocshArg dbCreateAliasArg0 = { "record",iocshArgStringRecord};
static const iocshArg dbCreateAliasArg1 = { "alias",iocshArgStringRecord};
static const iocshArg * const dbCreateAliasArgs[] = {&argPdbbase,&dbCreateAliasArg0, &dbCreateAliasArg1};
static const iocshFuncDef dbCreateAliasFuncDef = {
"dbCreateAlias",
3,
dbCreateAliasArgs,
"Add a new record alias.\n"
"\n"
"Example: dbCreateAlias pdbbase record:name new:alias\n",
};
static void dbCreateAliasCallFunc(const iocshArgBuf *args)
{
DBENTRY ent;
long status;
dbInitEntry(*iocshPpdbbase, &ent);
if(!args[1].sval || !args[2].sval) {
status = S_dbLib_recNotFound;
} else {
status = dbFindRecord(&ent, args[1].sval);
if(!status) {
status = dbCreateAlias(&ent, args[2].sval);
}
}
dbFinishEntry(&ent);
if(status) {
fprintf(stderr, "Error: %ld %s\n", status, errSymMsg(status));
iocshSetError(1);
}
}
void dbStaticIocRegister(void)
{
iocshRegister(&dbDumpPathFuncDef, dbDumpPathCallFunc);
@@ -234,4 +269,5 @@ void dbStaticIocRegister(void)
iocshRegister(&dbPvdDumpFuncDef, dbPvdDumpCallFunc);
iocshRegister(&dbPvdTableSizeFuncDef,dbPvdTableSizeCallFunc);
iocshRegister(&dbReportDeviceConfigFuncDef, dbReportDeviceConfigCallFunc);
iocshRegister(&dbCreateAliasFuncDef, dbCreateAliasCallFunc);
}
@@ -1652,6 +1652,7 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias)
dbRecordNode *pnewnode;
DBENTRY tempEntry;
PVDENTRY *ppvd;
long status;
if (!precordType)
return S_dbLib_recordTypeNotFound;
@@ -1664,9 +1665,10 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias)
return S_dbLib_recNotFound;
dbInitEntry(pdbentry->pdbbase, &tempEntry);
if (!dbFindRecord(&tempEntry, alias))
return S_dbLib_recExists;
status = dbFindRecord(&tempEntry, alias);
dbFinishEntry(&tempEntry);
if (!status)
return S_dbLib_recExists;
pnewnode = dbCalloc(1, sizeof(dbRecordNode));
pnewnode->recordname = epicsStrDup(alias);
@@ -1676,15 +1678,16 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias)
precnode->flags |= DBRN_FLAGS_HASALIAS;
ellInit(&pnewnode->infoList);
ellAdd(&precordType->recList, &pnewnode->node);
precordType->no_aliases++;
ppvd = dbPvdAdd(pdbentry->pdbbase, precordType, pnewnode);
if (!ppvd) {
errMessage(-1, "dbCreateAlias: Add to PVD failed");
free(pnewnode);
return -1;
}
ellAdd(&precordType->recList, &pnewnode->node);
precordType->no_aliases++;
return 0;
}
@@ -39,7 +39,9 @@ char *dbRecordName(DBENTRY *pdbentry);
char *dbGetStringNum(DBENTRY *pdbentry);
long dbPutStringNum(DBENTRY *pdbentry,const char *pstring);
void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...) EPICS_PRINTF_STYLE(2,3);
void dbMsgPrint(
DBENTRY *pdbentry, EPICS_PRINTF_FMT(const char *fmt), ...
) EPICS_PRINTF_STYLE(2,3);
void dbPutStringSuggest(DBENTRY *pdbentry, const char *pstring);
@@ -216,22 +216,96 @@ registrar(tsInitialize)
=head3 TimeStamp Filter C<"ts">
This filter replaces the timestamp in the data fetched through the channel with
the time the value was fetched (or an update was sent). The record's timestamp
This filter is used for two purposes:
=over
=item * to retrieve the timestamp of the record as a value in several different
formats;
=item * to retrieve the record value as normal, but replace the timestamp with
the time the value was fetched.
=back
=head4 Parameters
=head4 No parameters (an empty pair of braces)
Retrieve the record value as normal, but replace the timestamp with the time the
value was fetched (or an update was sent). This is useful for clients that can't
handle timestamps that are far in the past. Normally, the record's timestamp
indicates when the record last processed, which could have been days or even
weeks ago for some records, or set to the EPICS epoch if the record has never
processed.
=head4 Parameters
=head4 Numeric type C<"num">
None, use an empty pair of braces.
The following values are accepted for this parameter:
=head4 Example
=over
Hal$ caget -a 'test:channel.{"ts":{}}'
test:channel.{"ts":{}} 2012-08-28 22:10:31.192547 0 UDF INVALID
Hal$ caget -a 'test:channel'
test:channel <undefined> 0 UDF INVALID
=item * C<"dbl"> requests the timestamp as C<epicsFloat64> representing the
non-integral number of seconds since epoch. This format is convenient,
but loses precision, depending on which epoch is used.
=item * C<"sec"> requests the number of seconds since epoch as C<epicsUInt32>.
=item * C<"nsec"> requests the number of nanoseconds since epoch as
C<epicsUInt32>.
=item * C<"ts"> requests the entire timestamp. It is provided as a two-element
array of C<epicsUInt32> representing seconds and nanoseconds.
=back
Note that C<epicsUInt32> cannot be transferred over Channel Access; in that
case, the value will be converted to C<epicsFloat64>.
=head4 String type C<"str">
The following values are accepted for this parameter:
=over
=item * C<"epics"> requests the timestamp as a string in the format used by
tools such as C<caget>.
=item * C<"iso"> requests the timestamp as a string in the ISO8601 format.
=back
=head4 Epoch adjustment C<"epoch">
The following values are accepted for this parameter:
=over
=item * C<"epics"> keeps the EPICS epoch (1990-01-01) and is the default if the
C<"epoch"> parameter is not specified.
=item * C<"unix"> converts the timestamp to the UNIX/POSIX epoch (1970-01-01).
=back
=head4 Examples
Hal$ caget -a 'test:invalid_ts.{"ts":{}}'
test:invalid_ts.{"ts":{}} 2012-08-28 22:10:31.192547 0 UDF INVALID
Hal$ caget -a 'test:invalid_ts'
test:invalid_ts <undefined> 0 UDF INVALID
Hal$ caget -a test:channel
test:channel 2021-03-11 18:23:48.265386 42
Hal$ caget 'test:channel.{"ts": {"str": "epics"}}'
test:channel.{"ts": {"str": "epics"}} 2021-03-11 18:23:48.265386
Hal$ caget 'test:channel.{"ts": {"str": "iso"}}'
test:channel.{"ts": {"str": "iso"}} 2021-03-11T18:23:48.265386+0100
Hal$ caget -f9 'test:channel.{"ts": {"num": "dbl"}}'
test:channel.{"ts": {"num": "dbl"}} 984331428.265386105
Hal$ caget -f1 'test:channel.{"ts": {"num": "ts"}}'
test:channel.{"ts": {"num": "ts"}} 2 984331428.0 265386163.0
Hal$ caget -f1 'test:channel.{"ts": {"num": "ts", "epoch": "unix"}}'
test:channel.{"ts": {"num": "ts", "epoch": "unix"}} 2 1615483428.0 265386163.0
=cut
+335 -17
View File
@@ -1,4 +1,5 @@
/*************************************************************************\
* Copyright (c) 2021 Cosylab d.d
* Copyright (c) 2010 Brookhaven National Laboratory.
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
* fuer Materialien und Energie GmbH.
@@ -9,6 +10,7 @@
/*
* Author: Ralph Lange <Ralph.Lange@bessy.de>
* Author: Jure Varlec <jure.varlec@cosylab.com>
*/
#include <stdio.h>
@@ -16,20 +18,124 @@
#include <string.h>
#include "chfPlugin.h"
#include "db_field_log.h"
#include "dbExtractArray.h"
#include "dbLock.h"
#include "db_field_log.h"
#include "epicsExport.h"
#include "freeList.h"
#include "errlog.h"
/*
* The size of the data is different for each channel, and can even
* change at runtime, so a freeList doesn't make much sense here.
*/
/* Allocation size for freelists */
#define ALLOC_NUM_ELEMENTS 32
#define logicErrorMessage() \
errMessage(-1, "Logic error: invalid state encountered in ts filter")
/* Filter settings */
enum tsMode {
tsModeInvalid = 0,
tsModeGenerate = 1,
tsModeDouble = 2,
tsModeSec = 3,
tsModeNsec = 4,
tsModeArray = 5,
tsModeString = 6,
};
static const chfPluginEnumType ts_numeric_enum[] = {
{"dbl", 2}, {"sec", 3}, {"nsec", 4}, {"ts", 5}};
enum tsEpoch {
tsEpochEpics = 0,
tsEpochUnix = 1,
};
static const chfPluginEnumType ts_epoch_enum[] = {{"epics", 0}, {"unix", 1}};
enum tsString {
tsStringInvalid = 0,
tsStringEpics = 1,
tsStringIso = 2,
};
static const chfPluginEnumType ts_string_enum[] = {{"epics", 1}, {"iso", 2}};
typedef struct tsPrivate {
enum tsMode mode;
enum tsEpoch epoch;
enum tsString str;
} tsPrivate;
static const chfPluginArgDef ts_args[] = {
chfEnum(tsPrivate, mode, "num", 0, 0, ts_numeric_enum),
chfEnum(tsPrivate, epoch, "epoch", 0, 0, ts_epoch_enum),
chfEnum(tsPrivate, str, "str", 0, 0, ts_string_enum),
chfPluginArgEnd
};
static int parse_finished(void *pvt) {
tsPrivate *settings = (tsPrivate *)pvt;
if (settings->str != tsStringInvalid) {
settings->mode = tsModeString;
#if defined _MSC_VER && _MSC_VER <= 1700
// VS 2012 crashes in ISO mode, doesn't support timezones
if (settings->str == tsStringIso) {
return -1;
}
#endif
} else if (settings->mode == tsModeInvalid) {
settings->mode = tsModeGenerate;
}
return 0;
}
/* Allocation of filter settings */
static void *private_free_list;
static void * allocPvt() {
return freeListCalloc(private_free_list);
}
static void freePvt(void *pvt) {
freeListFree(private_free_list, pvt);
}
/* Allocation of two-element arrays for second+nanosecond pairs */
static void *ts_array_free_list;
static void *allocTsArray() {
return freeListCalloc(ts_array_free_list);
}
static void freeTsArray(db_field_log *pfl) {
freeListFree(ts_array_free_list, pfl->u.r.field);
}
/* Allocation of strings */
static void *string_free_list;
static void *allocString() {
return freeListCalloc(string_free_list);
}
static void freeString(db_field_log *pfl) {
freeListFree(string_free_list, pfl->u.r.field);
}
/* The dtor for waveform data for the case when we have to copy it. */
static void freeArray(db_field_log *pfl) {
/*
* The size of the data is different for each channel, and can even
* change at runtime, so a freeList doesn't make much sense here.
*/
free(pfl->u.r.field);
}
static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
static db_field_log* generate(void* pvt, dbChannel *chan, db_field_log *pfl) {
epicsTimeStamp now;
epicsTimeGetCurrent(&now);
@@ -44,7 +150,7 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
dbScanLock(dbChannelRecord(chan));
dbChannelGetArrayInfo(chan, &pSource, &nSource, &offset);
dbExtractArray(pSource, pTarget, pfl->field_size,
nSource, pfl->no_elements, offset, 1);
nSource, pfl->no_elements, offset, 1);
pfl->u.r.field = pTarget;
pfl->dtor = freeArray;
pfl->u.r.pvt = pvt;
@@ -56,34 +162,246 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
return pfl;
}
static void channelRegisterPre(dbChannel *chan, void *pvt,
chPostEventFunc **cb_out, void **arg_out, db_field_log *probe)
{
static db_field_log *replace_fl_value(tsPrivate const *pvt,
db_field_log *pfl,
int (*func)(tsPrivate const *,
db_field_log *)) {
/* Get rid of the old value */
if (pfl->type == dbfl_type_ref && pfl->dtor) {
pfl->dtor(pfl);
pfl->dtor = NULL;
}
pfl->no_elements = 1;
pfl->type = dbfl_type_val;
if (func(pvt, pfl)) {
db_delete_field_log(pfl);
pfl = NULL;
}
return pfl;
}
static void ts_to_array(tsPrivate const *settings,
epicsTimeStamp const *ts,
epicsUInt32 arr[2]) {
arr[0] = ts->secPastEpoch;
arr[1] = ts->nsec;
if (settings->epoch == tsEpochUnix) {
/* Cannot use epicsTimeToWhatever because Whatever uses signed ints */
arr[0] += POSIX_TIME_AT_EPICS_EPOCH;
}
}
static int ts_seconds(tsPrivate const *settings, db_field_log *pfl) {
epicsUInt32 arr[2];
ts_to_array(settings, &pfl->time, arr);
pfl->field_type = DBF_ULONG;
pfl->field_size = sizeof(epicsUInt32);
pfl->u.v.field.dbf_ulong = arr[0];
return 0;
}
static int ts_nanos(tsPrivate const *settings, db_field_log *pfl) {
epicsUInt32 arr[2];
ts_to_array(settings, &pfl->time, arr);
pfl->field_type = DBF_ULONG;
pfl->field_size = sizeof(epicsUInt32);
pfl->u.v.field.dbf_ulong = arr[1];
return 0;
}
static int ts_double(tsPrivate const *settings, db_field_log *pfl) {
epicsUInt32 arr[2];
ts_to_array(settings, &pfl->time, arr);
pfl->field_type = DBF_DOUBLE;
pfl->field_size = sizeof(epicsFloat64);
pfl->u.v.field.dbf_double = arr[0] + arr[1] * 1e-9;
return 0;
}
static int ts_array(tsPrivate const *settings, db_field_log *pfl) {
pfl->field_type = DBF_ULONG;
pfl->field_size = sizeof(epicsUInt32);
pfl->type = dbfl_type_ref;
pfl->u.r.pvt = NULL;
pfl->u.r.field = allocTsArray();
if (pfl->u.r.field) {
pfl->no_elements = 2;
pfl->dtor = freeTsArray;
ts_to_array(settings, &pfl->time, (epicsUInt32*)pfl->u.r.field);
} else {
pfl->no_elements = 0;
pfl->dtor = NULL;
}
return 0;
}
static int ts_string(tsPrivate const *settings, db_field_log *pfl) {
char const *fmt;
char *field;
size_t n;
switch (settings->str) {
case tsStringEpics:
fmt = "%Y-%m-%d %H:%M:%S.%06f";
break;
case tsStringIso:
fmt = "%Y-%m-%dT%H:%M:%S.%06f%z";
break;
case tsStringInvalid:
default:
logicErrorMessage();
return 1;
}
pfl->field_type = DBF_STRING;
pfl->field_size = MAX_STRING_SIZE;
pfl->type = dbfl_type_ref;
pfl->u.r.pvt = NULL;
pfl->u.r.field = allocString();
if (!pfl->u.r.field) {
pfl->no_elements = 0;
pfl->dtor = NULL;
return 0;
}
pfl->dtor = freeString;
field = (char *)pfl->u.r.field;
n = epicsTimeToStrftime(field, MAX_STRING_SIZE, fmt, &pfl->time);
if (!n) {
field[0] = 0;
}
return 0;
}
static db_field_log *filter(void *pvt, dbChannel *chan, db_field_log *pfl) {
tsPrivate *settings = (tsPrivate *)pvt;
(void)chan;
switch (settings->mode) {
case tsModeDouble:
return replace_fl_value(pvt, pfl, ts_double);
case tsModeSec:
return replace_fl_value(pvt, pfl, ts_seconds);
case tsModeNsec:
return replace_fl_value(pvt, pfl, ts_nanos);
case tsModeArray:
return replace_fl_value(pvt, pfl, ts_array);
case tsModeString:
return replace_fl_value(pvt, pfl, ts_string);
case tsModeGenerate:
case tsModeInvalid:
default:
logicErrorMessage();
db_delete_field_log(pfl);
pfl = NULL;
}
return pfl;
}
/* Only the "generate" mode is registered for the pre-queue chain as it creates
it's own timestamp which should be as close to the event as possible */
static void channelRegisterPre(dbChannel * chan, void *pvt,
chPostEventFunc **cb_out, void **arg_out,
db_field_log *probe) {
tsPrivate *settings = (tsPrivate *)pvt;
(void)chan;
(void)arg_out;
(void)probe;
*cb_out = settings->mode == tsModeGenerate ? generate : NULL;
}
/* For other modes, the post-chain is fine as they only manipulate existing
timestamps */
static void channelRegisterPost(dbChannel *chan, void *pvt,
chPostEventFunc **cb_out, void **arg_out,
db_field_log *probe) {
tsPrivate *settings = (tsPrivate *)pvt;
(void)chan;
if (settings->mode == tsModeGenerate || settings->mode == tsModeInvalid) {
*cb_out = NULL;
return;
}
*cb_out = filter;
*arg_out = pvt;
/* Get rid of the value of the probe because we will be changing the
datatype */
if (probe->type == dbfl_type_ref && probe->dtor) {
probe->dtor(probe);
probe->dtor = NULL;
}
probe->no_elements = 1;
probe->type = dbfl_type_val;
switch (settings->mode) {
case tsModeArray:
probe->no_elements = 2;
/* fallthrough */
case tsModeSec:
case tsModeNsec:
probe->field_type = DBF_ULONG;
probe->field_size = sizeof(epicsUInt32);
break;
case tsModeDouble:
probe->field_type = DBF_DOUBLE;
probe->field_size = sizeof(epicsFloat64);
break;
case tsModeString:
probe->field_type = DBF_STRING;
probe->field_size = MAX_STRING_SIZE;
break;
case tsModeGenerate:
case tsModeInvalid:
// Already handled above, added here for completeness.
default:
logicErrorMessage();
*cb_out = NULL;
}
}
static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent)
{
printf("%*sTimestamp (ts)\n", indent, "");
tsPrivate *settings = (tsPrivate *)pvt;
(void)chan;
(void)level;
printf("%*sTimestamp (ts): mode: %d, epoch: %d, str: %d\n",
indent, "", settings->mode, settings->epoch, settings->str);
}
static chfPluginIf pif = {
NULL, /* allocPvt, */
NULL, /* freePvt, */
allocPvt,
freePvt,
NULL, /* parse_error, */
NULL, /* parse_ok, */
NULL, /* parse_error, */
parse_finished,
NULL, /* channel_open, */
channelRegisterPre,
NULL, /* channelRegisterPost, */
channelRegisterPost,
channel_report,
NULL /* channel_close */
};
static void tsInitialize(void)
{
chfPluginRegister("ts", &pif, NULL);
freeListInitPvt(&private_free_list, sizeof(tsPrivate),
ALLOC_NUM_ELEMENTS);
freeListInitPvt(&ts_array_free_list, 2 * sizeof(epicsUInt32),
ALLOC_NUM_ELEMENTS);
freeListInitPvt(&string_free_list, MAX_STRING_SIZE,
ALLOC_NUM_ELEMENTS);
chfPluginRegister("ts", &pif, ts_args);
}
epicsExportRegistrar(tsInitialize);
@@ -42,6 +42,8 @@ value is non-zero.
=back
This record type was included in base.dbd beginning with epics-base 3.14.10 .
=head2 Record-specific Menus
=head3 Menu aSubLFLG
@@ -13,6 +13,8 @@ The histogram record is used to store frequency counts of a signal into an array
of arbitrary length. The user can configure the range of the signal value that
the array will store. Anything outside this range will be ignored.
This record type was included in base.dbd beginning with epics-base 3.15.0.1 .
=head2 Parameter Fields
The record-specific fields are described below.
@@ -14,6 +14,8 @@ from a hardware input.
The record supports alarm limits, alarm filtering, graphics and control
limits.
This record type was included in base.dbd beginning with epics-base 3.16.1 .
=head2 Parameter Fields
The record-specific fields are described below.
@@ -13,6 +13,8 @@ This record type is normally used to send an integer value of up to 64 bits
to an output device.
The record supports alarm, drive, graphics and control limits.
This record type was included in base.dbd beginning with epics-base 3.16.1 .
=head2 Parameter Fields
The record-specific fields are described below.
@@ -11,6 +11,8 @@
The long string input record is used to retrieve an arbitrary ASCII string with
a maximum length of 65535 characters.
This record type was included in base.dbd beginning with epics-base 3.15.0.2 .
=head2 Parameter Fields
The record-specific fields are described below, grouped by functionality.
@@ -11,6 +11,8 @@
The long string output record is used to write an arbitrary ASCII string with a
maximum length of 65535 characters.
This record type was included in base.dbd beginning with epics-base 3.15.0.2 .
=head2 Parameter Fields
The record-specific fields are described below, grouped by functionality.
@@ -11,6 +11,8 @@
The printf record is used to generate and write a string using a format
specification and parameters, analogous to the C C<printf()> function.
This record type was included in base.dbd beginning with epics-base 3.15.0.2 .
=head2 Parameter Fields
The record-specific fields are described below, grouped by functionality.
@@ -3,7 +3,7 @@ To start the ioc from this directory execute the command
Alternatively make the st.cmd file directly executable with
chmod +x st.cmd
and check the executable name on the first line of the st.cmd file
and check the executable name on the first line of the st.cmd file
You may need to change the name of the .dbd file given in the
st.cmd's dbLoadDatabase() command before starting the ioc.