Merge pull request #96 from mrkraimer/master
Merged Marty's rewrite of the CA provider
This commit is contained in:
5
Makefile
5
Makefile
@@ -20,6 +20,11 @@ pvtoolsSrc_DEPEND_DIRS = src src/ca
|
||||
DIRS += testApp
|
||||
testApp_DEPEND_DIRS = src
|
||||
|
||||
|
||||
DIRS += testCa
|
||||
testCa_DEPEND_DIRS = src src/ca
|
||||
|
||||
|
||||
DIRS += examples
|
||||
examples_DEPEND_DIRS += src src/ca
|
||||
|
||||
|
||||
636
caProvider.md
Normal file
636
caProvider.md
Normal file
@@ -0,0 +1,636 @@
|
||||
# pvAccessCPP: ca provider
|
||||
|
||||
2018.07.09
|
||||
|
||||
Editors:
|
||||
|
||||
* Marty Kraimer
|
||||
|
||||
This is a description of channel provider **ca** that is implemented as part of **pvAccessCPP**.
|
||||
|
||||
It uses the **channel access** network protocol to communicate with a server,
|
||||
i. e. the network protocol that has been used to communicate with **EPICS IOCs** since 1990.
|
||||
|
||||
Provider **pva** is another way to connect to a **DBRecord**,
|
||||
But this only works if the IOC has **qsrv** installed.
|
||||
**qsrv**, which is provided with
|
||||
[pva2pva](https://github.com/epics-base/pva2pva),
|
||||
has full support for communicating with a **DBRecord**.
|
||||
The only advantage of **ca** is that it does require any changes to an existing IOC.
|
||||
|
||||
|
||||
The following are discussed.
|
||||
|
||||
* [Introduction](#S-introduction)
|
||||
* [client API](#S-client)
|
||||
* [Mapping DBD data to pvData](#S-dbd_to_pvdata)
|
||||
* [Developing plugins for ca provider.](#S-plugin)
|
||||
|
||||
|
||||
|
||||
## <a name="S-introduction"></a>Introduction
|
||||
|
||||
The primary purpose of the **ca** provider is to access **DBRecord**s in an EPICS IOC via the
|
||||
**channel access** network protocol but to use pvData objects for the client.
|
||||
|
||||
Each **DBRecord** instance has a record name that must be unique in the local area network.
|
||||
A client can access any public field of a **DBRecord**;
|
||||
Each **DBRecord** instance has a record name that is unique with in the local area network
|
||||
A channel name is a **recordname.fieldName**.
|
||||
If the fieldname is not specified then **.VAL** is assumed
|
||||
|
||||
### example database
|
||||
|
||||
The following:
|
||||
|
||||
```
|
||||
mrk> pwd
|
||||
/home/epicsv4/masterCPP/pvAccessCPP/testCa
|
||||
mrk> softIoc -d testCaProvider.db
|
||||
```
|
||||
|
||||
Starts an EPICS IOC that is used for all examples in this document.
|
||||
|
||||
### examples
|
||||
|
||||
```
|
||||
mrk> caget -d DBR_TIME_FLOAT DBRdoubleout
|
||||
DBRdoubleout
|
||||
Native data type: DBF_DOUBLE
|
||||
Request type: DBR_TIME_FLOAT
|
||||
Element count: 1
|
||||
Value: 1
|
||||
Timestamp: 2018-06-21 06:23:07.939894
|
||||
Status: NO_ALARM
|
||||
Severity: NO_ALARM
|
||||
mrk> pvget -p ca -r "value,alarm,timeStamp" -i DBRdoubleout
|
||||
DBRdoubleout
|
||||
structure
|
||||
double value 1
|
||||
alarm_t alarm
|
||||
int severity 0
|
||||
int status 0
|
||||
string message
|
||||
time_t timeStamp
|
||||
long secondsPastEpoch 1529576587
|
||||
int nanoseconds 939894210
|
||||
int userTag 0
|
||||
mrk> pvget -p ca -r "value,alarm,timeStamp" DBRdoubleout
|
||||
DBRdoubleout
|
||||
structure
|
||||
double value 1
|
||||
alarm_t alarm NO_ALARM NO_STATUS <no message>
|
||||
time_t timeStamp 2018-06-21T06:23:07.940 0
|
||||
|
||||
mrk> pvget -p ca -r value -i DBRdoubleout.SCAN
|
||||
DBRdoubleout.SCAN
|
||||
epics:nt/NTEnum:1.0
|
||||
enum_t value
|
||||
int index 0
|
||||
string[] choices [Passive,Event,I/O Intr,10 second,5 second,2 second,1 second,.5 second,.2 second,.1 second]
|
||||
```
|
||||
|
||||
### Overview of Channel Access
|
||||
|
||||
**channel access**, which is provided with **epics-base**, defines and implements a protocol
|
||||
for client/server network communication.
|
||||
|
||||
**epics-base** provides both a client and a server implementation
|
||||
This document only discusses the client API.
|
||||
|
||||
For details see:
|
||||
|
||||
[EPICS Channel Access 4.13.1 Reference Manual](https://epics.anl.gov/base/R7-0/1-docs/CAref.html)
|
||||
|
||||
**channel access** allows a client to get, put, and monitor monitor data from a server.
|
||||
The data is defined by various DBD types.
|
||||
|
||||
The following, in **epics-base/include**, are the
|
||||
main include files that show the **channel access** API:
|
||||
|
||||
```
|
||||
cadef.h
|
||||
db_access.h
|
||||
```
|
||||
|
||||
The client requests data via one of the DBR types.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
DBR_STS_DOUBLE returns a double status structure (dbr_sts_double)
|
||||
where
|
||||
struct dbr_sts_double{
|
||||
dbr_short_t status; /* status of value */
|
||||
dbr_short_t severity; /* severity of alarm */
|
||||
dbr_long_t RISC_pad; /* RISC alignment */
|
||||
dbr_double_t value; /* current value */
|
||||
};
|
||||
```
|
||||
|
||||
The server converts data between the native type of the field being accessed and the DBR type.
|
||||
|
||||
|
||||
### Overview of ca provider
|
||||
|
||||
**ca** is a pvAccess Channel Provider that uses **channel access** to connect a client to a server.
|
||||
|
||||
With **ca**, the client data appears as pvData objects, i. e.
|
||||
**ca** converts the data provided by **channel access** to/from pvData
|
||||
|
||||
Thus a pvAccess client can communicate with an existing V3 EPICS IOC without making
|
||||
any changes to existing IOCs.
|
||||
|
||||
For an overview of pvData and pvAccess see:
|
||||
|
||||
[EPICS V4 Developer's Guide](https://mrkraimer.github.io/website/developerGuide/developerGuide.html)
|
||||
|
||||
**ca** requests data from the server with a DBR type that matches the native type.
|
||||
See the next section for more details.
|
||||
|
||||
All conversion to/from other types must be done by the client.
|
||||
|
||||
## <a name="S-client"></a>Client API
|
||||
|
||||
**ca** implements the following pvAccess methods : **getField**, **channelGet**, **channelPut** and **monitor**.
|
||||
|
||||
For channelPut the only field that can be accessed is **value**.
|
||||
For channelPut a client can issue puts with and without a callback from the server.
|
||||
The default is no callback. If createChannelPut has the option "record[block=true]" then a put callback used.
|
||||
|
||||
All of the other pvAccess methods provide access to fields **alarm** and **timeStamp**.
|
||||
|
||||
Depending on the type associated with the **value** field the following fields may also be available:
|
||||
**display**, **control** , and **valueAlarm**.
|
||||
|
||||
Thus a client can make requests like:
|
||||
|
||||
```
|
||||
pvget -p ca -r "value,alarm,timeStamp,display,control,valueAlarm" names ...
|
||||
```
|
||||
|
||||
**ca** will create a structure that has the fields requested but only for fields that are supported
|
||||
by the server.
|
||||
|
||||
* For puts only value is supported.
|
||||
* For gets and monitors every channel supports value, alarm, and timeStamp;
|
||||
* If any of display,control, or valueAlarm are requested then timeStamp is NOT available.
|
||||
|
||||
Lets discuss the various fields.
|
||||
|
||||
### value
|
||||
|
||||
This can be a scalar, scalarArray, or an enumerated structure.
|
||||
|
||||
For a scalar or scalarArray the ScalarType is one of the following:
|
||||
**pvString**, **pvByte**, **pvShort**, **pvInt**, **pvFloat**, or **pvDouble**.
|
||||
|
||||
Note that **channel access** does not support unsigned integers or 64 bit integers.
|
||||
|
||||
A enumerated structure is created if the native type is **DBR_ENUM**.
|
||||
|
||||
Some examples are:
|
||||
|
||||
```
|
||||
pvget -p ca -r value -i DBRlongout
|
||||
DBRlongout
|
||||
structure
|
||||
int value 0
|
||||
mrk> pvget -p ca -r value -i DBRdoubleout
|
||||
DBRdoubleout
|
||||
structure
|
||||
double value 0
|
||||
mrk> pvget -p ca -r value -i DBRshortArray
|
||||
DBRshortArray
|
||||
structure
|
||||
short[] value []
|
||||
mrk> pvget -p ca -r value -i DBRstringArray
|
||||
DBRstringArray
|
||||
structure
|
||||
string[] value [aa,bb,cc]
|
||||
mrk> pvget -p ca -r value -i DBRmbbin
|
||||
DBRmbbin
|
||||
epics:nt/NTEnum:1.0
|
||||
enum_t value
|
||||
int index 1
|
||||
string[] choices [zero,one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,thirteen,fourteen,fifteen]
|
||||
mrk>
|
||||
|
||||
```
|
||||
|
||||
|
||||
### alarm,timeStamp,display,control, and valueAlarm
|
||||
|
||||
Each of these is one of the property structures defined in pvData.
|
||||
|
||||
#### Examples
|
||||
|
||||
```
|
||||
mrk> pvget -p ca -r alarm -i DBRdoubleout
|
||||
DBRdoubleout
|
||||
structure
|
||||
alarm_t alarm
|
||||
int severity 2
|
||||
int status 3
|
||||
string message HIHI
|
||||
|
||||
mrk> pvget -p ca -r timeStamp -i DBRdoubleout
|
||||
DBRdoubleout
|
||||
structure
|
||||
time_t timeStamp
|
||||
long secondsPastEpoch 1529923341
|
||||
int nanoseconds 314916189
|
||||
int userTag 0
|
||||
mrk> pvget -p ca -r display -i DBRdoubleout
|
||||
DBRdoubleout
|
||||
structure
|
||||
display_t display
|
||||
double limitLow -10
|
||||
double limitHigh 10
|
||||
string description
|
||||
string format F8.2
|
||||
string units volts
|
||||
mrk> pvget -p ca -r control -i DBRdoubleout
|
||||
DBRdoubleout
|
||||
structure
|
||||
control_t control
|
||||
double limitLow -1e+29
|
||||
double limitHigh 1e+29
|
||||
double minStep 0
|
||||
mrk> pvget -p ca -r valueAlarm -i DBRdoubleout
|
||||
DBRdoubleout
|
||||
structure
|
||||
valueAlarm_t valueAlarm
|
||||
boolean active false
|
||||
double lowAlarmLimit -8
|
||||
double lowWarningLimit -6
|
||||
double highWarningLimit 6
|
||||
double highAlarmLimit 8
|
||||
int lowAlarmSeverity 0
|
||||
int lowWarningSeverity 0
|
||||
int highWarningSeverity 0
|
||||
int highAlarmSeverity 0
|
||||
```
|
||||
|
||||
|
||||
|
||||
## <a name="S-dbd_to_pvdata"></a>DBD to pvData
|
||||
|
||||
### Type Conversion
|
||||
|
||||
Three type systems are involved in accessing data in a **DBRecord** and converting it to/from pvData:
|
||||
|
||||
* DBF The type system used for **DBRecord**s.
|
||||
* DBR The type system used by **channel access**.
|
||||
* pvData
|
||||
|
||||
The following gives a summary of the conversions between the type systems:
|
||||
|
||||
```
|
||||
rawtype DBF DBR pvData ScalarType
|
||||
|
||||
char[MAX_STRING_SIZE] DBF_STRING DBR_STRING pvString
|
||||
epicsInt8 DBF_CHAR DBR_CHAR pvByte
|
||||
epicsUint8 DBF_UCHAR DBR_CHAR pvByte
|
||||
epicsInt16 DBF_SHORT DBR_SHORT pvShort
|
||||
epicsUInt16 DBF_USHORT DBR_LONG pvInt
|
||||
epicsInt32 DBF_LONG DBR_LONG pvInt
|
||||
epicsUInt32 DBF_ULONG DBR_DOUBLE pvDouble
|
||||
epicsInt64 DBF_INT64 no support
|
||||
epicsUInt64 DBF_UINT64 no support
|
||||
float DBF_FLOAT DBR_FLOAT pvFloat
|
||||
double DBF_DOUBLE DBR_DOUBLE pvDouble
|
||||
epicsUInt16 DBF_ENUM DBR_ENUM enum structure
|
||||
epicsUInt16 DBF_MENU DBR_ENUM enum structure
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
* Both DBF_CHAR and DBF_UCHAR go to DBR_CHAR. This is ambigous.
|
||||
* DBF_USHORT promoted to DBR_LONG
|
||||
* DBF_ULONG promoted to DBR_DOUBLE
|
||||
* qsrv provides full access to all DBF types, but the IOC must have qsrv installed.
|
||||
|
||||
### Accessing data in a DBRecord
|
||||
|
||||
An IOC database is a memory resident database of **DBRecord** instances.
|
||||
|
||||
Each **DBRecord** is an instance of one of an extensible set of record types.
|
||||
Each record type has an associated dbd definition which defines a set of fields for
|
||||
each record instance.
|
||||
|
||||
For example an aoRecord.dbd has the definition:
|
||||
|
||||
```
|
||||
recordtype(ao) {
|
||||
include "dbCommon.dbd"
|
||||
field(VAL,DBF_DOUBLE) {
|
||||
...
|
||||
}
|
||||
field(OVAL,DBF_DOUBLE) {
|
||||
...
|
||||
}
|
||||
... many more fields
|
||||
```
|
||||
|
||||
In addition each record type has a associated set of support code defined in recSup.h
|
||||
|
||||
```
|
||||
/* record support entry table */
|
||||
struct typed_rset {
|
||||
long number; /* number of support routines */
|
||||
long (*report)(void *precord);
|
||||
long (*init)();
|
||||
long (*init_record)(struct dbCommon *precord, int pass);
|
||||
long (*process)(struct dbCommon *precord);
|
||||
long (*special)(struct dbAddr *paddr, int after);
|
||||
long (*get_value)(void); /* DEPRECATED set to NULL */
|
||||
long (*cvt_dbaddr)(struct dbAddr *paddr);
|
||||
long (*get_array_info)(struct dbAddr *paddr, long *no_elements, long *offset);
|
||||
long (*put_array_info)(struct dbAddr *paddr, long nNew);
|
||||
long (*get_units)(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)(struct dbAddr *paddr, struct dbr_grDouble *p);
|
||||
long (*get_control_double)(struct dbAddr *paddr, struct dbr_ctrlDouble *p);
|
||||
long (*get_alarm_double)(struct dbAddr *paddr, struct dbr_alDouble *p);
|
||||
};
|
||||
```
|
||||
|
||||
The methods that support accessing data from the record include:
|
||||
|
||||
```
|
||||
cvt_dbaddr Implemented by record types that determine VAL type at record initialization
|
||||
*array_info Implemented by array record types
|
||||
get_units Implemented by numeric record types
|
||||
get_precision Implemented by float and double record types
|
||||
*_enum_* Implemented by enumerated record types
|
||||
get_graphic_double NOTE Always returns limits as double
|
||||
get_control_double NOTE Always returns limits as double
|
||||
get_alarm_double NOTE Always returns limits as double
|
||||
```
|
||||
|
||||
Each of these methods is optional, i. e. record support for a particular record type
|
||||
only implements methods that make sense for the record type.
|
||||
|
||||
For example the enum methods are only implemented by records that have the definition:
|
||||
|
||||
```
|
||||
...
|
||||
field(VAL,DBF_ENUM) {
|
||||
...
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
### Channel Access Data
|
||||
|
||||
A client can access any public field of a **DBRecord**;
|
||||
Each **DBRecord** instance has a record name that is unique within the local area network.
|
||||
|
||||
A channel name is a **recordname.fieldName**.
|
||||
|
||||
If the fieldname is not specified then **.VAL** is assumed and the record support methods shown
|
||||
above can also be used to get additional data from the record.
|
||||
|
||||
Any field that is accessable by client code must have a vald DBF_ type.
|
||||
|
||||
A client gets/puts data via a **DBR_*** request.
|
||||
|
||||
The basic DBR types are:
|
||||
```
|
||||
rawtype DBR
|
||||
|
||||
char[MAX_STRING_SIZE] DBR_STRING
|
||||
epicsInt8 DBR_CHAR
|
||||
epicsInt16 DBR_SHORT
|
||||
epicsInt32 DBR_LONG
|
||||
float DBF_FLOAT
|
||||
double DBF_DOUBLE
|
||||
epicsUInt16 DBR_ENUM
|
||||
```
|
||||
|
||||
In addition to the DBR basic types the following DBR types provide additional data:
|
||||
|
||||
```
|
||||
DBR one of the types above.
|
||||
DBR_STATUS_* adds status and severity to DBR.
|
||||
DBR_TIME_* adds epicsTimeStamp to DBR_STATUS.
|
||||
DBR_GR_* adds display limits to DBR_STATUS. NOTE: no epicsTimeStamp
|
||||
DBR_CTRL_ adds control limits to DBR_GR. NOTE: no epicsTimeStamp
|
||||
DBR_CTRL_ENUM This is a special case.
|
||||
```
|
||||
|
||||
NOTES:
|
||||
|
||||
* status, severity, and epicsTimeStamp are the same for each DBR type.
|
||||
* limits have the same types as the correspondng DBR type.
|
||||
* server converts limits from double to the DBR type.
|
||||
* GR and CTRL have precision only for DBR_FLOAT and DBR_DOUBLE
|
||||
|
||||
|
||||
Some examples:
|
||||
|
||||
```
|
||||
DBR_STS_DOUBLE returns a double status structure (dbr_sts_double)
|
||||
where
|
||||
struct dbr_sts_double{
|
||||
dbr_short_t status; /* status of value */
|
||||
dbr_short_t severity; /* severity of alarm */
|
||||
dbr_long_t RISC_pad; /* RISC alignment */
|
||||
dbr_double_t value; /* current value */
|
||||
};
|
||||
|
||||
DBR_TIME_DOUBLE returns a double time structure (dbr_time_double)
|
||||
where
|
||||
struct dbr_time_double{
|
||||
dbr_short_t status; /* status of value */
|
||||
dbr_short_t severity; /* severity of alarm */
|
||||
epicsTimeStamp stamp; /* time stamp */
|
||||
dbr_long_t RISC_pad; /* RISC alignment */
|
||||
dbr_double_t value; /* current value */
|
||||
};
|
||||
|
||||
DBR_GR_SHORT returns a graphic short structure (dbr_gr_short)
|
||||
where
|
||||
struct dbr_gr_short{
|
||||
dbr_short_t status; /* status of value */
|
||||
dbr_short_t severity; /* severity of alarm */
|
||||
char units[MAX_UNITS_SIZE]; /* units of value */
|
||||
dbr_short_t upper_disp_limit; /* upper limit of graph */
|
||||
dbr_short_t lower_disp_limit; /* lower limit of graph */
|
||||
dbr_short_t upper_alarm_limit;
|
||||
dbr_short_t upper_warning_limit;
|
||||
dbr_short_t lower_warning_limit;
|
||||
dbr_short_t lower_alarm_limit;
|
||||
dbr_short_t value; /* current value */
|
||||
};
|
||||
|
||||
DBR_GR_DOUBLE returns a graphic double structure (dbr_gr_double)
|
||||
where
|
||||
struct dbr_gr_double{
|
||||
dbr_short_t status; /* status of value */
|
||||
dbr_short_t severity; /* severity of alarm */
|
||||
dbr_short_t precision; /* number of decimal places */
|
||||
dbr_short_t RISC_pad0; /* RISC alignment */
|
||||
char units[MAX_UNITS_SIZE]; /* units of value */
|
||||
dbr_double_t upper_disp_limit; /* upper limit of graph */
|
||||
dbr_double_t lower_disp_limit; /* lower limit of graph */
|
||||
dbr_double_t upper_alarm_limit;
|
||||
dbr_double_t upper_warning_limit;
|
||||
dbr_double_t lower_warning_limit;
|
||||
dbr_double_t lower_alarm_limit;
|
||||
dbr_double_t value; /* current value */
|
||||
};
|
||||
|
||||
DBR_CTRL_DOUBLE returns a control double structure (dbr_ctrl_double)
|
||||
where
|
||||
struct dbr_ctrl_double{
|
||||
dbr_short_t status; /* status of value */
|
||||
dbr_short_t severity; /* severity of alarm */
|
||||
dbr_short_t precision; /* number of decimal places */
|
||||
dbr_short_t RISC_pad0; /* RISC alignment */
|
||||
char units[MAX_UNITS_SIZE]; /* units of value */
|
||||
dbr_double_t upper_disp_limit; /* upper limit of graph */
|
||||
dbr_double_t lower_disp_limit; /* lower limit of graph */
|
||||
dbr_double_t upper_alarm_limit;
|
||||
dbr_double_t upper_warning_limit;
|
||||
dbr_double_t lower_warning_limit;
|
||||
dbr_double_t lower_alarm_limit;
|
||||
dbr_double_t upper_ctrl_limit; /* upper control limit */
|
||||
dbr_double_t lower_ctrl_limit; /* lower control limit */
|
||||
dbr_double_t value; /* current value */
|
||||
};
|
||||
|
||||
|
||||
DBR_CTRL_ENUM returns a control enum structure (dbr_ctrl_enum)
|
||||
where
|
||||
struct dbr_ctrl_enum{
|
||||
dbr_short_t status; /* status of value */
|
||||
dbr_short_t severity; /* severity of alarm */
|
||||
dbr_short_t no_str; /* number of strings */
|
||||
char strs[MAX_ENUM_STATES][MAX_ENUM_STRING_SIZE];
|
||||
/* state strings */
|
||||
dbr_enum_t value; /* current value */
|
||||
};
|
||||
```
|
||||
|
||||
### PVData for a DBRrecord via ca provider
|
||||
|
||||
**pvAccessCPP/src/ca** has files **dbdToPv.h** and **dbdToPv.cpp**.
|
||||
This is the code that converts between DBD data and pvData.
|
||||
|
||||
This code must decide which of the many **DBR_*** types to use.
|
||||
|
||||
|
||||
There is a static method:
|
||||
|
||||
```
|
||||
static DbdToPvPtr create(
|
||||
CAChannelPtr const & caChannel,
|
||||
epics::pvData::PVStructurePtr const & pvRequest,
|
||||
IOType ioType); // one of getIO, putIO, and monitorIO
|
||||
```
|
||||
|
||||
|
||||
When this is called the first thing is to determine which fields are requested by the client.
|
||||
This is from the set **value**, **alarm**, **timeStamp**. **display**, **control** , and **valueAlarm**.
|
||||
|
||||
|
||||
* If the ioType is putIO only **value** is valid.
|
||||
* If the channel type is **DBR_ENUM** then **display**, **control** , and **valueAlarm** are ignored.
|
||||
* If the channel is an array then **control** , and **valueAlarm** are ignored.
|
||||
* If the channel type is **DBR_STRING** then **display**, **control** , and **valueAlarm** are ignored.
|
||||
* If any of **display**, **control** , and **valueAlarm** are still allowed then **timeStamp** is ignored,
|
||||
because the DBR type selected will not return the timeStamp.
|
||||
|
||||
If ths channel type is **DBR_ENUM** a one time **ca_array_get_callback(DBR_GR_ENUM...** request is issued
|
||||
to get the choices for the enumerated value.
|
||||
|
||||
Depending or which fields are still valid, the DBR type is obtained via
|
||||
|
||||
* If any of **display**, **control** ,or **valueAlarm** is valid then **dbf_type_to_DBR_CTRL(caValueType)** .
|
||||
* else If **alarm** or **timeStamp** is valid then **dbf_type_to_DBR_TIME(caValueType)** .
|
||||
* else **dbf_type_to_DBR(caValueType)**
|
||||
|
||||
Where **caValueType** is one of DBR_STRING, DBR_SHORT, DBR_FLOAT, DBR_ENUM, DBR_CHAR, DBR_LONG, DBR_DOUBLE.
|
||||
|
||||
If **display** is still valid then the following call is made:
|
||||
|
||||
```
|
||||
string name(caChannel->getChannelName() + ".DESC");
|
||||
int result = ca_create_channel(name.c_str(),
|
||||
...
|
||||
```
|
||||
When the channel connects a get is issued to get the value for **display.description**.
|
||||
|
||||
## <a name="S-plugin"></a>Developing plugins for ca provider
|
||||
|
||||
This section provides guidelines for code developers that use **ca** to connect a client to a server.
|
||||
This includes plugins for things like MEDM, EDM, caqtDM, etc.
|
||||
But also means any code that use **ca**: pvget, pvput, pvaClientCPP, exampleCPP/exampleClient, etc.
|
||||
|
||||
The **channel access** reference manual describes channel context:
|
||||
|
||||
[CA Client Contexts and Application Specific Auxiliary Threads](https://epics.anl.gov/base/R7-0/1-docs/CAref.html#Client2)
|
||||
|
||||
A brief summary of channel context is:
|
||||
|
||||
|
||||
* Only the thread that calls CAClientFactory::start() and associated auxillary threads
|
||||
can call **ca_xxx** functions.
|
||||
|
||||
The public access to **ca** is:
|
||||
|
||||
```
|
||||
class epicsShareClass CAClientFactory
|
||||
{
|
||||
public:
|
||||
/** @brief start provider ca
|
||||
*
|
||||
*/
|
||||
static void start();
|
||||
/** @brief get the ca_client_context
|
||||
*
|
||||
* This can be called by an application specific auxiliary thread.
|
||||
* See ca documentation. Not for casual use.
|
||||
*/
|
||||
static ca_client_context * get_ca_client_context();
|
||||
/** @brief stop provider ca
|
||||
*
|
||||
* This does nothing since epicsAtExit is used to destroy the instance.
|
||||
*/
|
||||
static void stop();
|
||||
};
|
||||
```
|
||||
Any code that uses **ca** must call **CAClientFactory::start()** before making any pvAccess client requests.
|
||||
|
||||
ca_context_create is called for the thread that calls CAClientFactory::start().
|
||||
|
||||
If the client creates auxillary threads the make pvAccess client requests then the auxillary threads will automatically become
|
||||
a **ca** auxilary thread.
|
||||
|
||||
|
||||
[Deadlock in ca_clear_subscription()](https://bugs.launchpad.net/epics-base/7.0/+bug/1751380)
|
||||
|
||||
Shows a problem with monitor callbacks.
|
||||
A test was created that shows that the same problem can occur with a combination of rapid get, put and monitor events.
|
||||
|
||||
|
||||
In order to prevent this problem **ca** creates the following threads:
|
||||
**getEventThread**, **putEventThread**, and **monitorEventThread**.
|
||||
|
||||
All client callbacks are made via one of these threads.
|
||||
For example a call to the requester's **monitorEvent** method is made from the monitorEventThread.
|
||||
|
||||
**Notes**
|
||||
|
||||
* These threads do not call **ca_attach_context**.
|
||||
* No **ca_xxx** function should be called from the requester's callback method.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@ TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
LIBRARY += pvAccessCA
|
||||
pvAccessCA_LIBS += ca pvAccess pvData
|
||||
LIB_LIBS += Com
|
||||
pvAccessCA_LIBS += pvAccess pvData ca Com
|
||||
|
||||
SHRLIB_VERSION ?= $(EPICS_PVA_MAJOR_VERSION).$(EPICS_PVA_MINOR_VERSION).$(EPICS_PVA_MAINTENANCE_VERSION)
|
||||
|
||||
@@ -11,10 +10,12 @@ SHRLIB_VERSION ?= $(EPICS_PVA_MAJOR_VERSION).$(EPICS_PVA_MINOR_VERSION).$(EPICS_
|
||||
LIB_SYS_LIBS_WIN32 += ws2_32
|
||||
|
||||
INC += pv/caProvider.h
|
||||
INC += pv/caStatus.h
|
||||
|
||||
pvAccessCA_SRCS += monitorEventThread.cpp
|
||||
pvAccessCA_SRCS += getDoneThread.cpp
|
||||
pvAccessCA_SRCS += putDoneThread.cpp
|
||||
pvAccessCA_SRCS += caProvider.cpp
|
||||
pvAccessCA_SRCS += caChannel.cpp
|
||||
pvAccessCA_SRCS += caStatus.cpp
|
||||
pvAccessCA_SRCS += dbdToPv.cpp
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
1591
src/ca/caChannel.cpp
1591
src/ca/caChannel.cpp
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,11 @@
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author msekoranja, mrk
|
||||
* @date 2018.07
|
||||
*/
|
||||
|
||||
#ifndef CACHANNEL_H
|
||||
#define CACHANNEL_H
|
||||
|
||||
@@ -11,17 +16,34 @@
|
||||
#include <vector>
|
||||
|
||||
#include <pv/pvAccess.h>
|
||||
|
||||
#include <pv/event.h>
|
||||
|
||||
/* for CA */
|
||||
#include <cadef.h>
|
||||
|
||||
#include "caProviderPvt.h"
|
||||
#include "dbdToPv.h"
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
namespace ca {
|
||||
|
||||
class NotifyMonitorRequester;
|
||||
typedef std::tr1::shared_ptr<NotifyMonitorRequester> NotifyMonitorRequesterPtr;
|
||||
class MonitorEventThread;
|
||||
typedef std::tr1::shared_ptr<MonitorEventThread> MonitorEventThreadPtr;
|
||||
|
||||
class NotifyGetRequester;
|
||||
typedef std::tr1::shared_ptr<NotifyGetRequester> NotifyGetRequesterPtr;
|
||||
typedef std::tr1::weak_ptr<NotifyGetRequester> NotifyGetRequesterWPtr;
|
||||
class GetDoneThread;
|
||||
typedef std::tr1::shared_ptr<GetDoneThread> GetDoneThreadPtr;
|
||||
|
||||
class NotifyPutRequester;
|
||||
typedef std::tr1::shared_ptr<NotifyPutRequester> NotifyPutRequesterPtr;
|
||||
typedef std::tr1::weak_ptr<NotifyPutRequester> NotifyPutRequesterWPtr;
|
||||
class PutDoneThread;
|
||||
typedef std::tr1::shared_ptr<PutDoneThread> PutDoneThreadPtr;
|
||||
|
||||
class CAChannelGetField;
|
||||
typedef std::tr1::shared_ptr<CAChannelGetField> CAChannelGetFieldPtr;
|
||||
@@ -53,220 +75,140 @@ class CAChannel :
|
||||
public Channel,
|
||||
public std::tr1::enable_shared_from_this<CAChannel>
|
||||
{
|
||||
|
||||
public:
|
||||
POINTER_DEFINITIONS(CAChannel);
|
||||
|
||||
static size_t num_instances;
|
||||
|
||||
static CAChannelPtr create(CAChannelProvider::shared_pointer const & channelProvider,
|
||||
std::string const & channelName,
|
||||
short priority,
|
||||
ChannelRequester::shared_pointer const & channelRequester);
|
||||
|
||||
static CAChannelPtr create(
|
||||
CAChannelProvider::shared_pointer const & channelProvider,
|
||||
std::string const & channelName,
|
||||
short priority,
|
||||
ChannelRequester::shared_pointer const & channelRequester);
|
||||
virtual ~CAChannel();
|
||||
|
||||
void connected();
|
||||
void disconnected();
|
||||
|
||||
chid getChannelID();
|
||||
chtype getNativeType();
|
||||
unsigned getElementCount();
|
||||
epics::pvData::Structure::const_shared_pointer getStructure();
|
||||
|
||||
/* --------------- epics::pvAccess::Channel --------------- */
|
||||
|
||||
virtual std::tr1::shared_ptr<ChannelProvider> getProvider();
|
||||
virtual std::string getRemoteAddress();
|
||||
virtual ConnectionState getConnectionState();
|
||||
virtual std::string getChannelName();
|
||||
virtual std::tr1::shared_ptr<ChannelRequester> getChannelRequester();
|
||||
|
||||
virtual void getField(GetFieldRequester::shared_pointer const & requester,std::string const & subField);
|
||||
|
||||
virtual AccessRights getAccessRights(epics::pvData::PVField::shared_pointer const & pvField);
|
||||
|
||||
virtual ChannelGet::shared_pointer createChannelGet(
|
||||
ChannelGetRequester::shared_pointer const & channelGetRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
epics::pvData::PVStructurePtr const & pvRequest);
|
||||
virtual ChannelPut::shared_pointer createChannelPut(
|
||||
ChannelPutRequester::shared_pointer const & channelPutRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
epics::pvData::PVStructurePtr const & pvRequest);
|
||||
virtual Monitor::shared_pointer createMonitor(
|
||||
MonitorRequester::shared_pointer const & monitorRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
epics::pvData::PVStructurePtr const & pvRequest);
|
||||
virtual void printInfo(std::ostream& out);
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
void attachContext();
|
||||
|
||||
void addChannelGet(const CAChannelGetPtr & get);
|
||||
void addChannelPut(const CAChannelPutPtr & get);
|
||||
void addChannelMonitor(const CAChannelMonitorPtr & get);
|
||||
void disconnectChannel();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/* --------------- Destroyable --------------- */
|
||||
|
||||
virtual void destroy() {}
|
||||
|
||||
CAChannel(std::string const & channelName,
|
||||
CAChannelProvider::shared_pointer const & channelProvider,
|
||||
ChannelRequester::shared_pointer const & channelRequester);
|
||||
void activate(short priority);
|
||||
void addMonitor(CAChannelMonitorPtr const & monitor);
|
||||
|
||||
std::string channelName;
|
||||
|
||||
CAChannelProviderWPtr channelProvider;
|
||||
ChannelRequester::weak_pointer channelRequester;
|
||||
|
||||
chid channelID;
|
||||
chtype channelType;
|
||||
unsigned elementCount;
|
||||
bool channelCreated;
|
||||
epics::pvData::Structure::const_shared_pointer structure;
|
||||
|
||||
epics::pvData::Mutex requestsMutex;
|
||||
|
||||
std::queue<CAChannelGetFieldPtr> getFieldQueue;
|
||||
std::queue<CAChannelPutPtr> putQueue;
|
||||
std::queue<CAChannelGetPtr> getQueue;
|
||||
std::queue<CAChannelMonitorPtr> monitorQueue;
|
||||
std::vector<CAChannelGetWPtr> getList;
|
||||
std::vector<CAChannelPutWPtr> putList;
|
||||
std::vector<CAChannelMonitorWPtr> monitorList;
|
||||
std::vector<CAChannelMonitorWPtr> monitorlist;
|
||||
};
|
||||
|
||||
|
||||
class CAChannelGet :
|
||||
public ChannelGet,
|
||||
public ChannelRequester,
|
||||
public ChannelBaseRequester,
|
||||
public std::tr1::enable_shared_from_this<CAChannelGet>
|
||||
{
|
||||
|
||||
public:
|
||||
POINTER_DEFINITIONS(CAChannelGet);
|
||||
|
||||
static size_t num_instances;
|
||||
|
||||
static CAChannelGet::shared_pointer create(CAChannel::shared_pointer const & channel,
|
||||
ChannelGetRequester::shared_pointer const & channelGetRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
epics::pvData::PVStructurePtr const & pvRequest);
|
||||
virtual ~CAChannelGet();
|
||||
|
||||
void getDone(struct event_handler_args &args);
|
||||
|
||||
/* --------------- epics::pvAccess::ChannelGet --------------- */
|
||||
virtual void get();
|
||||
|
||||
/* --------------- epics::pvData::ChannelRequest --------------- */
|
||||
virtual Channel::shared_pointer getChannel();
|
||||
virtual void cancel();
|
||||
virtual void lastRequest();
|
||||
|
||||
/* --------------- ChannelRequester --------------- */
|
||||
virtual void channelCreated(
|
||||
const epics::pvData::Status& status,
|
||||
Channel::shared_pointer const & channel);
|
||||
virtual void channelStateChange(
|
||||
Channel::shared_pointer const & channel,
|
||||
Channel::ConnectionState connectionState);
|
||||
virtual std::string getRequesterName() { return "CAChannelGet";}
|
||||
/* --------------- ChannelBaseRequester --------------- */
|
||||
virtual void channelDisconnect(bool destroy);
|
||||
virtual std::string getRequesterName();
|
||||
|
||||
void activate();
|
||||
|
||||
void notifyClient();
|
||||
private:
|
||||
/* --------------- Destroyable --------------- */
|
||||
virtual void destroy() {}
|
||||
|
||||
virtual void destroy() {}
|
||||
CAChannelGet(CAChannel::shared_pointer const & _channel,
|
||||
ChannelGetRequester::shared_pointer const & _channelGetRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
epics::pvData::PVStructurePtr const & pvRequest);
|
||||
|
||||
CAChannelPtr channel;
|
||||
ChannelGetRequester::weak_pointer channelGetRequester;
|
||||
const epics::pvData::PVStructure::shared_pointer pvRequest;
|
||||
|
||||
chtype getType;
|
||||
epics::pvData::PVStructurePtr const & pvRequest;
|
||||
epics::pvData::Status getStatus;
|
||||
GetDoneThreadPtr getDoneThread;
|
||||
NotifyGetRequesterPtr notifyGetRequester;
|
||||
DbdToPvPtr dbdToPv;
|
||||
epics::pvData::Mutex mutex;
|
||||
epics::pvData::PVStructure::shared_pointer pvStructure;
|
||||
epics::pvData::BitSet::shared_pointer bitSet;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CAChannelPut :
|
||||
public ChannelPut,
|
||||
public ChannelRequester,
|
||||
public ChannelBaseRequester,
|
||||
public std::tr1::enable_shared_from_this<CAChannelPut>
|
||||
{
|
||||
|
||||
public:
|
||||
POINTER_DEFINITIONS(CAChannelPut);
|
||||
|
||||
static size_t num_instances;
|
||||
|
||||
static CAChannelPut::shared_pointer create(CAChannel::shared_pointer const & channel,
|
||||
ChannelPutRequester::shared_pointer const & channelPutRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
epics::pvData::PVStructurePtr const & pvRequest);
|
||||
virtual ~CAChannelPut();
|
||||
|
||||
void putDone(struct event_handler_args &args);
|
||||
void getDone(struct event_handler_args &args);
|
||||
|
||||
/* --------------- epics::pvAccess::ChannelPut --------------- */
|
||||
|
||||
virtual void put(
|
||||
epics::pvData::PVStructure::shared_pointer const & pvPutStructure,
|
||||
epics::pvData::BitSet::shared_pointer const & putBitSet
|
||||
);
|
||||
virtual void get();
|
||||
|
||||
/* --------------- epics::pvData::ChannelRequest --------------- */
|
||||
|
||||
virtual Channel::shared_pointer getChannel();
|
||||
virtual void cancel();
|
||||
virtual void lastRequest();
|
||||
|
||||
/* --------------- ChannelRequester --------------- */
|
||||
virtual void channelCreated(
|
||||
const epics::pvData::Status& status,
|
||||
Channel::shared_pointer const & channel);
|
||||
virtual void channelStateChange(
|
||||
Channel::shared_pointer const & channel,
|
||||
Channel::ConnectionState connectionState);
|
||||
virtual std::string getRequesterName() { return "CAChannelPut";}
|
||||
/* --------------- ChannelBaseRequester --------------- */
|
||||
virtual void channelDisconnect(bool destroy);
|
||||
|
||||
void activate();
|
||||
|
||||
virtual std::string getRequesterName();
|
||||
void activate();
|
||||
void notifyClient();
|
||||
private:
|
||||
/* --------------- Destroyable --------------- */
|
||||
|
||||
virtual void destroy() {}
|
||||
|
||||
virtual void destroy() {}
|
||||
CAChannelPut(CAChannel::shared_pointer const & _channel,
|
||||
ChannelPutRequester::shared_pointer const & _channelPutRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
epics::pvData::PVStructurePtr const & pvRequest);
|
||||
CAChannelPtr channel;
|
||||
ChannelPutRequester::weak_pointer channelPutRequester;
|
||||
const epics::pvData::PVStructure::shared_pointer pvRequest;
|
||||
bool block;
|
||||
|
||||
chtype getType;
|
||||
bool isPut;
|
||||
epics::pvData::Status getStatus;
|
||||
epics::pvData::Status putStatus;
|
||||
PutDoneThreadPtr putDoneThread;
|
||||
NotifyPutRequesterPtr notifyPutRequester;
|
||||
DbdToPvPtr dbdToPv;
|
||||
epics::pvData::Mutex mutex;
|
||||
epics::pvData::PVStructure::shared_pointer pvStructure;
|
||||
epics::pvData::BitSet::shared_pointer bitSet;
|
||||
};
|
||||
@@ -276,66 +218,46 @@ typedef std::tr1::shared_ptr<CACMonitorQueue> CACMonitorQueuePtr;
|
||||
|
||||
class CAChannelMonitor :
|
||||
public Monitor,
|
||||
public ChannelRequester,
|
||||
public ChannelBaseRequester,
|
||||
public std::tr1::enable_shared_from_this<CAChannelMonitor>
|
||||
{
|
||||
|
||||
public:
|
||||
POINTER_DEFINITIONS(CAChannelMonitor);
|
||||
|
||||
static size_t num_instances;
|
||||
|
||||
static CAChannelMonitor::shared_pointer create(CAChannel::shared_pointer const & channel,
|
||||
MonitorRequester::shared_pointer const & monitorRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
epics::pvData::PVStructurePtr const & pvRequest);
|
||||
virtual ~CAChannelMonitor();
|
||||
|
||||
void subscriptionEvent(struct event_handler_args &args);
|
||||
|
||||
/* --------------- Monitor --------------- */
|
||||
|
||||
virtual epics::pvData::Status start();
|
||||
virtual epics::pvData::Status stop();
|
||||
virtual MonitorElementPtr poll();
|
||||
virtual void release(MonitorElementPtr const & monitorElement);
|
||||
|
||||
/* --------------- epics::pvData::ChannelRequest --------------- */
|
||||
virtual void cancel();
|
||||
/* --------------- ChannelRequester --------------- */
|
||||
virtual void channelCreated(
|
||||
const epics::pvData::Status& status,
|
||||
Channel::shared_pointer const & channel);
|
||||
virtual void channelStateChange(
|
||||
Channel::shared_pointer const & channel,
|
||||
Channel::ConnectionState connectionState);
|
||||
virtual std::string getRequesterName() { return "CAChannelMonitor";}
|
||||
/* --------------- ChannelBaseRequester --------------- */
|
||||
virtual void channelDisconnect(bool destroy);
|
||||
virtual std::string getRequesterName();
|
||||
void activate();
|
||||
void notifyClient();
|
||||
private:
|
||||
/* --------------- Destroyable --------------- */
|
||||
virtual void destroy() {}
|
||||
|
||||
CAChannelMonitor(CAChannel::shared_pointer const & _channel,
|
||||
MonitorRequester::shared_pointer const & _monitorRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
|
||||
epics::pvData::PVStructurePtr const & pvRequest);
|
||||
CAChannelPtr channel;
|
||||
MonitorRequester::weak_pointer monitorRequester;
|
||||
const epics::pvData::PVStructure::shared_pointer pvRequest;
|
||||
bool isStarted;
|
||||
chtype getType;
|
||||
MonitorEventThreadPtr monitorEventThread;
|
||||
evid pevid;
|
||||
NotifyMonitorRequesterPtr notifyMonitorRequester;
|
||||
|
||||
DbdToPvPtr dbdToPv;
|
||||
epics::pvData::Mutex mutex;
|
||||
epics::pvData::PVStructure::shared_pointer pvStructure;
|
||||
evid eventID;
|
||||
epics::pvData::MonitorElementPtr activeElement;
|
||||
|
||||
CACMonitorQueuePtr monitorQueue;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}}}
|
||||
|
||||
#endif /* CACHANNEL_H */
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <cadef.h>
|
||||
#include <epicsSignal.h>
|
||||
#include <epicsThread.h>
|
||||
#include <epicsExit.h>
|
||||
#include <pv/logger.h>
|
||||
#include <pv/configuration.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/reftrack.h>
|
||||
|
||||
#include "monitorEventThread.h"
|
||||
#include "getDoneThread.h"
|
||||
#include "putDoneThread.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/caProvider.h>
|
||||
@@ -31,8 +31,6 @@ using namespace epics::pvData;
|
||||
catch (std::exception &e) { LOG(logLevelError, "Unhandled exception caught from client code at %s:%d: %s", __FILE__, __LINE__, e.what()); } \
|
||||
catch (...) { LOG(logLevelError, "Unhandled exception caught from client code at %s:%d.", __FILE__, __LINE__); }
|
||||
|
||||
size_t CAChannelProvider::num_instances;
|
||||
|
||||
CAChannelProvider::CAChannelProvider()
|
||||
: current_context(0)
|
||||
{
|
||||
@@ -40,13 +38,14 @@ CAChannelProvider::CAChannelProvider()
|
||||
}
|
||||
|
||||
CAChannelProvider::CAChannelProvider(const std::tr1::shared_ptr<Configuration>&)
|
||||
: current_context(0)
|
||||
: current_context(0),
|
||||
monitorEventThread(MonitorEventThread::get()),
|
||||
getDoneThread(GetDoneThread::get()),
|
||||
putDoneThread(PutDoneThread::get())
|
||||
{
|
||||
if(DEBUG_LEVEL>0) {
|
||||
std::cout<< "CAChannelProvider::CAChannelProvider\n";
|
||||
}
|
||||
// Ignoring Configuration as CA only allows config via. environment,
|
||||
// and we don't want to change this here.
|
||||
initialize();
|
||||
}
|
||||
|
||||
@@ -60,7 +59,8 @@ CAChannelProvider::~CAChannelProvider()
|
||||
std::queue<CAChannelPtr> channelQ;
|
||||
{
|
||||
Lock lock(channelListMutex);
|
||||
for(size_t i=0; i< caChannelList.size(); ++i) {
|
||||
for(size_t i=0; i< caChannelList.size(); ++i)
|
||||
{
|
||||
CAChannelPtr caChannel(caChannelList[i].lock());
|
||||
if(caChannel) channelQ.push(caChannel);
|
||||
}
|
||||
@@ -68,16 +68,21 @@ CAChannelProvider::~CAChannelProvider()
|
||||
}
|
||||
while(!channelQ.empty()) {
|
||||
if(DEBUG_LEVEL>0) {
|
||||
std::cout << "disconnectAllChannels calling disconnectChannel "
|
||||
std::cout << "~CAChannelProvider() calling disconnectChannel "
|
||||
<< channelQ.front()->getChannelName()
|
||||
<< std::endl;
|
||||
}
|
||||
channelQ.front()->disconnectChannel();
|
||||
channelQ.pop();
|
||||
}
|
||||
attachContext();
|
||||
ca_flush_io();
|
||||
monitorEventThread->stop();
|
||||
getDoneThread->stop();
|
||||
putDoneThread->stop();
|
||||
if(DEBUG_LEVEL>0) {
|
||||
std::cout << "CAChannelProvider::~CAChannelProvider() calling ca_context_destroy\n";
|
||||
}
|
||||
ca_context_destroy();
|
||||
//std::cout << "CAChannelProvider::~CAChannelProvider() returning\n";
|
||||
}
|
||||
|
||||
std::string CAChannelProvider::getProviderName()
|
||||
@@ -90,12 +95,12 @@ ChannelFind::shared_pointer CAChannelProvider::channelFind(
|
||||
ChannelFindRequester::shared_pointer const & channelFindRequester)
|
||||
{
|
||||
if (channelName.empty())
|
||||
throw std::invalid_argument("empty channel name");
|
||||
throw std::invalid_argument("CAChannelProvider::channelFind empty channel name");
|
||||
|
||||
if (!channelFindRequester)
|
||||
throw std::invalid_argument("null requester");
|
||||
throw std::invalid_argument("CAChannelProvider::channelFind null requester");
|
||||
|
||||
Status errorStatus(Status::STATUSTYPE_ERROR, "not implemented");
|
||||
Status errorStatus(Status::STATUSTYPE_ERROR, "CAChannelProvider::channelFind not implemented");
|
||||
ChannelFind::shared_pointer nullChannelFind;
|
||||
EXCEPTION_GUARD(channelFindRequester->channelFindResult(errorStatus, nullChannelFind, false));
|
||||
return nullChannelFind;
|
||||
@@ -105,9 +110,9 @@ ChannelFind::shared_pointer CAChannelProvider::channelList(
|
||||
ChannelListRequester::shared_pointer const & channelListRequester)
|
||||
{
|
||||
if (!channelListRequester.get())
|
||||
throw std::runtime_error("null requester");
|
||||
throw std::runtime_error("CAChannelProvider::channelList null requester");
|
||||
|
||||
Status errorStatus(Status::STATUSTYPE_ERROR, "not implemented");
|
||||
Status errorStatus(Status::STATUSTYPE_ERROR, "CAChannelProvider::channelList not implemented");
|
||||
ChannelFind::shared_pointer nullChannelFind;
|
||||
PVStringArray::const_svector none;
|
||||
EXCEPTION_GUARD(channelListRequester->channelListResult(errorStatus, nullChannelFind, none, false));
|
||||
@@ -131,7 +136,7 @@ Channel::shared_pointer CAChannelProvider::createChannel(
|
||||
std::string const & address)
|
||||
{
|
||||
if (!address.empty())
|
||||
throw std::invalid_argument("CA does not support 'address' parameter");
|
||||
throw std::invalid_argument("CAChannelProvider::createChannel does not support 'address' parameter");
|
||||
|
||||
return CAChannel::create(shared_from_this(), channelName, priority, channelRequester);
|
||||
}
|
||||
@@ -165,31 +170,29 @@ void CAChannelProvider::poll()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void CAChannelProvider::attachContext()
|
||||
{
|
||||
ca_client_context* thread_context = ca_current_context();
|
||||
if (thread_context == current_context) return;
|
||||
if (thread_context != NULL) {
|
||||
throw std::runtime_error("CAChannelProvider: Foreign CA context in use");
|
||||
throw std::runtime_error("CAChannelProvider::attachContext Foreign CA context in use");
|
||||
}
|
||||
int result = ca_attach_context(current_context);
|
||||
if (result != ECA_NORMAL) {
|
||||
std::cout <<
|
||||
"CA error %s occurred while calling ca_attach_context:"
|
||||
<< ca_message(result) << std::endl;
|
||||
std::string mess("CAChannelProvider::attachContext error calling ca_attach_context ");
|
||||
mess += ca_message(result);
|
||||
throw std::runtime_error(mess);
|
||||
}
|
||||
}
|
||||
|
||||
void CAChannelProvider::initialize()
|
||||
{
|
||||
if(DEBUG_LEVEL>0) std::cout << "CAChannelProvider::initialize()\n";
|
||||
/* Create Channel Access */
|
||||
int result = ca_context_create(ca_enable_preemptive_callback);
|
||||
if (result != ECA_NORMAL) {
|
||||
throw std::runtime_error(
|
||||
std::string("CA error %s occurred while trying to start channel access:")
|
||||
+ ca_message(result));
|
||||
std::string mess("CAChannelProvider::initialize error calling ca_context_create ");
|
||||
mess += ca_message(result);
|
||||
throw std::runtime_error(mess);
|
||||
}
|
||||
current_context = ca_current_context();
|
||||
}
|
||||
@@ -198,17 +201,10 @@ void CAClientFactory::start()
|
||||
{
|
||||
if(DEBUG_LEVEL>0) std::cout << "CAClientFactory::start()\n";
|
||||
if(ChannelProviderRegistry::clients()->getProvider("ca")) {
|
||||
// do not start twice
|
||||
return;
|
||||
}
|
||||
epicsSignalInstallSigAlarmIgnore();
|
||||
epicsSignalInstallSigPipeIgnore();
|
||||
registerRefCounter("CAChannelProvider", &CAChannelProvider::num_instances);
|
||||
registerRefCounter("CAChannel", &CAChannel::num_instances);
|
||||
registerRefCounter("CAChannelGet", &CAChannelGet::num_instances);
|
||||
registerRefCounter("CAChannelPut", &CAChannelPut::num_instances);
|
||||
registerRefCounter("CAChannelMonitor", &CAChannelMonitor::num_instances);
|
||||
|
||||
if(!ChannelProviderRegistry::clients()->add<CAChannelProvider>("ca", true))
|
||||
{
|
||||
throw std::runtime_error("CAClientFactory::start failed");
|
||||
@@ -220,8 +216,5 @@ void CAClientFactory::stop()
|
||||
// unregister now done with exit hook
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}}}
|
||||
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author msekoranja, mrk
|
||||
* @date 2018.07
|
||||
*/
|
||||
|
||||
#ifndef CAPROVIDERPVT_H
|
||||
#define CAPROVIDERPVT_H
|
||||
|
||||
@@ -19,6 +24,15 @@ namespace ca {
|
||||
|
||||
#define DEBUG_LEVEL 0
|
||||
|
||||
class MonitorEventThread;
|
||||
typedef std::tr1::shared_ptr<MonitorEventThread> MonitorEventThreadPtr;
|
||||
|
||||
class GetDoneThread;
|
||||
typedef std::tr1::shared_ptr<GetDoneThread> GetDoneThreadPtr;
|
||||
|
||||
class PutDoneThread;
|
||||
typedef std::tr1::shared_ptr<PutDoneThread> PutDoneThreadPtr;
|
||||
|
||||
class CAChannel;
|
||||
typedef std::tr1::shared_ptr<CAChannel> CAChannelPtr;
|
||||
typedef std::tr1::weak_ptr<CAChannel> CAChannelWPtr;
|
||||
@@ -33,9 +47,6 @@ class CAChannelProvider :
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(CAChannelProvider);
|
||||
|
||||
static size_t num_instances;
|
||||
|
||||
CAChannelProvider();
|
||||
CAChannelProvider(const std::tr1::shared_ptr<Configuration>&);
|
||||
virtual ~CAChannelProvider();
|
||||
@@ -66,22 +77,20 @@ public:
|
||||
virtual void flush();
|
||||
virtual void poll();
|
||||
|
||||
void addChannel(const CAChannelPtr & channel);
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
void attachContext();
|
||||
|
||||
void addChannel(const CAChannelPtr & channel);
|
||||
private:
|
||||
|
||||
virtual void destroy() EPICS_DEPRECATED {}
|
||||
void initialize();
|
||||
ca_client_context* current_context;
|
||||
epics::pvData::Mutex channelListMutex;
|
||||
std::vector<CAChannelWPtr> caChannelList;
|
||||
MonitorEventThreadPtr monitorEventThread;
|
||||
GetDoneThreadPtr getDoneThread;
|
||||
PutDoneThreadPtr putDoneThread;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}}}
|
||||
|
||||
#endif /* CAPROVIDERPVT_H */
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvAccessCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/caStatus.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
namespace ca {
|
||||
|
||||
epicsShareDef std::string dbrStatus2alarmMessage[] = {
|
||||
"NO_ALARM", // 0 ..
|
||||
"READ_ALARM",
|
||||
"WRITE_ALARM",
|
||||
"HIHI_ALARM",
|
||||
"HIGH_ALARM",
|
||||
"LOLO_ALARM",
|
||||
"LOW_ALARM",
|
||||
"STATE_ALARM",
|
||||
"COS_ALARM",
|
||||
"COMM_ALARM",
|
||||
"TIMEOUT_ALARM",
|
||||
"HW_LIMIT_ALARM",
|
||||
"CALC_ALARM",
|
||||
"SCAN_ALARM",
|
||||
"LINK_ALARM",
|
||||
"SOFT_ALARM",
|
||||
"BAD_SUB_ALARM",
|
||||
"UDF_ALARM",
|
||||
"DISABLE_ALARM",
|
||||
"SIMM_ALARM",
|
||||
"READ_ACCESS_ALARM",
|
||||
"WRITE_ACCESS_ALARM" // .. 21
|
||||
};
|
||||
|
||||
epicsShareDef int dbrStatus2alarmStatus[] = {
|
||||
noStatus, //"NO_ALARM"
|
||||
driverStatus, //"READ_ALARM",
|
||||
driverStatus, //"WRITE_ALARM",
|
||||
recordStatus, //"HIHI_ALARM",
|
||||
recordStatus, //"HIGH_ALARM",
|
||||
recordStatus, //"LOLO_ALARM",
|
||||
recordStatus, //"LOW_ALARM",
|
||||
recordStatus, //"STATE_ALARM",
|
||||
recordStatus, //"COS_ALARM",
|
||||
driverStatus, //"COMM_ALARM",
|
||||
driverStatus, //"TIMEOUT_ALARM",
|
||||
deviceStatus, //"HW_LIMIT_ALARM",
|
||||
recordStatus, //"CALC_ALARM",
|
||||
dbStatus, //"SCAN_ALARM",
|
||||
dbStatus, //"LINK_ALARM",
|
||||
dbStatus, //"SOFT_ALARM",
|
||||
confStatus, //"BAD_SUB_ALARM",
|
||||
recordStatus, //"UDF_ALARM",
|
||||
recordStatus, //"DISABLE_ALARM",
|
||||
recordStatus, //"SIMM_ALARM",
|
||||
clientStatus, //"READ_ACCESS_ALARM",
|
||||
clientStatus //"WRITE_ACCESS_ALARM" // .. 21
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
803
src/ca/dbdToPv.cpp
Normal file
803
src/ca/dbdToPv.cpp
Normal file
@@ -0,0 +1,803 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvAccessCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
|
||||
#include <epicsVersion.h>
|
||||
#include <sstream>
|
||||
#include <alarm.h>
|
||||
#include <alarmString.h>
|
||||
|
||||
#include <pv/standardField.h>
|
||||
#include <pv/logger.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/reftrack.h>
|
||||
#include <pv/convert.h>
|
||||
#include <pv/timeStamp.h>
|
||||
#include "caChannel.h"
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbdToPv.h"
|
||||
|
||||
using namespace epics::pvData;
|
||||
using std::string;
|
||||
using std::ostringstream;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
namespace ca {
|
||||
|
||||
#define CA_PRIORITY 50
|
||||
|
||||
static void enumChoicesHandler(struct event_handler_args args)
|
||||
{
|
||||
DbdToPv *dbdToPv = static_cast<DbdToPv*>(args.usr);
|
||||
dbdToPv->getChoicesDone(args);
|
||||
}
|
||||
|
||||
static void description_connection_handler(struct connection_handler_args args)
|
||||
{
|
||||
DbdToPv *dbdToPv = static_cast<DbdToPv*>(ca_puser(args.chid));
|
||||
dbdToPv->descriptionConnected(args);
|
||||
}
|
||||
|
||||
static void descriptionHandler(struct event_handler_args args)
|
||||
{
|
||||
DbdToPv *dbdToPv = static_cast<DbdToPv*>(args.usr);
|
||||
dbdToPv->getDescriptionDone(args);
|
||||
}
|
||||
|
||||
DbdToPvPtr DbdToPv::create(
|
||||
CAChannelPtr const & caChannel,
|
||||
PVStructurePtr const & pvRequest,
|
||||
IOType ioType)
|
||||
{
|
||||
DbdToPvPtr dbdToPv(new DbdToPv(ioType));
|
||||
dbdToPv->activate(caChannel,pvRequest);
|
||||
return dbdToPv;
|
||||
}
|
||||
|
||||
DbdToPv::DbdToPv(IOType ioType)
|
||||
: ioType(ioType),
|
||||
fieldRequested(false),
|
||||
alarmRequested(false),
|
||||
timeStampRequested(false),
|
||||
displayRequested(false),
|
||||
controlRequested(false),
|
||||
valueAlarmRequested(false),
|
||||
isArray(false),
|
||||
firstTime(true),
|
||||
choicesValid(false),
|
||||
waitForChoicesValid(false),
|
||||
caValueType(-1),
|
||||
caRequestType(-1),
|
||||
maxElements(0)
|
||||
{
|
||||
caTimeStamp.secPastEpoch = 0;
|
||||
caTimeStamp.nsec = 0;
|
||||
}
|
||||
|
||||
static ScalarType dbr2ST[] =
|
||||
{
|
||||
pvString, // DBR_STRING = 0
|
||||
pvShort, // DBR_SHORT. DBR_INT = 1
|
||||
pvFloat, // DBR_FLOAT = 2
|
||||
static_cast<ScalarType>(-1), // DBR_ENUM = 3
|
||||
pvByte, // DBR_CHAR = 4
|
||||
pvInt, // DBR_LONG = 5
|
||||
pvDouble // DBR_DOUBLE = 6
|
||||
};
|
||||
|
||||
static chtype getDbrType(const ScalarType scalarType)
|
||||
{
|
||||
switch(scalarType)
|
||||
{
|
||||
case pvString : return DBR_STRING;
|
||||
case pvByte : return DBR_CHAR;
|
||||
case pvShort : return DBR_SHORT;
|
||||
case pvInt : return DBR_LONG;
|
||||
case pvFloat : return DBR_FLOAT;
|
||||
case pvDouble : return DBR_DOUBLE;
|
||||
default: break;
|
||||
}
|
||||
throw std::runtime_error("getDbr: illegal scalarType");
|
||||
}
|
||||
|
||||
|
||||
void DbdToPv::activate(
|
||||
CAChannelPtr const & caChannel,
|
||||
PVStructurePtr const & pvRequest)
|
||||
{
|
||||
chid channelID = caChannel->getChannelID();
|
||||
chtype channelType = ca_field_type(channelID);
|
||||
caValueType = (channelType==DBR_ENUM ? DBR_ENUM : getDbrType(dbr2ST[channelType]));
|
||||
if(!pvRequest) {
|
||||
string mess(caChannel->getChannelName());
|
||||
mess += " DbdToPv::activate pvRequest is null";
|
||||
throw std::runtime_error(mess);
|
||||
}
|
||||
PVStructurePtr fieldPVStructure;
|
||||
if(pvRequest->getPVFields().size()==0) {
|
||||
fieldPVStructure = pvRequest;
|
||||
} else {
|
||||
fieldPVStructure = pvRequest->getSubField<PVStructure>("field");
|
||||
}
|
||||
if(!fieldPVStructure) {
|
||||
ostringstream mess;
|
||||
mess << caChannel->getChannelName()
|
||||
<< " DbdToPv::activate illegal pvRequest " << pvRequest;
|
||||
throw std::runtime_error(mess.str());
|
||||
}
|
||||
if(fieldPVStructure->getPVFields().size()==0)
|
||||
{
|
||||
fieldRequested = true;
|
||||
alarmRequested = true;
|
||||
timeStampRequested = true;
|
||||
displayRequested = true;
|
||||
controlRequested = true;
|
||||
valueAlarmRequested = true;
|
||||
} else {
|
||||
if(fieldPVStructure->getSubField("value")) fieldRequested = true;
|
||||
if(fieldPVStructure->getSubField("alarm")) alarmRequested = true;
|
||||
if(fieldPVStructure->getSubField("timeStamp")) timeStampRequested = true;
|
||||
if(fieldPVStructure->getSubField("display")) displayRequested = true;
|
||||
if(fieldPVStructure->getSubField("control")) controlRequested = true;
|
||||
if(fieldPVStructure->getSubField("valueAlarm")) valueAlarmRequested = true;
|
||||
}
|
||||
switch(ioType)
|
||||
{
|
||||
case getIO : break;
|
||||
case putIO:
|
||||
alarmRequested = false;
|
||||
timeStampRequested = false;
|
||||
displayRequested = false;
|
||||
controlRequested = false;
|
||||
valueAlarmRequested = false;
|
||||
break;
|
||||
case monitorIO: break;
|
||||
}
|
||||
StandardFieldPtr standardField = getStandardField();
|
||||
if(channelType==DBR_ENUM)
|
||||
{
|
||||
displayRequested = false;
|
||||
controlRequested = false;
|
||||
valueAlarmRequested = false;
|
||||
string properties;
|
||||
if(alarmRequested && timeStampRequested) {
|
||||
properties += "alarm,timeStamp";
|
||||
} else if(timeStampRequested) {
|
||||
properties += "timeStamp";
|
||||
} else if(alarmRequested) {
|
||||
properties += "alarm";
|
||||
}
|
||||
caRequestType = (properties.size()==0 ? DBR_ENUM : DBR_TIME_ENUM);
|
||||
structure = standardField->enumerated(properties);
|
||||
int result = ca_array_get_callback(DBR_GR_ENUM,
|
||||
1,
|
||||
channelID, enumChoicesHandler, this);
|
||||
if (result == ECA_NORMAL) result = ca_flush_io();
|
||||
if (result != ECA_NORMAL) {
|
||||
string mess(caChannel->getChannelName());
|
||||
mess += " DbdToPv::activate getting enum cnoices ";
|
||||
mess += ca_message(result);
|
||||
throw std::runtime_error(mess);
|
||||
}
|
||||
// NOTE: we do not wait here, since all subsequent request (over TCP) is serialized
|
||||
// and will guarantee that enumChoicesHandler is called first
|
||||
return;
|
||||
}
|
||||
maxElements = ca_element_count(channelID);
|
||||
if(maxElements!=1) isArray = true;
|
||||
if(isArray)
|
||||
{
|
||||
controlRequested = false;
|
||||
valueAlarmRequested = false;
|
||||
}
|
||||
ScalarType st = dbr2ST[channelType];
|
||||
if(st==pvString) {
|
||||
displayRequested = false;
|
||||
controlRequested = false;
|
||||
valueAlarmRequested = false;
|
||||
}
|
||||
if(controlRequested || displayRequested || valueAlarmRequested) timeStampRequested = false;
|
||||
FieldCreatePtr fieldCreate(FieldCreate::getFieldCreate());
|
||||
PVDataCreatePtr pvDataCreate(PVDataCreate::getPVDataCreate());
|
||||
FieldBuilderPtr fieldBuilder(fieldCreate->createFieldBuilder());
|
||||
if(fieldRequested) {
|
||||
if(isArray) {
|
||||
fieldBuilder->addArray("value",st);
|
||||
} else {
|
||||
fieldBuilder->add("value",st);
|
||||
}
|
||||
}
|
||||
if(alarmRequested) fieldBuilder->add("alarm",standardField->alarm());
|
||||
if(timeStampRequested) fieldBuilder->add("timeStamp",standardField->timeStamp());
|
||||
if(displayRequested) fieldBuilder->add("display",standardField->display());
|
||||
if(controlRequested) fieldBuilder->add("control",standardField->control());
|
||||
if(valueAlarmRequested) {
|
||||
switch(st)
|
||||
{
|
||||
case pvByte:
|
||||
fieldBuilder->add("valueAlarm",standardField->byteAlarm()); break;
|
||||
case pvShort:
|
||||
fieldBuilder->add("valueAlarm",standardField->shortAlarm()); break;
|
||||
case pvInt:
|
||||
fieldBuilder->add("valueAlarm",standardField->intAlarm()); break;
|
||||
case pvFloat:
|
||||
fieldBuilder->add("valueAlarm",standardField->floatAlarm()); break;
|
||||
case pvDouble:
|
||||
fieldBuilder->add("valueAlarm",standardField->doubleAlarm()); break;
|
||||
default:
|
||||
throw std::runtime_error("DbDToPv::activate: bad type");
|
||||
}
|
||||
}
|
||||
structure = fieldBuilder->createStructure();
|
||||
caRequestType = caValueType;
|
||||
if(displayRequested || controlRequested || valueAlarmRequested)
|
||||
{
|
||||
caRequestType = dbf_type_to_DBR_CTRL(caValueType);
|
||||
} else if(timeStampRequested || alarmRequested) {
|
||||
caRequestType = dbf_type_to_DBR_TIME(caValueType);
|
||||
} else {
|
||||
caRequestType = dbf_type_to_DBR(caValueType);
|
||||
}
|
||||
if(displayRequested) {
|
||||
chid channelID;
|
||||
string name(caChannel->getChannelName() + ".DESC");
|
||||
int result = ca_create_channel(name.c_str(),
|
||||
description_connection_handler,
|
||||
this,
|
||||
CA_PRIORITY, // TODO mapping
|
||||
&channelID);
|
||||
if (result == ECA_NORMAL) result = ca_flush_io();
|
||||
if (result != ECA_NORMAL) {
|
||||
string mess(caChannel->getChannelName());
|
||||
mess += " DbdToPv::activate getting description ";
|
||||
mess += ca_message(result);
|
||||
throw std::runtime_error(mess);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DbdToPv::descriptionConnected(struct connection_handler_args args)
|
||||
{
|
||||
if (args.op != CA_OP_CONN_UP) return;
|
||||
ca_array_get_callback(DBR_STRING,
|
||||
0,
|
||||
args.chid, descriptionHandler, this);
|
||||
}
|
||||
|
||||
void DbdToPv::getDescriptionDone(struct event_handler_args &args)
|
||||
{
|
||||
if(args.status!=ECA_NORMAL) return;
|
||||
const dbr_string_t *value = static_cast<const dbr_string_t *>(dbr_value_ptr(args.dbr,DBR_STRING));
|
||||
description = string(*value);
|
||||
ca_clear_channel(args.chid);
|
||||
}
|
||||
|
||||
void DbdToPv::getChoicesDone(struct event_handler_args &args)
|
||||
{
|
||||
if(args.status!=ECA_NORMAL)
|
||||
{
|
||||
string message("DbdToPv::getChoicesDone ca_message ");
|
||||
message += ca_message(args.status);
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
const dbr_gr_enum* dbr_enum_p = static_cast<const dbr_gr_enum*>(args.dbr);
|
||||
size_t num = dbr_enum_p->no_str;
|
||||
choices.reserve(num);
|
||||
for(size_t i=0; i<num; ++i) choices.push_back(string(&dbr_enum_p->strs[i][0]));
|
||||
bool signal = false;
|
||||
{
|
||||
Lock lock(choicesMutex);
|
||||
choicesValid = true;
|
||||
if(waitForChoicesValid) signal = true;
|
||||
}
|
||||
if(signal) choicesEvent.signal();
|
||||
}
|
||||
|
||||
chtype DbdToPv::getRequestType()
|
||||
{
|
||||
if(caRequestType<0) {
|
||||
throw std::runtime_error("DbDToPv::getRequestType: bad type");
|
||||
}
|
||||
return caRequestType;
|
||||
}
|
||||
|
||||
PVStructurePtr DbdToPv::createPVStructure()
|
||||
{
|
||||
return getPVDataCreate()->createPVStructure(structure);
|
||||
}
|
||||
|
||||
template<typename dbrT, typename pvT>
|
||||
void copy_DBRScalar(const void * dbr, PVScalar::shared_pointer const & pvScalar)
|
||||
{
|
||||
std::tr1::shared_ptr<pvT> value = std::tr1::static_pointer_cast<pvT>(pvScalar);
|
||||
value->put(static_cast<const dbrT*>(dbr)[0]);
|
||||
}
|
||||
|
||||
template<typename dbrT, typename pvT>
|
||||
void copy_DBRScalarArray(const void * dbr, unsigned count, PVScalarArray::shared_pointer const & pvArray)
|
||||
{
|
||||
std::tr1::shared_ptr<pvT> value = std::tr1::static_pointer_cast<pvT>(pvArray);
|
||||
typename pvT::svector temp(value->reuse());
|
||||
temp.resize(count);
|
||||
std::copy(
|
||||
static_cast<const dbrT*>(dbr),
|
||||
static_cast<const dbrT*>(dbr) + count,
|
||||
temp.begin());
|
||||
value->replace(freeze(temp));
|
||||
}
|
||||
|
||||
template<typename dbrT>
|
||||
void get_DBRControl(const void * dbr, double *upper_ctrl_limit,double *lower_ctrl_limit)
|
||||
{
|
||||
*upper_ctrl_limit = static_cast<const dbrT*>(dbr)->upper_ctrl_limit;
|
||||
*lower_ctrl_limit = static_cast<const dbrT*>(dbr)->lower_ctrl_limit;
|
||||
}
|
||||
|
||||
template<typename dbrT>
|
||||
void get_DBRDisplay(
|
||||
const void * dbr, double *upper_disp_limit,double *lower_disp_limit,string *units)
|
||||
{
|
||||
*upper_disp_limit = static_cast<const dbrT*>(dbr)->upper_disp_limit;
|
||||
*lower_disp_limit = static_cast<const dbrT*>(dbr)->lower_disp_limit;
|
||||
*units = static_cast<const dbrT*>(dbr)->units;
|
||||
}
|
||||
|
||||
template<typename dbrT>
|
||||
void get_DBRValueAlarm(
|
||||
const void * dbr,
|
||||
double *upper_alarm_limit,double *upper_warning_limit,
|
||||
double *lower_warning_limit,double *lower_alarm_limit)
|
||||
{
|
||||
*upper_alarm_limit = static_cast<const dbrT*>(dbr)->upper_alarm_limit;
|
||||
*upper_warning_limit = static_cast<const dbrT*>(dbr)->upper_warning_limit;
|
||||
*lower_warning_limit = static_cast<const dbrT*>(dbr)->lower_warning_limit;
|
||||
*lower_alarm_limit = static_cast<const dbrT*>(dbr)->lower_alarm_limit;
|
||||
}
|
||||
|
||||
Status DbdToPv::getFromDBD(
|
||||
PVStructurePtr const & pvStructure,
|
||||
BitSet::shared_pointer const & bitSet,
|
||||
struct event_handler_args &args)
|
||||
{
|
||||
if(args.status!=ECA_NORMAL)
|
||||
{
|
||||
Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(args.status)));
|
||||
return errorStatus;
|
||||
}
|
||||
if(fieldRequested)
|
||||
{
|
||||
void * value = dbr_value_ptr(args.dbr,caRequestType);
|
||||
if(isArray) {
|
||||
long count = args.count;
|
||||
PVScalarArrayPtr pvValue = pvStructure->getSubField<PVScalarArray>("value");
|
||||
switch(caValueType) {
|
||||
case DBR_STRING:
|
||||
{
|
||||
const dbr_string_t *dbrval = static_cast<const dbr_string_t *>(value);
|
||||
PVStringArrayPtr pvValue = pvStructure->getSubField<PVStringArray>("value");
|
||||
PVStringArray::svector arr(pvValue->reuse());
|
||||
arr.resize(count);
|
||||
std::copy(dbrval, dbrval + count, arr.begin());
|
||||
pvValue->replace(freeze(arr));
|
||||
break;
|
||||
}
|
||||
case DBR_CHAR:
|
||||
copy_DBRScalarArray<dbr_char_t,PVByteArray>(value,count,pvValue);
|
||||
break;
|
||||
case DBR_SHORT:
|
||||
copy_DBRScalarArray<dbr_short_t,PVShortArray>(value,count,pvValue);
|
||||
break;
|
||||
case DBR_LONG:
|
||||
copy_DBRScalarArray<dbr_int_t,PVIntArray>(value,count,pvValue);
|
||||
break;
|
||||
case DBR_FLOAT:
|
||||
copy_DBRScalarArray<dbr_float_t,PVFloatArray>(value,count,pvValue);
|
||||
break;
|
||||
case DBR_DOUBLE:
|
||||
copy_DBRScalarArray<dbr_double_t,PVDoubleArray>(value,count,pvValue);
|
||||
break;
|
||||
default:
|
||||
Status errorStatus(
|
||||
Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error"));
|
||||
return errorStatus;
|
||||
}
|
||||
} else {
|
||||
PVScalarPtr pvValue = pvStructure->getSubField<PVScalar>("value");
|
||||
switch(caValueType) {
|
||||
case DBR_ENUM:
|
||||
{
|
||||
const dbr_enum_t *dbrval = static_cast<const dbr_enum_t *>(value);
|
||||
PVIntPtr value = pvStructure->getSubField<PVInt>("value.index");
|
||||
value->put(*dbrval);
|
||||
PVStringArrayPtr pvChoices
|
||||
= pvStructure->getSubField<PVStringArray>("value.choices");
|
||||
if(pvChoices->getLength()==0)
|
||||
{
|
||||
ConvertPtr convert = getConvert();
|
||||
size_t n = choices.size();
|
||||
pvChoices->setLength(n);
|
||||
convert->fromStringArray(pvChoices,0,n,choices,0);
|
||||
bitSet->set(pvStructure->getSubField("value")->getFieldOffset());
|
||||
} else {
|
||||
bitSet->set(value->getFieldOffset());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DBR_STRING: copy_DBRScalar<dbr_string_t,PVString>(value,pvValue); break;
|
||||
case DBR_CHAR: copy_DBRScalar<dbr_char_t,PVByte>(value,pvValue); break;
|
||||
case DBR_SHORT: copy_DBRScalar<dbr_short_t,PVShort>(value,pvValue); break;
|
||||
case DBR_LONG: copy_DBRScalar<dbr_int_t,PVInt>(value,pvValue); break;
|
||||
case DBR_FLOAT: copy_DBRScalar<dbr_float_t,PVFloat>(value,pvValue); break;
|
||||
case DBR_DOUBLE: copy_DBRScalar<dbr_double_t,PVDouble>(value,pvValue); break;
|
||||
default:
|
||||
Status errorStatus(
|
||||
Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error"));
|
||||
return errorStatus;
|
||||
}
|
||||
}
|
||||
if(caValueType!=DBR_ENUM) {
|
||||
bitSet->set(pvStructure->getSubField("value")->getFieldOffset());
|
||||
}
|
||||
}
|
||||
if(alarmRequested) {
|
||||
// Note that status and severity are aways the first two members of DBR_
|
||||
const dbr_sts_string *data = static_cast<const dbr_sts_string *>(args.dbr);
|
||||
dbr_short_t status = data->status;
|
||||
dbr_short_t severity = data->severity;
|
||||
bool statusChanged = false;
|
||||
bool severityChanged = false;
|
||||
PVStructurePtr pvAlarm(pvStructure->getSubField<PVStructure>("alarm"));
|
||||
PVIntPtr pvSeverity(pvAlarm->getSubField<PVInt>("severity"));
|
||||
if(caAlarm.severity!=severity) {
|
||||
caAlarm.severity = severity;
|
||||
pvSeverity->put(severity);
|
||||
severityChanged = true;
|
||||
}
|
||||
PVStringPtr pvMessage(pvAlarm->getSubField<PVString>("message"));
|
||||
PVIntPtr pvStatus(pvAlarm->getSubField<PVInt>("status"));
|
||||
if(caAlarm.status!=status) {
|
||||
caAlarm.status = status;
|
||||
pvStatus->put(status);
|
||||
string message("UNKNOWN STATUS");
|
||||
if(status<=ALARM_NSTATUS) message = string(epicsAlarmConditionStrings[status]);
|
||||
pvMessage->put(message);
|
||||
statusChanged = true;
|
||||
}
|
||||
if(statusChanged&&severityChanged) {
|
||||
bitSet->set(pvAlarm->getFieldOffset());
|
||||
} else if(severityChanged) {
|
||||
bitSet->set(pvSeverity->getFieldOffset());
|
||||
} else if(statusChanged) {
|
||||
bitSet->set(pvStatus->getFieldOffset());
|
||||
bitSet->set(pvMessage->getFieldOffset());
|
||||
}
|
||||
}
|
||||
if(timeStampRequested) {
|
||||
// Note that epicsTimeStamp always follows status and severity
|
||||
const dbr_time_string *data = static_cast<const dbr_time_string *>(args.dbr);
|
||||
epicsTimeStamp stamp = data->stamp;
|
||||
PVStructurePtr pvTimeStamp(pvStructure->getSubField<PVStructure>("timeStamp"));
|
||||
if(caTimeStamp.secPastEpoch!=stamp.secPastEpoch) {
|
||||
caTimeStamp.secPastEpoch = stamp.secPastEpoch;
|
||||
PVLongPtr pvSeconds(pvTimeStamp->getSubField<PVLong>("secondsPastEpoch"));
|
||||
pvSeconds->put(stamp.secPastEpoch+posixEpochAtEpicsEpoch);
|
||||
bitSet->set(pvSeconds->getFieldOffset());
|
||||
}
|
||||
if(caTimeStamp.nsec!=stamp.nsec) {
|
||||
caTimeStamp.secPastEpoch = stamp.secPastEpoch;
|
||||
PVIntPtr pvNano(pvTimeStamp->getSubField<PVInt>("nanoseconds"));
|
||||
pvNano->put(stamp.nsec);
|
||||
bitSet->set(pvNano->getFieldOffset());
|
||||
}
|
||||
}
|
||||
if(controlRequested)
|
||||
{
|
||||
double upper_ctrl_limit = 0.0;
|
||||
double lower_ctrl_limit = 0.0;
|
||||
switch(caRequestType) {
|
||||
case DBR_CTRL_CHAR:
|
||||
get_DBRControl<dbr_ctrl_char>(args.dbr,&upper_ctrl_limit,&lower_ctrl_limit); break;
|
||||
case DBR_CTRL_SHORT:
|
||||
get_DBRControl<dbr_ctrl_short>(args.dbr,&upper_ctrl_limit,&lower_ctrl_limit); break;
|
||||
case DBR_CTRL_LONG:
|
||||
get_DBRControl<dbr_ctrl_long>(args.dbr,&upper_ctrl_limit,&lower_ctrl_limit); break;
|
||||
case DBR_CTRL_FLOAT:
|
||||
get_DBRControl<dbr_ctrl_float>(args.dbr,&upper_ctrl_limit,&lower_ctrl_limit); break;
|
||||
case DBR_CTRL_DOUBLE:
|
||||
get_DBRControl<dbr_ctrl_double>(args.dbr,&upper_ctrl_limit,&lower_ctrl_limit); break;
|
||||
default :
|
||||
throw std::runtime_error("DbdToPv::getFromDBD logic error");
|
||||
}
|
||||
PVStructurePtr pvControl(pvStructure->getSubField<PVStructure>("control"));
|
||||
if(caControl.upper_ctrl_limit!=upper_ctrl_limit) {
|
||||
caControl.upper_ctrl_limit = upper_ctrl_limit;
|
||||
PVDoublePtr pv = pvControl->getSubField<PVDouble>("limitHigh");
|
||||
pv->put(upper_ctrl_limit);
|
||||
bitSet->set(pv->getFieldOffset());
|
||||
}
|
||||
if(caControl.lower_ctrl_limit!=lower_ctrl_limit) {
|
||||
caControl.lower_ctrl_limit = lower_ctrl_limit;
|
||||
PVDoublePtr pv = pvControl->getSubField<PVDouble>("limitLow");
|
||||
pv->put(lower_ctrl_limit);
|
||||
bitSet->set(pv->getFieldOffset());
|
||||
}
|
||||
}
|
||||
if(displayRequested)
|
||||
{
|
||||
string units;
|
||||
string format;
|
||||
double upper_disp_limit = 0.0;
|
||||
double lower_disp_limit = 0.0;
|
||||
switch(caRequestType) {
|
||||
case DBR_CTRL_CHAR:
|
||||
get_DBRDisplay<dbr_ctrl_char>(args.dbr,&upper_disp_limit,&lower_disp_limit,&units);
|
||||
format = "I4"; break;
|
||||
case DBR_CTRL_SHORT:
|
||||
get_DBRDisplay<dbr_ctrl_short>(args.dbr,&upper_disp_limit,&lower_disp_limit,&units);
|
||||
format = "I6"; break;
|
||||
case DBR_CTRL_LONG:
|
||||
get_DBRDisplay<dbr_ctrl_long>(args.dbr,&upper_disp_limit,&lower_disp_limit,&units);
|
||||
format = "I12"; break;
|
||||
case DBR_CTRL_FLOAT:
|
||||
get_DBRDisplay<dbr_ctrl_float>(args.dbr,&upper_disp_limit,&lower_disp_limit,&units);
|
||||
{
|
||||
const dbr_ctrl_float *data = static_cast<const dbr_ctrl_float *>(args.dbr);
|
||||
int prec = data->precision;
|
||||
ostringstream s;
|
||||
s << "F" << prec + 6 << "." << prec;
|
||||
format = s.str();
|
||||
}
|
||||
break;
|
||||
case DBR_CTRL_DOUBLE:
|
||||
get_DBRDisplay<dbr_ctrl_double>(args.dbr,&upper_disp_limit,&lower_disp_limit,&units);
|
||||
{
|
||||
const dbr_ctrl_double *data = static_cast<const dbr_ctrl_double *>(args.dbr);
|
||||
int prec = data->precision;
|
||||
ostringstream s;
|
||||
s << "F" << prec + 6 << "." << prec;
|
||||
format = s.str();
|
||||
}
|
||||
break;
|
||||
default :
|
||||
throw std::runtime_error("DbdToPv::getFromDBD logic error");
|
||||
}
|
||||
PVStructurePtr pvDisplay(pvStructure->getSubField<PVStructure>("display"));
|
||||
if(caDisplay.lower_disp_limit!=lower_disp_limit) {
|
||||
caDisplay.lower_disp_limit = lower_disp_limit;
|
||||
PVDoublePtr pvDouble = pvDisplay->getSubField<PVDouble>("limitLow");
|
||||
pvDouble->put(lower_disp_limit);
|
||||
bitSet->set(pvDouble->getFieldOffset());
|
||||
}
|
||||
if(caDisplay.upper_disp_limit!=upper_disp_limit) {
|
||||
caDisplay.upper_disp_limit = upper_disp_limit;
|
||||
PVDoublePtr pvDouble = pvDisplay->getSubField<PVDouble>("limitHigh");
|
||||
pvDouble->put(upper_disp_limit);
|
||||
bitSet->set(pvDouble->getFieldOffset());
|
||||
}
|
||||
if(caDisplay.units!=units) {
|
||||
caDisplay.units = units;
|
||||
PVStringPtr pvString = pvDisplay->getSubField<PVString>("units");
|
||||
pvString->put(units);
|
||||
bitSet->set(pvString->getFieldOffset());
|
||||
}
|
||||
if(caDisplay.format!=format) {
|
||||
caDisplay.format = format;
|
||||
PVStringPtr pvString = pvDisplay->getSubField<PVString>("format");
|
||||
pvString->put(format);
|
||||
bitSet->set(pvString->getFieldOffset());
|
||||
}
|
||||
if(!description.empty())
|
||||
{
|
||||
PVStringPtr pvString = pvDisplay->getSubField<PVString>("description");
|
||||
if(description.compare(pvString->get()) !=0) {
|
||||
pvString->put(description);
|
||||
bitSet->set(pvString->getFieldOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
if(valueAlarmRequested) {
|
||||
double upper_alarm_limit = 0.0;
|
||||
double upper_warning_limit = 0.0;
|
||||
double lower_warning_limit = 0.0;
|
||||
double lower_alarm_limit = 0.0;
|
||||
switch(caRequestType) {
|
||||
case DBR_CTRL_CHAR:
|
||||
get_DBRValueAlarm<dbr_ctrl_char>(args.dbr,
|
||||
&upper_alarm_limit,&upper_warning_limit,
|
||||
&lower_warning_limit,&lower_alarm_limit);
|
||||
break;
|
||||
case DBR_CTRL_SHORT:
|
||||
get_DBRValueAlarm<dbr_ctrl_short>(args.dbr,
|
||||
&upper_alarm_limit,&upper_warning_limit,
|
||||
&lower_warning_limit,&lower_alarm_limit);
|
||||
break;
|
||||
case DBR_CTRL_LONG:
|
||||
get_DBRValueAlarm<dbr_ctrl_long>(args.dbr,
|
||||
&upper_alarm_limit,&upper_warning_limit,
|
||||
&lower_warning_limit,&lower_alarm_limit);
|
||||
break;
|
||||
case DBR_CTRL_FLOAT:
|
||||
get_DBRValueAlarm<dbr_ctrl_float>(args.dbr,
|
||||
&upper_alarm_limit,&upper_warning_limit,
|
||||
&lower_warning_limit,&lower_alarm_limit);
|
||||
break;
|
||||
case DBR_CTRL_DOUBLE:
|
||||
get_DBRValueAlarm<dbr_ctrl_double>(args.dbr,
|
||||
&upper_alarm_limit,&upper_warning_limit,
|
||||
&lower_warning_limit,&lower_alarm_limit);
|
||||
break;
|
||||
default :
|
||||
throw std::runtime_error("DbdToPv::getFromDBD logic error");
|
||||
}
|
||||
ConvertPtr convert(getConvert());
|
||||
PVStructurePtr pvValueAlarm(pvStructure->getSubField<PVStructure>("valueAlarm"));
|
||||
if(caValueAlarm.upper_alarm_limit!=upper_alarm_limit) {
|
||||
caValueAlarm.upper_alarm_limit = upper_alarm_limit;
|
||||
PVScalarPtr pv = pvValueAlarm->getSubField<PVScalar>("highAlarmLimit");
|
||||
convert->fromDouble(pv,upper_alarm_limit);
|
||||
bitSet->set(pv->getFieldOffset());
|
||||
}
|
||||
if(caValueAlarm.upper_warning_limit!=upper_warning_limit) {
|
||||
caValueAlarm.upper_warning_limit = upper_warning_limit;
|
||||
PVScalarPtr pv = pvValueAlarm->getSubField<PVScalar>("highWarningLimit");
|
||||
convert->fromDouble(pv,upper_warning_limit);
|
||||
bitSet->set(pv->getFieldOffset());
|
||||
}
|
||||
if(caValueAlarm.lower_warning_limit!=lower_warning_limit) {
|
||||
caValueAlarm.lower_warning_limit = lower_warning_limit;
|
||||
PVScalarPtr pv = pvValueAlarm->getSubField<PVScalar>("lowWarningLimit");
|
||||
convert->fromDouble(pv,lower_warning_limit);
|
||||
bitSet->set(pv->getFieldOffset());
|
||||
}
|
||||
if(caValueAlarm.lower_alarm_limit!=lower_alarm_limit) {
|
||||
caValueAlarm.lower_alarm_limit = lower_alarm_limit;
|
||||
PVScalarPtr pv = pvValueAlarm->getSubField<PVScalar>("lowAlarmLimit");
|
||||
convert->fromDouble(pv,lower_alarm_limit);
|
||||
bitSet->set(pv->getFieldOffset());
|
||||
}
|
||||
}
|
||||
if(firstTime) {
|
||||
firstTime = false;
|
||||
bitSet->clear();
|
||||
bitSet->set(0);
|
||||
}
|
||||
return Status::Ok;
|
||||
}
|
||||
|
||||
template<typename dbrT, typename pvT>
|
||||
const void * put_DBRScalar(dbrT *val,PVScalar::shared_pointer const & pvScalar)
|
||||
{
|
||||
std::tr1::shared_ptr<pvT> value = std::tr1::static_pointer_cast<pvT>(pvScalar);
|
||||
*val = value->get();
|
||||
return val;
|
||||
}
|
||||
|
||||
template<typename dbrT, typename pvT>
|
||||
const void * put_DBRScalarArray(unsigned long*count, PVScalarArray::shared_pointer const & pvArray)
|
||||
{
|
||||
std::tr1::shared_ptr<pvT> value = std::tr1::static_pointer_cast<pvT>(pvArray);
|
||||
*count = value->getLength();
|
||||
return value->view().data();
|
||||
}
|
||||
|
||||
|
||||
Status DbdToPv::putToDBD(
|
||||
CAChannelPtr const & caChannel,
|
||||
PVStructurePtr const & pvStructure,
|
||||
bool block,
|
||||
caCallbackFunc putHandler,
|
||||
void * userarg)
|
||||
{
|
||||
chid channelID = caChannel->getChannelID();
|
||||
const void *pValue = NULL;
|
||||
unsigned long count = 1;
|
||||
char *ca_stringBuffer(0);
|
||||
dbr_char_t bvalue(0);
|
||||
dbr_short_t svalue(0);
|
||||
dbr_long_t lvalue(0);
|
||||
dbr_float_t fvalue(0);
|
||||
dbr_double_t dvalue(0);
|
||||
if(isArray) {
|
||||
PVScalarArrayPtr pvValue = pvStructure->getSubField<PVScalarArray>("value");
|
||||
switch(caValueType) {
|
||||
case DBR_STRING:
|
||||
{
|
||||
PVStringArrayPtr pvValue = pvStructure->getSubField<PVStringArray>("value");
|
||||
count = pvValue->getLength();
|
||||
if(count<1) break;
|
||||
if(count>maxElements) count = maxElements;
|
||||
int nbytes = count*MAX_STRING_SIZE;
|
||||
ca_stringBuffer = new char[nbytes];
|
||||
memset(ca_stringBuffer, 0, nbytes);
|
||||
pValue = ca_stringBuffer;
|
||||
PVStringArray::const_svector stringArray(pvValue->view());
|
||||
char *pnext = ca_stringBuffer;
|
||||
for(size_t i=0; i<count; ++i) {
|
||||
string value = stringArray[i];
|
||||
size_t len = value.length();
|
||||
if (len >= MAX_STRING_SIZE) len = MAX_STRING_SIZE - 1;
|
||||
memcpy(pnext, value.c_str(), len);
|
||||
pnext += MAX_STRING_SIZE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DBR_CHAR:
|
||||
pValue = put_DBRScalarArray<dbr_char_t,PVByteArray>(&count,pvValue);
|
||||
break;
|
||||
case DBR_SHORT:
|
||||
pValue = put_DBRScalarArray<dbr_short_t,PVShortArray>(&count,pvValue);
|
||||
break;
|
||||
case DBR_LONG:
|
||||
pValue = put_DBRScalarArray<dbr_long_t,PVIntArray>(&count,pvValue);
|
||||
break;
|
||||
case DBR_FLOAT:
|
||||
pValue = put_DBRScalarArray<dbr_float_t,PVFloatArray>(&count,pvValue);
|
||||
break;
|
||||
case DBR_DOUBLE:
|
||||
pValue = put_DBRScalarArray<dbr_double_t,PVDoubleArray>(&count,pvValue);
|
||||
break;
|
||||
default:
|
||||
Status errorStatus(
|
||||
Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error"));
|
||||
return errorStatus;
|
||||
}
|
||||
} else {
|
||||
PVScalarPtr pvValue = pvStructure->getSubField<PVScalar>("value");
|
||||
switch(caValueType) {
|
||||
case DBR_ENUM:
|
||||
{
|
||||
bool wait = false;
|
||||
{
|
||||
Lock lock(choicesMutex);
|
||||
if(!choicesValid) {
|
||||
wait = true;
|
||||
waitForChoicesValid = true;
|
||||
}
|
||||
}
|
||||
bool result = true;
|
||||
if(wait) {
|
||||
result = choicesEvent.wait(5.0);
|
||||
}
|
||||
if(!result) {
|
||||
Status errorStatus(
|
||||
Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD "));
|
||||
return errorStatus;
|
||||
}
|
||||
dbr_enum_t indexvalue = pvStructure->getSubField<PVInt>("value.index")->get();
|
||||
pValue = &indexvalue;
|
||||
break;
|
||||
}
|
||||
case DBR_STRING: pValue = pvStructure->getSubField<PVString>("value")->get().c_str(); break;
|
||||
case DBR_CHAR: pValue = put_DBRScalar<dbr_char_t,PVByte>(&bvalue,pvValue); break;
|
||||
case DBR_SHORT: pValue = put_DBRScalar<dbr_short_t,PVShort>(&svalue,pvValue); break;
|
||||
case DBR_LONG: pValue = put_DBRScalar<dbr_long_t,PVInt>(&lvalue,pvValue); break;
|
||||
case DBR_FLOAT: pValue = put_DBRScalar<dbr_float_t,PVFloat>(&fvalue,pvValue); break;
|
||||
case DBR_DOUBLE: pValue = put_DBRScalar<dbr_double_t,PVDouble>(&dvalue,pvValue); break;
|
||||
default:
|
||||
Status errorStatus(
|
||||
Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error"));
|
||||
return errorStatus;
|
||||
}
|
||||
}
|
||||
Status status = Status::Ok;
|
||||
int result = 0;
|
||||
caChannel->attachContext();
|
||||
if(block) {
|
||||
result = ca_array_put_callback(caValueType,count,channelID,pValue,putHandler,userarg);
|
||||
} else {
|
||||
result = ca_array_put(caValueType,count,channelID,pValue);
|
||||
}
|
||||
if(result==ECA_NORMAL) {
|
||||
ca_flush_io();
|
||||
} else {
|
||||
status = Status(Status::STATUSTYPE_ERROR, string(ca_message(result)));
|
||||
}
|
||||
if(ca_stringBuffer!=NULL) delete[] ca_stringBuffer;
|
||||
return status;
|
||||
}
|
||||
|
||||
}}}
|
||||
145
src/ca/dbdToPv.h
Normal file
145
src/ca/dbdToPv.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvAccessCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2018.03
|
||||
*/
|
||||
#ifndef DbdToPv_H
|
||||
#define DbdToPv_H
|
||||
|
||||
#include <shareLib.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <cadef.h>
|
||||
#include <pv/event.h>
|
||||
#include "caChannel.h"
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
namespace ca {
|
||||
|
||||
enum IOType {getIO,putIO,monitorIO};
|
||||
|
||||
class AlarmDbd;
|
||||
typedef std::tr1::shared_ptr<AlarmDbd> AlarmDbdPtr;
|
||||
class TimeStampDbd;
|
||||
typedef std::tr1::shared_ptr<TimeStampDbd> TimeStampDbdPtr;
|
||||
class DisplayDbd;
|
||||
typedef std::tr1::shared_ptr<DisplayDbd> DisplayDbdPtr;
|
||||
class ControlDbd;
|
||||
typedef std::tr1::shared_ptr<ControlDbd> ControlDbdPtr;
|
||||
class ValueAlarmDbd;
|
||||
typedef std::tr1::shared_ptr<ValueAlarmDbd> ValueAlarmDbdPtr;
|
||||
|
||||
struct CaAlarm
|
||||
{
|
||||
dbr_short_t status;
|
||||
dbr_short_t severity;
|
||||
CaAlarm() : status(0), severity(0) {}
|
||||
};
|
||||
|
||||
struct CaDisplay
|
||||
{
|
||||
double lower_disp_limit;
|
||||
double upper_disp_limit;
|
||||
std::string units;
|
||||
std::string format;
|
||||
CaDisplay() : lower_disp_limit(0),upper_disp_limit(0) {}
|
||||
};
|
||||
|
||||
struct CaControl
|
||||
{
|
||||
double upper_ctrl_limit;
|
||||
double lower_ctrl_limit;
|
||||
CaControl() : upper_ctrl_limit(0),lower_ctrl_limit(0) {}
|
||||
};
|
||||
|
||||
struct CaValueAlarm
|
||||
{
|
||||
double upper_alarm_limit;
|
||||
double upper_warning_limit;
|
||||
double lower_warning_limit;
|
||||
double lower_alarm_limit;
|
||||
CaValueAlarm() :
|
||||
upper_alarm_limit(0),
|
||||
upper_warning_limit(0),
|
||||
lower_warning_limit(0),
|
||||
lower_alarm_limit(0)
|
||||
{}
|
||||
};
|
||||
|
||||
class DbdToPv;
|
||||
typedef std::tr1::shared_ptr<DbdToPv> DbdToPvPtr;
|
||||
|
||||
typedef void ( caCallbackFunc ) (struct event_handler_args);
|
||||
|
||||
/**
|
||||
* @brief DbdToPv converts between DBD data and pvData.
|
||||
*
|
||||
*
|
||||
*/
|
||||
class DbdToPv
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(DbdToPv);
|
||||
static DbdToPvPtr create(
|
||||
CAChannelPtr const & caChannel,
|
||||
epics::pvData::PVStructurePtr const & pvRequest,
|
||||
IOType ioType
|
||||
);
|
||||
epics::pvData::PVStructurePtr createPVStructure();
|
||||
chtype getRequestType();
|
||||
epics::pvData::Status getFromDBD(
|
||||
epics::pvData::PVStructurePtr const & pvStructure,
|
||||
epics::pvData::BitSet::shared_pointer const & bitSet,
|
||||
struct event_handler_args &args
|
||||
);
|
||||
epics::pvData::Status putToDBD(
|
||||
CAChannelPtr const & caChannel,
|
||||
epics::pvData::PVStructurePtr const & pvStructure,
|
||||
bool block,
|
||||
caCallbackFunc putHandler,
|
||||
void *userArg
|
||||
);
|
||||
void getChoicesDone(struct event_handler_args &args);
|
||||
void descriptionConnected(struct connection_handler_args args);
|
||||
void getDescriptionDone(struct event_handler_args &args);
|
||||
private:
|
||||
DbdToPv(IOType ioType);
|
||||
void activate(
|
||||
CAChannelPtr const & caChannel,
|
||||
epics::pvData::PVStructurePtr const & pvRequest
|
||||
);
|
||||
IOType ioType;
|
||||
bool fieldRequested;
|
||||
bool alarmRequested;
|
||||
bool timeStampRequested;
|
||||
bool displayRequested;
|
||||
bool controlRequested;
|
||||
bool valueAlarmRequested;
|
||||
bool isArray;
|
||||
bool firstTime;
|
||||
bool choicesValid;
|
||||
bool waitForChoicesValid;
|
||||
chtype caValueType;
|
||||
chtype caRequestType;
|
||||
unsigned long maxElements;
|
||||
epics::pvData::Mutex choicesMutex;
|
||||
epics::pvData::Event choicesEvent;
|
||||
epicsTimeStamp caTimeStamp;
|
||||
CaAlarm caAlarm;
|
||||
CaDisplay caDisplay;
|
||||
CaControl caControl;
|
||||
CaValueAlarm caValueAlarm;
|
||||
std::string description;
|
||||
epics::pvData::Structure::const_shared_pointer structure;
|
||||
std::vector<std::string> choices;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* DbdToPv_H */
|
||||
114
src/ca/getDoneThread.cpp
Normal file
114
src/ca/getDoneThread.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvAccessCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2018.07
|
||||
*/
|
||||
|
||||
#include "caChannel.h"
|
||||
#include <epicsExit.h>
|
||||
#define epicsExportSharedSymbols
|
||||
#include "getDoneThread.h"
|
||||
|
||||
using namespace epics::pvData;
|
||||
using namespace std;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
namespace ca {
|
||||
|
||||
GetDoneThreadPtr GetDoneThread::get()
|
||||
{
|
||||
static GetDoneThreadPtr master;
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
if(!master) {
|
||||
master = GetDoneThreadPtr(new GetDoneThread());
|
||||
master->start();
|
||||
}
|
||||
return master;
|
||||
}
|
||||
|
||||
GetDoneThread::GetDoneThread()
|
||||
: isStop(false)
|
||||
{
|
||||
}
|
||||
|
||||
GetDoneThread::~GetDoneThread()
|
||||
{
|
||||
//std::cout << "GetDoneThread::~GetDoneThread()\n";
|
||||
}
|
||||
|
||||
|
||||
void GetDoneThread::start()
|
||||
{
|
||||
thread = std::tr1::shared_ptr<epicsThread>(new epicsThread(
|
||||
*this,
|
||||
"getDoneThread",
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||
epicsThreadPriorityLow));
|
||||
thread->start();
|
||||
}
|
||||
|
||||
|
||||
void GetDoneThread::stop()
|
||||
{
|
||||
{
|
||||
Lock xx(mutex);
|
||||
isStop = true;
|
||||
}
|
||||
waitForCommand.signal();
|
||||
waitForStop.wait();
|
||||
}
|
||||
|
||||
void GetDoneThread::getDone(NotifyGetRequesterPtr const ¬ifyGetRequester)
|
||||
{
|
||||
{
|
||||
Lock lock(mutex);
|
||||
if(notifyGetRequester->isOnQueue) return;
|
||||
notifyGetRequester->isOnQueue = true;
|
||||
notifyGetQueue.push(notifyGetRequester);
|
||||
}
|
||||
waitForCommand.signal();
|
||||
}
|
||||
|
||||
void GetDoneThread::run()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
waitForCommand.wait();
|
||||
while(true) {
|
||||
bool more = false;
|
||||
NotifyGetRequester* notifyGetRequester(NULL);
|
||||
{
|
||||
Lock lock(mutex);
|
||||
if(!notifyGetQueue.empty())
|
||||
{
|
||||
more = true;
|
||||
NotifyGetRequesterWPtr req(notifyGetQueue.front());
|
||||
notifyGetQueue.pop();
|
||||
NotifyGetRequesterPtr reqPtr(req.lock());
|
||||
if(reqPtr) {
|
||||
notifyGetRequester = reqPtr.get();
|
||||
reqPtr->isOnQueue = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!more) break;
|
||||
if(notifyGetRequester!=NULL)
|
||||
{
|
||||
CAChannelGetPtr channelGet(notifyGetRequester->channelGet.lock());
|
||||
if(channelGet) channelGet->notifyClient();
|
||||
}
|
||||
}
|
||||
if(isStop) {
|
||||
waitForStop.signal();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}}}
|
||||
71
src/ca/getDoneThread.h
Normal file
71
src/ca/getDoneThread.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvAccessCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2018.07
|
||||
*/
|
||||
#ifndef GetDoneThread_H
|
||||
#define GetDoneThread_H
|
||||
#include <queue>
|
||||
#include <cadef.h>
|
||||
#include <shareLib.h>
|
||||
#include <epicsThread.h>
|
||||
#include <pv/event.h>
|
||||
#include <pv/lock.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
namespace ca {
|
||||
|
||||
class NotifyGetRequester;
|
||||
typedef std::tr1::shared_ptr<NotifyGetRequester> NotifyGetRequesterPtr;
|
||||
typedef std::tr1::weak_ptr<NotifyGetRequester> NotifyGetRequesterWPtr;
|
||||
|
||||
|
||||
class GetDoneThread;
|
||||
typedef std::tr1::shared_ptr<GetDoneThread> GetDoneThreadPtr;
|
||||
|
||||
class CAChannelGet;
|
||||
typedef std::tr1::shared_ptr<CAChannelGet> CAChannelGetPtr;
|
||||
typedef std::tr1::weak_ptr<CAChannelGet> CAChannelGetWPtr;
|
||||
|
||||
class NotifyGetRequester
|
||||
{
|
||||
public:
|
||||
ChannelGetRequester::weak_pointer channelGetRequester;
|
||||
CAChannelGetWPtr channelGet;
|
||||
bool isOnQueue;
|
||||
NotifyGetRequester() : isOnQueue(false) {}
|
||||
void setChannelGet(CAChannelGetPtr const &channelGet)
|
||||
{ this->channelGet = channelGet;}
|
||||
};
|
||||
|
||||
|
||||
class GetDoneThread :
|
||||
public epicsThreadRunable
|
||||
{
|
||||
public:
|
||||
static GetDoneThreadPtr get();
|
||||
~GetDoneThread();
|
||||
virtual void run();
|
||||
void start();
|
||||
void stop();
|
||||
void getDone(NotifyGetRequesterPtr const ¬ifyGetRequester);
|
||||
private:
|
||||
GetDoneThread();
|
||||
|
||||
bool isStop;
|
||||
std::tr1::shared_ptr<epicsThread> thread;
|
||||
epics::pvData::Mutex mutex;
|
||||
epics::pvData::Event waitForCommand;
|
||||
epics::pvData::Event waitForStop;
|
||||
std::queue<NotifyGetRequesterWPtr> notifyGetQueue;
|
||||
};
|
||||
|
||||
|
||||
}}}
|
||||
|
||||
#endif /* GetDoneThread_H */
|
||||
113
src/ca/monitorEventThread.cpp
Normal file
113
src/ca/monitorEventThread.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvAccessCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2018.06
|
||||
*/
|
||||
|
||||
#include "caChannel.h"
|
||||
#include <epicsExit.h>
|
||||
#define epicsExportSharedSymbols
|
||||
#include "monitorEventThread.h"
|
||||
|
||||
using namespace epics::pvData;
|
||||
using namespace std;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
namespace ca {
|
||||
|
||||
MonitorEventThreadPtr MonitorEventThread::get()
|
||||
{
|
||||
static MonitorEventThreadPtr master;
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
if(!master) {
|
||||
master = MonitorEventThreadPtr(new MonitorEventThread());
|
||||
master->start();
|
||||
}
|
||||
return master;
|
||||
}
|
||||
|
||||
MonitorEventThread::MonitorEventThread()
|
||||
: isStop(false)
|
||||
{
|
||||
}
|
||||
|
||||
MonitorEventThread::~MonitorEventThread()
|
||||
{
|
||||
//std::cout << "MonitorEventThread::~MonitorEventThread()\n";
|
||||
}
|
||||
|
||||
void MonitorEventThread::start()
|
||||
{
|
||||
thread = std::tr1::shared_ptr<epicsThread>(new epicsThread(
|
||||
*this,
|
||||
"monitorEventThread",
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||
epicsThreadPriorityLow));
|
||||
thread->start();
|
||||
}
|
||||
|
||||
void MonitorEventThread::stop()
|
||||
{
|
||||
{
|
||||
Lock xx(mutex);
|
||||
isStop = true;
|
||||
}
|
||||
waitForCommand.signal();
|
||||
waitForStop.wait();
|
||||
}
|
||||
|
||||
|
||||
void MonitorEventThread::event(NotifyMonitorRequesterPtr const ¬ifyMonitorRequester)
|
||||
{
|
||||
{
|
||||
Lock lock(mutex);
|
||||
if(notifyMonitorRequester->isOnQueue) return;
|
||||
notifyMonitorRequester->isOnQueue = true;
|
||||
notifyMonitorQueue.push(notifyMonitorRequester);
|
||||
}
|
||||
waitForCommand.signal();
|
||||
}
|
||||
|
||||
void MonitorEventThread::run()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
waitForCommand.wait();
|
||||
while(true) {
|
||||
bool more = false;
|
||||
NotifyMonitorRequester* notifyMonitorRequester(NULL);
|
||||
{
|
||||
Lock lock(mutex);
|
||||
if(!notifyMonitorQueue.empty())
|
||||
{
|
||||
more = true;
|
||||
NotifyMonitorRequesterWPtr req(notifyMonitorQueue.front());
|
||||
notifyMonitorQueue.pop();
|
||||
NotifyMonitorRequesterPtr reqPtr(req.lock());
|
||||
if(reqPtr) {
|
||||
notifyMonitorRequester = reqPtr.get();
|
||||
reqPtr->isOnQueue = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!more) break;
|
||||
if(notifyMonitorRequester!=NULL)
|
||||
{
|
||||
CAChannelMonitorPtr channelMonitor(notifyMonitorRequester->channelMonitor.lock());
|
||||
if(channelMonitor) channelMonitor->notifyClient();
|
||||
}
|
||||
}
|
||||
if(isStop) {
|
||||
waitForStop.signal();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}}}
|
||||
71
src/ca/monitorEventThread.h
Normal file
71
src/ca/monitorEventThread.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvAccessCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2018.06
|
||||
*/
|
||||
#ifndef MonitorEventThread_H
|
||||
#define MonitorEventThread_H
|
||||
#include <queue>
|
||||
#include <cadef.h>
|
||||
#include <shareLib.h>
|
||||
#include <epicsThread.h>
|
||||
#include <pv/event.h>
|
||||
#include <pv/lock.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
namespace ca {
|
||||
|
||||
class NotifyMonitorRequester;
|
||||
typedef std::tr1::shared_ptr<NotifyMonitorRequester> NotifyMonitorRequesterPtr;
|
||||
typedef std::tr1::weak_ptr<NotifyMonitorRequester> NotifyMonitorRequesterWPtr;
|
||||
|
||||
|
||||
class MonitorEventThread;
|
||||
typedef std::tr1::shared_ptr<MonitorEventThread> MonitorEventThreadPtr;
|
||||
|
||||
class CAChannelMonitor;
|
||||
typedef std::tr1::shared_ptr<CAChannelMonitor> CAChannelMonitorPtr;
|
||||
typedef std::tr1::weak_ptr<CAChannelMonitor> CAChannelMonitorWPtr;
|
||||
|
||||
class NotifyMonitorRequester
|
||||
{
|
||||
public:
|
||||
MonitorRequester::weak_pointer monitorRequester;
|
||||
CAChannelMonitorWPtr channelMonitor;
|
||||
bool isOnQueue;
|
||||
NotifyMonitorRequester() : isOnQueue(false) {}
|
||||
void setChannelMonitor(CAChannelMonitorPtr const &channelMonitor)
|
||||
{ this->channelMonitor = channelMonitor;}
|
||||
};
|
||||
|
||||
|
||||
class MonitorEventThread :
|
||||
public epicsThreadRunable
|
||||
{
|
||||
public:
|
||||
static MonitorEventThreadPtr get();
|
||||
~MonitorEventThread();
|
||||
virtual void run();
|
||||
void start();
|
||||
void stop();
|
||||
void event(NotifyMonitorRequesterPtr const ¬ifyMonitorRequester);
|
||||
private:
|
||||
MonitorEventThread();
|
||||
|
||||
bool isStop;
|
||||
std::tr1::shared_ptr<epicsThread> thread;
|
||||
epics::pvData::Mutex mutex;
|
||||
epics::pvData::Event waitForCommand;
|
||||
epics::pvData::Event waitForStop;
|
||||
std::queue<NotifyMonitorRequesterWPtr> notifyMonitorQueue;
|
||||
};
|
||||
|
||||
|
||||
}}}
|
||||
|
||||
#endif /* MonitorEventThread_H */
|
||||
114
src/ca/putDoneThread.cpp
Normal file
114
src/ca/putDoneThread.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvAccessCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2018.07
|
||||
*/
|
||||
|
||||
#include "caChannel.h"
|
||||
#include <epicsExit.h>
|
||||
#define epicsExportSharedSymbols
|
||||
#include "putDoneThread.h"
|
||||
|
||||
using namespace epics::pvData;
|
||||
using namespace std;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
namespace ca {
|
||||
|
||||
PutDoneThreadPtr PutDoneThread::get()
|
||||
{
|
||||
static PutDoneThreadPtr master;
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
if(!master) {
|
||||
master = PutDoneThreadPtr(new PutDoneThread());
|
||||
master->start();
|
||||
}
|
||||
return master;
|
||||
}
|
||||
|
||||
PutDoneThread::PutDoneThread()
|
||||
: isStop(false)
|
||||
{
|
||||
}
|
||||
|
||||
PutDoneThread::~PutDoneThread()
|
||||
{
|
||||
//std::cout << "PutDoneThread::~PutDoneThread()\n";
|
||||
}
|
||||
|
||||
|
||||
void PutDoneThread::start()
|
||||
{
|
||||
thread = std::tr1::shared_ptr<epicsThread>(new epicsThread(
|
||||
*this,
|
||||
"putDoneThread",
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||
epicsThreadPriorityLow));
|
||||
thread->start();
|
||||
}
|
||||
|
||||
|
||||
void PutDoneThread::stop()
|
||||
{
|
||||
{
|
||||
Lock xx(mutex);
|
||||
isStop = true;
|
||||
}
|
||||
waitForCommand.signal();
|
||||
waitForStop.wait();
|
||||
}
|
||||
|
||||
void PutDoneThread::putDone(NotifyPutRequesterPtr const ¬ifyPutRequester)
|
||||
{
|
||||
{
|
||||
Lock lock(mutex);
|
||||
if(notifyPutRequester->isOnQueue) return;
|
||||
notifyPutRequester->isOnQueue = true;
|
||||
notifyPutQueue.push(notifyPutRequester);
|
||||
}
|
||||
waitForCommand.signal();
|
||||
}
|
||||
|
||||
void PutDoneThread::run()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
waitForCommand.wait();
|
||||
while(true) {
|
||||
bool more = false;
|
||||
NotifyPutRequester* notifyPutRequester(NULL);
|
||||
{
|
||||
Lock lock(mutex);
|
||||
if(!notifyPutQueue.empty())
|
||||
{
|
||||
more = true;
|
||||
NotifyPutRequesterWPtr req(notifyPutQueue.front());
|
||||
notifyPutQueue.pop();
|
||||
NotifyPutRequesterPtr reqPtr(req.lock());
|
||||
if(reqPtr) {
|
||||
notifyPutRequester = reqPtr.get();
|
||||
reqPtr->isOnQueue = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!more) break;
|
||||
if(notifyPutRequester!=NULL)
|
||||
{
|
||||
CAChannelPutPtr channelPut(notifyPutRequester->channelPut.lock());
|
||||
if(channelPut) channelPut->notifyClient();
|
||||
}
|
||||
}
|
||||
if(isStop) {
|
||||
waitForStop.signal();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}}}
|
||||
70
src/ca/putDoneThread.h
Normal file
70
src/ca/putDoneThread.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvAccessCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2018.07
|
||||
*/
|
||||
#ifndef PutDoneThread_H
|
||||
#define PutDoneThread_H
|
||||
#include <queue>
|
||||
#include <cadef.h>
|
||||
#include <shareLib.h>
|
||||
#include <epicsThread.h>
|
||||
#include <pv/event.h>
|
||||
#include <pv/lock.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
namespace ca {
|
||||
|
||||
class NotifyPutRequester;
|
||||
typedef std::tr1::shared_ptr<NotifyPutRequester> NotifyPutRequesterPtr;
|
||||
typedef std::tr1::weak_ptr<NotifyPutRequester> NotifyPutRequesterWPtr;
|
||||
|
||||
|
||||
class PutDoneThread;
|
||||
typedef std::tr1::shared_ptr<PutDoneThread> PutDoneThreadPtr;
|
||||
|
||||
class CAChannelPut;
|
||||
typedef std::tr1::shared_ptr<CAChannelPut> CAChannelPutPtr;
|
||||
typedef std::tr1::weak_ptr<CAChannelPut> CAChannelPutWPtr;
|
||||
|
||||
class NotifyPutRequester
|
||||
{
|
||||
public:
|
||||
ChannelPutRequester::weak_pointer channelPutRequester;
|
||||
CAChannelPutWPtr channelPut;
|
||||
bool isOnQueue;
|
||||
NotifyPutRequester() : isOnQueue(false) {}
|
||||
void setChannelPut(CAChannelPutPtr const &channelPut)
|
||||
{ this->channelPut = channelPut;}
|
||||
};
|
||||
|
||||
|
||||
class PutDoneThread :
|
||||
public epicsThreadRunable
|
||||
{
|
||||
public:
|
||||
static PutDoneThreadPtr get();
|
||||
~PutDoneThread();
|
||||
virtual void run();
|
||||
void start();
|
||||
void stop();
|
||||
void putDone(NotifyPutRequesterPtr const ¬ifyPutRequester);
|
||||
private:
|
||||
PutDoneThread();
|
||||
bool isStop;
|
||||
std::tr1::shared_ptr<epicsThread> thread;
|
||||
epics::pvData::Mutex mutex;
|
||||
epics::pvData::Event waitForCommand;
|
||||
epics::pvData::Event waitForStop;
|
||||
std::queue<NotifyPutRequesterWPtr> notifyPutQueue;
|
||||
};
|
||||
|
||||
|
||||
}}}
|
||||
|
||||
#endif /* PutDoneThread_H */
|
||||
@@ -3,6 +3,9 @@
|
||||
* pvAccessCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author msekoranja
|
||||
*/
|
||||
|
||||
#ifndef CAPROVIDER_H
|
||||
#define CAPROVIDER_H
|
||||
@@ -10,6 +13,8 @@
|
||||
#include <shareLib.h>
|
||||
#include <pv/pvAccess.h>
|
||||
|
||||
struct ca_client_context;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
namespace ca {
|
||||
@@ -17,23 +22,36 @@ namespace ca {
|
||||
/**
|
||||
* @brief CAClientFactory is a channel provider for the ca network provider.
|
||||
*
|
||||
* A single instance is created the first time CAClientFactory::start is called.
|
||||
* epicsAtExit is used to destroy the instance.
|
||||
*
|
||||
* The single instance calls:
|
||||
* ca_context_create(ca_enable_preemptive_callback);
|
||||
*
|
||||
* The thread that calls start, or a ca auxillary thread, are the only threads
|
||||
* that can call the ca_* functions.
|
||||
*
|
||||
* NOTE: callbacks for monitor, get, and put are made from a separate thread.
|
||||
* This is done to prevent a deadly embrace that can occur
|
||||
* when rapid gets, puts, and monitor events are happening.
|
||||
* The callbacks should not call any pvAccess method.
|
||||
* If any such call is made the separate thread becomes a ca auxillary thread.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass CAClientFactory
|
||||
{
|
||||
public:
|
||||
/** @brief start the provider
|
||||
/** @brief start provider ca
|
||||
*
|
||||
*/
|
||||
static void start();
|
||||
/** @brief stop the provider
|
||||
/** @brief stop provider ca
|
||||
*
|
||||
* This does nothing since epicsAtExit is used to destroy the instance.
|
||||
*/
|
||||
static void stop();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}}}
|
||||
|
||||
#endif /* CAPROVIDER_H */
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvAccessCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
#ifndef CASTATUS_H
|
||||
#define CASTATUS_H
|
||||
|
||||
#include <string>
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
namespace ca {
|
||||
|
||||
enum AlarmStatus {
|
||||
noStatus,deviceStatus,driverStatus,recordStatus,
|
||||
dbStatus,confStatus,undefinedStatus,clientStatus
|
||||
};
|
||||
|
||||
epicsShareExtern std::string dbrStatus2alarmMessage[];
|
||||
epicsShareExtern int dbrStatus2alarmStatus[];
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CASTATUS_H */
|
||||
47
testCa/Makefile
Normal file
47
testCa/Makefile
Normal file
@@ -0,0 +1,47 @@
|
||||
# Makefile for the ca tests
|
||||
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
# Need access to caProviderPvt.h
|
||||
USR_CPPFLAGS += -I$(TOP)/src/ca
|
||||
|
||||
PROD_LIBS += pvAccess pvAccessCA pvData $(EPICS_BASE_IOC_LIBS)
|
||||
|
||||
TESTPROD_HOST += testCaProvider
|
||||
testCaProvider_SRCS += testCaProvider.cpp
|
||||
TESTS += testCaProvider
|
||||
ifdef BASE_3_16
|
||||
testCaProvider_SRCS += testIoc_registerRecordDeviceDriver.cpp
|
||||
REGRDDFLAGS = -l
|
||||
endif
|
||||
caTestHarness_SRCS += $(testCaProvider_SRCS)
|
||||
|
||||
# Ensure EPICS_HOST_ARCH is set in the environment
|
||||
export EPICS_HOST_ARCH
|
||||
|
||||
ifdef BASE_3_16
|
||||
# Embedded OSes need Base-3.16.2 or higher to pass tests
|
||||
|
||||
# Code that runs all tests in the collection
|
||||
caTestHarness_SRCS += pvCaAllTests.c
|
||||
|
||||
# Build for vxWorks
|
||||
PROD_vxWorks = caTestHarness
|
||||
TESTSPEC_vxWorks = caTestHarness.$(MUNCH_SUFFIX); pvCaAllTests
|
||||
|
||||
# Build for RTEMS, with harness code & configuration
|
||||
PROD_RTEMS += caTestHarness
|
||||
caTestHarness_SRCS_RTEMS += rtemsTestHarness.c
|
||||
TESTSPEC_RTEMS = caTestHarness.$(MUNCH_SUFFIX); pvCaAllTests
|
||||
endif
|
||||
|
||||
# Build test scripts for hosts
|
||||
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
ifdef BASE_3_16
|
||||
$(COMMON_DIR)/testIoc.dbd: $(EPICS_BASE)/dbd/softIoc.dbd
|
||||
$(CP) $< $@
|
||||
endif
|
||||
18
testCa/pvCaAllTests.c
Normal file
18
testCa/pvCaAllTests.c
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <epicsThread.h>
|
||||
#include <epicsUnitTest.h>
|
||||
#include <epicsExit.h>
|
||||
|
||||
int testCaProvider(void);
|
||||
|
||||
void pvCaAllTests(void)
|
||||
{
|
||||
testHarness();
|
||||
runTest(testCaProvider);
|
||||
|
||||
epicsExit(0); /* Trigger test harness */
|
||||
}
|
||||
1
testCa/qemuRunTest.sh
Executable file
1
testCa/qemuRunTest.sh
Executable file
@@ -0,0 +1 @@
|
||||
qemu-system-i386 --kernel O.RTEMS-pc386/rtemsTestHarness -netdev user,id=mynet0 -device ne2k_isa,netdev=mynet0 -redir tcp:5075::5075 -redir udp:5076::5076 -m 1024 --no-reboot -curses
|
||||
12
testCa/rtemsTestHarness.c
Normal file
12
testCa/rtemsTestHarness.c
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
extern void pvCaAllTests(void);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
pvCaAllTests(); /* calls epicsExit(0) */
|
||||
return 0;
|
||||
}
|
||||
802
testCa/testCaProvider.cpp
Normal file
802
testCa/testCaProvider.cpp
Normal file
@@ -0,0 +1,802 @@
|
||||
/* testCaProvider.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/* Author: Marty Kraimer Date: 2018.05 */
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
#include <epicsUnitTest.h>
|
||||
#include <testMain.h>
|
||||
#include <epicsVersion.h>
|
||||
|
||||
#ifdef EPICS_VERSION_INT
|
||||
#if EPICS_VERSION_INT >= VERSION_INT(3,16,2,0)
|
||||
#define USE_DBUNITTEST
|
||||
// USE_TYPED_RSET prevents deprecation warnings
|
||||
#define USE_TYPED_RSET
|
||||
#define EXIT_TESTS 0
|
||||
#include <dbAccess.h>
|
||||
#include <errlog.h>
|
||||
#include <dbUnitTest.h>
|
||||
|
||||
extern "C" int testIoc_registerRecordDeviceDriver(struct dbBase *pbase);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef EXIT_TESTS
|
||||
#define EXIT_TESTS 1
|
||||
#endif
|
||||
|
||||
#include <pv/thread.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/convert.h>
|
||||
#include <pv/caProvider.h>
|
||||
#include <pv/requester.h>
|
||||
#include <pv/status.h>
|
||||
#include <pv/event.h>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <pv/pvData.h>
|
||||
|
||||
// DEBUG must be 0 to run under the automated test harness
|
||||
#define DEBUG 0
|
||||
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
using namespace epics::pvAccess::ca;
|
||||
using namespace std;
|
||||
|
||||
class TestChannel;
|
||||
typedef std::tr1::shared_ptr<TestChannel> TestChannelPtr;
|
||||
|
||||
class TestChannel:
|
||||
public ChannelRequester,
|
||||
public std::tr1::enable_shared_from_this<TestChannel>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(TestChannel);
|
||||
string getRequesterName();
|
||||
void message(
|
||||
string const & message,
|
||||
MessageType messageType);
|
||||
virtual void channelCreated(const Status& status, Channel::shared_pointer const & channel);
|
||||
virtual void channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState);
|
||||
string getChannelName();
|
||||
Channel::shared_pointer getChannel();
|
||||
static TestChannelPtr create(string const & channelName);
|
||||
void connect();
|
||||
void waitConnect(double timeout);
|
||||
private:
|
||||
TestChannel(string const & channelName);
|
||||
string channelName;
|
||||
Event waitForConnect;
|
||||
Channel::shared_pointer channel;
|
||||
};
|
||||
|
||||
string TestChannel::getChannelName() { return channelName;}
|
||||
|
||||
Channel::shared_pointer TestChannel::getChannel() { return channel;}
|
||||
|
||||
TestChannelPtr TestChannel::create(string const & channelName)
|
||||
{
|
||||
TestChannelPtr testChannel(new TestChannel(channelName));
|
||||
testChannel->connect();
|
||||
return testChannel;
|
||||
}
|
||||
|
||||
TestChannel::TestChannel(string const & channelName)
|
||||
: channelName(channelName)
|
||||
{
|
||||
}
|
||||
|
||||
string TestChannel::getRequesterName() { return "testChannel";}
|
||||
void TestChannel::message(string const & message,MessageType messageType) {};
|
||||
|
||||
void TestChannel::channelCreated(const Status& status, Channel::shared_pointer const & channel)
|
||||
{
|
||||
if(channel->isConnected()) waitForConnect.signal();
|
||||
}
|
||||
|
||||
void TestChannel::channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState)
|
||||
{
|
||||
if(connectionState==Channel::CONNECTED) waitForConnect.signal();
|
||||
}
|
||||
|
||||
void TestChannel::connect()
|
||||
{
|
||||
ChannelProviderRegistry::shared_pointer reg(ChannelProviderRegistry::clients());
|
||||
ChannelProvider::shared_pointer channelProvider(reg->getProvider("ca"));
|
||||
if(!channelProvider) throw std::runtime_error(channelName + " provider ca not registered");
|
||||
channel = channelProvider->createChannel(channelName,shared_from_this(),ChannelProvider::PRIORITY_DEFAULT);
|
||||
if(!channel) throw std::runtime_error(channelName + " channelCreate failed ");
|
||||
waitConnect(5.0);
|
||||
}
|
||||
|
||||
void TestChannel::waitConnect(double timeout)
|
||||
{
|
||||
if(waitForConnect.wait(timeout)) return;
|
||||
throw std::runtime_error(channelName + " TestChannel::waitConnect failed ");
|
||||
}
|
||||
|
||||
class TestChannelGet;
|
||||
typedef std::tr1::shared_ptr<TestChannelGet> TestChannelGetPtr;
|
||||
|
||||
class TestChannelGetRequester;
|
||||
typedef std::tr1::shared_ptr<TestChannelGetRequester> TestChannelGetRequesterPtr;
|
||||
typedef std::tr1::weak_ptr<TestChannelGetRequester> TestChannelGetRequesterWPtr;
|
||||
|
||||
class TestChannelGetRequester
|
||||
{
|
||||
public:
|
||||
virtual void getDone(
|
||||
PVStructure::shared_pointer const & pvStructure,
|
||||
BitSet::shared_pointer const & bitSet) = 0;
|
||||
};
|
||||
|
||||
class TestChannelGet:
|
||||
public ChannelGetRequester,
|
||||
public std::tr1::enable_shared_from_this<TestChannelGet>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(TestChannelGet);
|
||||
virtual string getRequesterName();
|
||||
virtual void message(string const & message, epics::pvData::MessageType messageType) {}
|
||||
virtual void channelGetConnect(
|
||||
const Status& status,
|
||||
ChannelGet::shared_pointer const & channelGet,
|
||||
Structure::const_shared_pointer const & structure);
|
||||
virtual void getDone(
|
||||
const Status& status,
|
||||
ChannelGet::shared_pointer const & channelGet,
|
||||
PVStructure::shared_pointer const & pvStructure,
|
||||
BitSet::shared_pointer const & bitSet);
|
||||
static TestChannelGetPtr create(
|
||||
TestChannelGetRequesterPtr const &getRequester,
|
||||
TestChannelPtr const &testChannel,
|
||||
PVStructurePtr const & pvRequest);
|
||||
void connect();
|
||||
void waitConnect(double timeout);
|
||||
void get();
|
||||
private:
|
||||
TestChannelGet(
|
||||
TestChannelGetRequesterPtr const &getRequester,
|
||||
TestChannelPtr const &testChannel,
|
||||
PVStructurePtr const & pvRequest);
|
||||
|
||||
TestChannelGetRequesterWPtr getRequester;
|
||||
TestChannelPtr testChannel;
|
||||
PVStructurePtr pvRequest;
|
||||
PVStructurePtr pvStructure;
|
||||
Event waitForConnect;
|
||||
ChannelGet::shared_pointer channelGet;
|
||||
};
|
||||
|
||||
TestChannelGetPtr TestChannelGet::create(
|
||||
TestChannelGetRequesterPtr const &getRequester,
|
||||
TestChannelPtr const &testChannel,
|
||||
PVStructurePtr const & pvRequest)
|
||||
{
|
||||
TestChannelGetPtr testChannelGet(new TestChannelGet(getRequester,testChannel,pvRequest));
|
||||
testChannelGet->connect();
|
||||
testChannelGet->waitConnect(5.0);
|
||||
return testChannelGet;
|
||||
}
|
||||
|
||||
TestChannelGet::TestChannelGet(
|
||||
TestChannelGetRequesterPtr const &getRequester,
|
||||
TestChannelPtr const &testChannel,
|
||||
PVStructurePtr const & pvRequest)
|
||||
: getRequester(getRequester),
|
||||
testChannel(testChannel),
|
||||
pvRequest(pvRequest)
|
||||
{
|
||||
}
|
||||
|
||||
string TestChannelGet::getRequesterName() {return "TestChannelGet";}
|
||||
|
||||
void TestChannelGet::channelGetConnect(
|
||||
const Status& status,
|
||||
ChannelGet::shared_pointer const & channelGet,
|
||||
Structure::const_shared_pointer const & structure)
|
||||
{
|
||||
waitForConnect.signal();
|
||||
}
|
||||
|
||||
void TestChannelGet::getDone(
|
||||
const Status& status,
|
||||
ChannelGet::shared_pointer const & channelGet,
|
||||
PVStructure::shared_pointer const & pvStructure,
|
||||
BitSet::shared_pointer const & bitSet)
|
||||
{
|
||||
TestChannelGetRequesterPtr req(getRequester.lock());
|
||||
if(!req) return;
|
||||
if(status.isOK()) {
|
||||
req->getDone(pvStructure,bitSet);
|
||||
return;
|
||||
}
|
||||
string message = string("channel ")
|
||||
+ testChannel->getChannelName()
|
||||
+ " TestChannelGet::getDone "
|
||||
+ status.getMessage();
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
|
||||
void TestChannelGet::connect()
|
||||
{
|
||||
channelGet = testChannel->getChannel()->createChannelGet(shared_from_this(),pvRequest);
|
||||
if(!channelGet) throw std::runtime_error(testChannel->getChannelName() + " channelCreate failed ");
|
||||
}
|
||||
|
||||
void TestChannelGet::waitConnect(double timeout)
|
||||
{
|
||||
if(waitForConnect.wait(timeout)) return;
|
||||
throw std::runtime_error(testChannel->getChannelName() + " TestChannelGet::waitConnect failed ");
|
||||
}
|
||||
|
||||
|
||||
void TestChannelGet::get()
|
||||
{
|
||||
channelGet->get();
|
||||
}
|
||||
|
||||
class TestChannelPut;
|
||||
typedef std::tr1::shared_ptr<TestChannelPut> TestChannelPutPtr;
|
||||
|
||||
class TestChannelPutRequester;
|
||||
typedef std::tr1::shared_ptr<TestChannelPutRequester> TestChannelPutRequesterPtr;
|
||||
typedef std::tr1::weak_ptr<TestChannelPutRequester> TestChannelPutRequesterWPtr;
|
||||
|
||||
class TestChannelPutRequester
|
||||
{
|
||||
public:
|
||||
virtual void putDone() = 0;
|
||||
};
|
||||
|
||||
class TestChannelPut:
|
||||
public ChannelPutRequester,
|
||||
public std::tr1::enable_shared_from_this<TestChannelPut>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(TestChannelPut);
|
||||
virtual string getRequesterName();
|
||||
virtual void message(string const & message, MessageType messageType) {}
|
||||
virtual void channelPutConnect(
|
||||
const Status& status,
|
||||
ChannelPut::shared_pointer const & channelPut,
|
||||
Structure::const_shared_pointer const & structure);
|
||||
virtual void putDone(
|
||||
const Status& status,
|
||||
ChannelPut::shared_pointer const & channelPut);
|
||||
virtual void getDone(
|
||||
const Status& status,
|
||||
ChannelPut::shared_pointer const & channelPut,
|
||||
PVStructure::shared_pointer const & pvStructure,
|
||||
BitSet::shared_pointer const & bitSet);
|
||||
static TestChannelPutPtr create(
|
||||
TestChannelPutRequesterPtr const &putRequester,
|
||||
TestChannelPtr const &testChannel);
|
||||
void connect();
|
||||
void waitConnect(double timeout);
|
||||
void put(string const & value);
|
||||
private:
|
||||
TestChannelPut(
|
||||
TestChannelPutRequesterPtr const &putRequester,
|
||||
TestChannelPtr const &testChannel);
|
||||
|
||||
TestChannelPutRequesterWPtr putRequester;
|
||||
TestChannelPtr testChannel;
|
||||
PVStructurePtr pvStructure;
|
||||
BitSetPtr bitSet;
|
||||
Event waitForConnect;
|
||||
ChannelPut::shared_pointer channelPut;
|
||||
};
|
||||
|
||||
TestChannelPutPtr TestChannelPut::create(
|
||||
TestChannelPutRequesterPtr const &putRequester,
|
||||
TestChannelPtr const &testChannel)
|
||||
{
|
||||
TestChannelPutPtr testChannelPut(new TestChannelPut(putRequester,testChannel));
|
||||
testChannelPut->connect();
|
||||
testChannelPut->waitConnect(5.0);
|
||||
return testChannelPut;
|
||||
}
|
||||
|
||||
TestChannelPut::TestChannelPut(
|
||||
TestChannelPutRequesterPtr const &putRequester,
|
||||
TestChannelPtr const &testChannel)
|
||||
: putRequester(putRequester),
|
||||
testChannel(testChannel)
|
||||
{
|
||||
}
|
||||
|
||||
string TestChannelPut::getRequesterName() {return "TestChannelPut";}
|
||||
|
||||
void TestChannelPut::channelPutConnect(
|
||||
const Status& status,
|
||||
ChannelPut::shared_pointer const & channelPut,
|
||||
Structure::const_shared_pointer const & structure)
|
||||
{
|
||||
pvStructure = PVDataCreate::getPVDataCreate()->createPVStructure(structure);
|
||||
bitSet = BitSetPtr(new BitSet(pvStructure->getNumberFields()));
|
||||
waitForConnect.signal();
|
||||
}
|
||||
|
||||
void TestChannelPut::getDone(
|
||||
const Status& status,
|
||||
ChannelPut::shared_pointer const & channelPut,
|
||||
PVStructure::shared_pointer const & pvStructure,
|
||||
BitSet::shared_pointer const & bitSet)
|
||||
{
|
||||
throw std::runtime_error("TestChannelPut::getDone should not be called");
|
||||
}
|
||||
|
||||
void TestChannelPut::putDone(
|
||||
const Status& status,
|
||||
ChannelPut::shared_pointer const & channelPut)
|
||||
{
|
||||
TestChannelPutRequesterPtr req(putRequester.lock());
|
||||
if(!req) return;
|
||||
if(status.isOK()) {
|
||||
req->putDone();
|
||||
return;
|
||||
}
|
||||
string message = string("channel ")
|
||||
+ testChannel->getChannelName()
|
||||
+ " TestChannelPut::putDone "
|
||||
+ status.getMessage();
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
|
||||
void TestChannelPut::connect()
|
||||
{
|
||||
string request("value");
|
||||
PVStructurePtr pvRequest(createRequest(request));
|
||||
|
||||
channelPut = testChannel->getChannel()->createChannelPut(shared_from_this(),pvRequest);
|
||||
if(!channelPut) throw std::runtime_error(testChannel->getChannelName() + " channelCreate failed ");
|
||||
}
|
||||
|
||||
void TestChannelPut::waitConnect(double timeout)
|
||||
{
|
||||
if(waitForConnect.wait(timeout)) return;
|
||||
throw std::runtime_error(testChannel->getChannelName()
|
||||
+ " TestChannelPut::waitConnect failed ");
|
||||
}
|
||||
|
||||
|
||||
void TestChannelPut::put(string const & value)
|
||||
{
|
||||
PVFieldPtr pvField(pvStructure->getSubField("value"));
|
||||
if(!pvField) throw std::runtime_error(testChannel->getChannelName()
|
||||
+ " TestChannelPut::put no value ");
|
||||
FieldConstPtr field(pvField->getField());
|
||||
Type type(field->getType());
|
||||
if(type==scalar) {
|
||||
PVScalarPtr pvScalar(std::tr1::static_pointer_cast<PVScalar>(pvField));
|
||||
getConvert()->fromString(pvScalar,value);
|
||||
bitSet->set(pvField->getFieldOffset());
|
||||
channelPut->put(pvStructure,bitSet);
|
||||
return;
|
||||
}
|
||||
if(type==scalarArray) {
|
||||
PVScalarArrayPtr pvScalarArray(std::tr1::static_pointer_cast<PVScalarArray>(pvField));
|
||||
std::vector<string> values;
|
||||
size_t pos = 0;
|
||||
size_t n = 1;
|
||||
while(true)
|
||||
{
|
||||
size_t offset = value.find(" ",pos);
|
||||
if(offset==string::npos) {
|
||||
values.push_back(value.substr(pos));
|
||||
break;
|
||||
}
|
||||
values.push_back(value.substr(pos,offset-pos));
|
||||
pos = offset+1;
|
||||
n++;
|
||||
}
|
||||
pvScalarArray->setLength(n);
|
||||
getConvert()->fromStringArray(pvScalarArray,0,n,values,0);
|
||||
bitSet->set(pvField->getFieldOffset());
|
||||
channelPut->put(pvStructure,bitSet);
|
||||
return;
|
||||
}
|
||||
if(type==structure) {
|
||||
PVScalarPtr pvScalar(pvStructure->getSubField<PVScalar>("value.index"));
|
||||
if(pvScalar) {
|
||||
getConvert()->fromString(pvScalar,value);
|
||||
bitSet->set(pvScalar->getFieldOffset());
|
||||
channelPut->put(pvStructure,bitSet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error(testChannel->getChannelName()
|
||||
+ " TestChannelPut::put not supported type");
|
||||
}
|
||||
|
||||
class TestChannelMonitor;
|
||||
typedef std::tr1::shared_ptr<TestChannelMonitor> TestChannelMonitorPtr;
|
||||
|
||||
class TestChannelMonitorRequester;
|
||||
typedef std::tr1::shared_ptr<TestChannelMonitorRequester> TestChannelMonitorRequesterPtr;
|
||||
typedef std::tr1::weak_ptr<TestChannelMonitorRequester> TestChannelMonitorRequesterWPtr;
|
||||
|
||||
class TestChannelMonitorRequester
|
||||
{
|
||||
public:
|
||||
virtual void monitorEvent(
|
||||
PVStructure::shared_pointer const & pvStructure,
|
||||
BitSet::shared_pointer const & bitSet) = 0;
|
||||
};
|
||||
|
||||
class TestChannelMonitor:
|
||||
public MonitorRequester,
|
||||
public std::tr1::enable_shared_from_this<TestChannelMonitor>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(TestChannelMonitor);
|
||||
virtual string getRequesterName();
|
||||
virtual void message(string const & message, MessageType messageType) {}
|
||||
virtual void monitorConnect(
|
||||
Status const & status,
|
||||
MonitorPtr const & monitor,
|
||||
StructureConstPtr const & structure);
|
||||
virtual void monitorEvent(MonitorPtr const & monitor);
|
||||
virtual void unlisten(MonitorPtr const & monitor);
|
||||
static TestChannelMonitorPtr create(
|
||||
TestChannelMonitorRequesterPtr const &putRequester,
|
||||
TestChannelPtr const &testChannel,
|
||||
PVStructurePtr const & pvRequest);
|
||||
void connect();
|
||||
void waitConnect(double timeout);
|
||||
void stopEvents();
|
||||
private:
|
||||
TestChannelMonitor(
|
||||
TestChannelMonitorRequesterPtr const &putRequester,
|
||||
TestChannelPtr const &testChannel,
|
||||
PVStructurePtr const & pvRequest);
|
||||
|
||||
TestChannelMonitorRequesterWPtr monitorRequester;
|
||||
TestChannelPtr testChannel;
|
||||
PVStructurePtr pvRequest;
|
||||
Event waitForConnect;
|
||||
Monitor::shared_pointer channelMonitor;
|
||||
};
|
||||
|
||||
TestChannelMonitorPtr TestChannelMonitor::create(
|
||||
TestChannelMonitorRequesterPtr const &monitorRequester,
|
||||
TestChannelPtr const &testChannel,
|
||||
PVStructurePtr const & pvRequest)
|
||||
{
|
||||
TestChannelMonitorPtr testChannelMonitor(new TestChannelMonitor(monitorRequester,testChannel,pvRequest));
|
||||
testChannelMonitor->connect();
|
||||
testChannelMonitor->waitConnect(5.0);
|
||||
return testChannelMonitor;
|
||||
}
|
||||
|
||||
TestChannelMonitor::TestChannelMonitor(
|
||||
TestChannelMonitorRequesterPtr const &monitorRequester,
|
||||
TestChannelPtr const &testChannel,
|
||||
PVStructurePtr const & pvRequest)
|
||||
: monitorRequester(monitorRequester),
|
||||
testChannel(testChannel),
|
||||
pvRequest(pvRequest)
|
||||
{
|
||||
}
|
||||
|
||||
string TestChannelMonitor::getRequesterName() {return "TestChannelMonitor";}
|
||||
|
||||
void TestChannelMonitor::monitorConnect(
|
||||
Status const & status,
|
||||
MonitorPtr const & monitor,
|
||||
StructureConstPtr const & structure)
|
||||
{
|
||||
waitForConnect.signal();
|
||||
}
|
||||
|
||||
|
||||
void TestChannelMonitor::monitorEvent(MonitorPtr const & monitor)
|
||||
{
|
||||
TestChannelMonitorRequesterPtr req(monitorRequester.lock());
|
||||
if(!req) return;
|
||||
while(true) {
|
||||
MonitorElementPtr monitorElement = monitor->poll();
|
||||
if(!monitorElement) return;
|
||||
req->monitorEvent(monitorElement->pvStructurePtr,monitorElement->changedBitSet);
|
||||
monitor->release(monitorElement);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TestChannelMonitor::unlisten(MonitorPtr const & monitor)
|
||||
{
|
||||
}
|
||||
|
||||
void TestChannelMonitor::connect()
|
||||
{
|
||||
channelMonitor = testChannel->getChannel()->createMonitor(shared_from_this(),pvRequest);
|
||||
if(!channelMonitor) throw std::runtime_error(testChannel->getChannelName()
|
||||
+ " TestChannelMonitor::connect failed ");
|
||||
}
|
||||
|
||||
void TestChannelMonitor::waitConnect(double timeout)
|
||||
{
|
||||
if(waitForConnect.wait(timeout)) {
|
||||
channelMonitor->start();
|
||||
return;
|
||||
}
|
||||
throw std::runtime_error(testChannel->getChannelName()
|
||||
+ " TestChannelMonitor::waitConnect failed ");
|
||||
}
|
||||
|
||||
void TestChannelMonitor::stopEvents()
|
||||
{
|
||||
channelMonitor->stop();
|
||||
}
|
||||
|
||||
class TestClient;
|
||||
typedef std::tr1::shared_ptr<TestClient> TestClientPtr;
|
||||
|
||||
class TestClient:
|
||||
public TestChannelGetRequester,
|
||||
public TestChannelPutRequester,
|
||||
public TestChannelMonitorRequester,
|
||||
public std::tr1::enable_shared_from_this<TestClient>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(TestClient);
|
||||
virtual void getDone(
|
||||
PVStructure::shared_pointer const & pvStructure,
|
||||
BitSet::shared_pointer const & bitSet);
|
||||
virtual void putDone();
|
||||
virtual void monitorEvent(
|
||||
PVStructure::shared_pointer const & pvStructure,
|
||||
BitSet::shared_pointer const & bitSet);
|
||||
static TestClientPtr create(string const &channelName,PVStructurePtr const & pvRequest);
|
||||
void connect();
|
||||
void get();
|
||||
void put(string const & value);
|
||||
void waitGet(double timeout);
|
||||
void waitPut(double timeout);
|
||||
void stopEvents();
|
||||
private:
|
||||
TestClient(string const &channelName,PVStructurePtr const & pvRequest);
|
||||
string channelName;
|
||||
PVStructurePtr pvRequest;
|
||||
TestChannelPtr testChannel;
|
||||
TestChannelGetPtr testChannelGet;
|
||||
TestChannelPutPtr testChannelPut;
|
||||
TestChannelMonitorPtr testChannelMonitor;
|
||||
Event waitForGet;
|
||||
Event waitForPut;
|
||||
};
|
||||
|
||||
TestClientPtr TestClient::create(string const &channelName,PVStructurePtr const & pvRequest)
|
||||
{
|
||||
TestClientPtr testClient(new TestClient(channelName,pvRequest));
|
||||
testClient->connect();
|
||||
return testClient;
|
||||
}
|
||||
|
||||
TestClient::TestClient(string const &channelName,PVStructurePtr const & pvRequest)
|
||||
: channelName(channelName),
|
||||
pvRequest(pvRequest)
|
||||
{
|
||||
}
|
||||
|
||||
void TestClient::connect()
|
||||
{
|
||||
testChannel = TestChannel::create(channelName);
|
||||
testChannelGet = TestChannelGet::create(shared_from_this(),testChannel,pvRequest);
|
||||
testChannelPut = TestChannelPut::create(shared_from_this(),testChannel);
|
||||
testChannelMonitor = TestChannelMonitor::create(shared_from_this(),testChannel,pvRequest);
|
||||
}
|
||||
|
||||
void TestClient::getDone(
|
||||
PVStructure::shared_pointer const & pvStructure,
|
||||
BitSet::shared_pointer const & bitSet)
|
||||
{
|
||||
testOk(pvStructure!=NULL,"pvStructure not null");
|
||||
testOk(pvStructure->getSubField("value")!=NULL,"value not null");
|
||||
testOk(pvStructure->getSubField("timeStamp")!=NULL,"timeStamp not null");
|
||||
testOk(pvStructure->getSubField("alarm")!=NULL,"alarm not null");
|
||||
if (DEBUG) std::cout << testChannel->getChannelName() + " TestClient::getDone"
|
||||
<< " bitSet " << *bitSet
|
||||
<< " pvStructure\n" << pvStructure << "\n";
|
||||
waitForGet.signal();
|
||||
}
|
||||
|
||||
void TestClient::putDone()
|
||||
{
|
||||
waitForPut.signal();
|
||||
}
|
||||
|
||||
void TestClient::monitorEvent(
|
||||
PVStructure::shared_pointer const & pvStructure,
|
||||
BitSet::shared_pointer const & bitSet)
|
||||
{
|
||||
if (DEBUG) std::cout << testChannel->getChannelName() + " TestClient::monitorEvent"
|
||||
<< " bitSet " << *bitSet
|
||||
<< " pvStructure\n" << pvStructure << "\n";
|
||||
}
|
||||
|
||||
void TestClient::get()
|
||||
{
|
||||
testDiag("TestClient::get %s",
|
||||
testChannel->getChannelName().c_str());
|
||||
testChannelGet->get();
|
||||
if (DEBUG) cout << "TestClient::get() calling waitGet\n";
|
||||
waitGet(5.0);
|
||||
}
|
||||
|
||||
void TestClient::waitGet(double timeout)
|
||||
{
|
||||
testOk(waitForGet.wait(timeout),
|
||||
"waitGet(%s) succeeded", testChannel->getChannelName().c_str());
|
||||
}
|
||||
|
||||
void TestClient::put(string const & value)
|
||||
{
|
||||
testDiag("TestClient::put %s := %s",
|
||||
testChannel->getChannelName().c_str(), value.c_str());
|
||||
testChannelPut->put(value);
|
||||
waitPut(5.0);
|
||||
}
|
||||
|
||||
void TestClient::waitPut(double timeout)
|
||||
{
|
||||
testOk(waitForPut.wait(timeout),
|
||||
"waitPut(%s) succeeded", testChannel->getChannelName().c_str());
|
||||
}
|
||||
|
||||
void TestClient::stopEvents()
|
||||
{
|
||||
testChannelMonitor->stopEvents();
|
||||
}
|
||||
|
||||
class TestIoc;
|
||||
typedef std::tr1::shared_ptr<TestIoc> TestIocPtr;
|
||||
|
||||
class TestIoc :
|
||||
public epicsThreadRunable
|
||||
{
|
||||
public:
|
||||
virtual void run();
|
||||
static TestIocPtr create();
|
||||
void start();
|
||||
void shutdown();
|
||||
private:
|
||||
#ifndef USE_DBUNITTEST
|
||||
std::auto_ptr<epicsThread> thread;
|
||||
const char *base;
|
||||
const char *arch;
|
||||
#endif
|
||||
};
|
||||
|
||||
TestIocPtr TestIoc::create()
|
||||
{
|
||||
return TestIocPtr(new TestIoc());
|
||||
}
|
||||
|
||||
void TestIoc::start()
|
||||
{
|
||||
#ifdef USE_DBUNITTEST
|
||||
testdbPrepare();
|
||||
testdbReadDatabase("testIoc.dbd", NULL, NULL);
|
||||
testIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("testCaProvider.db", NULL, NULL);
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
#else
|
||||
base = getenv("EPICS_BASE");
|
||||
if (!base)
|
||||
testAbort("Environment variable $EPICS_BASE not defined");
|
||||
arch = getenv("EPICS_HOST_ARCH");
|
||||
if (!arch)
|
||||
testAbort("Environment variable $EPICS_HOST_ARCH not defined");
|
||||
setenv("EPICS_CA_ADDR_LIST", "localhost", 1);
|
||||
setenv("EPICS_CA_AUTO_ADDR_LIST", "NO", 1);
|
||||
|
||||
thread = std::auto_ptr<epicsThread>(new epicsThread(
|
||||
*this,
|
||||
"testIoc",
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||
epicsThreadPriorityLow));
|
||||
thread->start();
|
||||
#endif
|
||||
}
|
||||
|
||||
void TestIoc::run()
|
||||
{
|
||||
#ifndef USE_DBUNITTEST
|
||||
// Base-3.14 doesn't provide the dbUnitTest APIs, and the CA
|
||||
// tests with an embedded IOC fail with a Base before 3.16.2.
|
||||
// This version only works on workstation targets, it runs the
|
||||
// softIoc from Base as a separate process, using system().
|
||||
if(system("$EPICS_BASE/bin/$EPICS_HOST_ARCH/softIoc -x test -d ../testCaProvider.db")!=0) {
|
||||
string message(base);
|
||||
message += "/bin/";
|
||||
message += arch;
|
||||
message += "/softIoc -d ../testCaProvider.db not started";
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void TestIoc::shutdown()
|
||||
{
|
||||
#ifdef USE_DBUNITTEST
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
void checkClient(const string &channelName, const string &putValue)
|
||||
{
|
||||
string request("value,alarm,timeStamp");
|
||||
PVStructurePtr pvRequest(createRequest(request));
|
||||
TestClientPtr client = TestClient::create(channelName,pvRequest);
|
||||
if (!client)
|
||||
testAbort("NULL client for %s", channelName.c_str());
|
||||
client->put(putValue);
|
||||
client->get();
|
||||
client->stopEvents();
|
||||
}
|
||||
|
||||
MAIN(testCaProvider)
|
||||
{
|
||||
testPlan(84 + EXIT_TESTS);
|
||||
|
||||
TestIocPtr testIoc(new TestIoc());
|
||||
testIoc->start();
|
||||
|
||||
testDiag("===Test caProvider===");
|
||||
CAClientFactory::start();
|
||||
ChannelProviderRegistry::shared_pointer reg(ChannelProviderRegistry::clients());
|
||||
try {
|
||||
ChannelProvider::shared_pointer channelProvider(reg->getProvider("ca"));
|
||||
if (!channelProvider)
|
||||
testAbort("Channel provider 'ca' not registered");
|
||||
|
||||
checkClient("DBRlongout", "5");
|
||||
checkClient("DBRdoubleout", "1.5");
|
||||
checkClient("DBRstringout", "test");
|
||||
checkClient("DBRbyteArray", "1 2 3");
|
||||
checkClient("DBRshortArray", "1 2 3");
|
||||
checkClient("DBRintArray", "1 2 3");
|
||||
checkClient("DBRubyteArray", "1 2 3");
|
||||
checkClient("DBRushortArray", "1 2 3");
|
||||
checkClient("DBRuintArray", "1 2 3");
|
||||
checkClient("DBRfloatArray", "1 2 3");
|
||||
checkClient("DBRdoubleArray", "1 2 3");
|
||||
checkClient("DBRstringArray", "aa bb cc");
|
||||
checkClient("DBRmbbout", "2");
|
||||
checkClient("DBRbinaryout", "1");
|
||||
|
||||
#ifndef USE_DBUNITTEST
|
||||
// put to record that makes IOC exit
|
||||
string channelName = "test:exit";
|
||||
string request("value");
|
||||
PVStructurePtr pvRequest(createRequest(request));
|
||||
TestClientPtr client = TestClient::create(channelName,pvRequest);
|
||||
if (!client)
|
||||
testAbort("NULL client for %s", channelName.c_str());
|
||||
client->put("1");
|
||||
client->stopEvents();
|
||||
#endif
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
testAbort("caught un-expected exception: %s", e.what());
|
||||
}
|
||||
|
||||
testIoc->shutdown();
|
||||
|
||||
return testDone();;
|
||||
}
|
||||
|
||||
278
testCa/testCaProvider.db
Normal file
278
testCa/testCaProvider.db
Normal file
@@ -0,0 +1,278 @@
|
||||
|
||||
record(calcout, "DBRcalcout")
|
||||
{
|
||||
field(DESC, "calcout")
|
||||
field(CALC, "(A<B)?(A+C):D")
|
||||
field(INPA, "DBRcalcout.VAL NPP NMS")
|
||||
field(INPB, "9")
|
||||
field(INPC, "1")
|
||||
field(INPD, "0")
|
||||
field(EGU, "Counts")
|
||||
field(HOPR, "10")
|
||||
field(HIHI, "8")
|
||||
field(HIGH, "6")
|
||||
field(LOW, "4")
|
||||
field(LOLO, "2")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HSV, "MINOR")
|
||||
field(LSV, "MINOR")
|
||||
field(LLSV, "MAJOR")
|
||||
}
|
||||
|
||||
record(calc, "DBRcalcin")
|
||||
{
|
||||
field(DESC, "calcin")
|
||||
field(CALC, "(A<B)?(A+C):D")
|
||||
field(INPA, "DBRcalcin.VAL NPP NMS")
|
||||
field(INPB, "9")
|
||||
field(INPC, "1")
|
||||
field(INPD, "0")
|
||||
field(EGU, "Counts")
|
||||
field(HOPR, "10")
|
||||
field(HIHI, "8")
|
||||
field(HIGH, "6")
|
||||
field(LOW, "4")
|
||||
field(LOLO, "2")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HSV, "MINOR")
|
||||
field(LSV, "MINOR")
|
||||
field(LLSV, "MAJOR")
|
||||
}
|
||||
|
||||
record(longin,"DBRlongin") {
|
||||
field(DESC, "longin")
|
||||
field(EGU, "volts")
|
||||
field(HIHI, "80")
|
||||
field(HIGH, "60")
|
||||
field(LOW, "-60")
|
||||
field(LOLO, "-80")
|
||||
field(HYST, "2")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HSV, "MINOR")
|
||||
field(LSV, "MINOR")
|
||||
field(LLSV, "MAJOR")
|
||||
field(HOPR, "2147483647")
|
||||
field(LOPR, "-2147483648")
|
||||
}
|
||||
|
||||
record(longout,"DBRlongout") {
|
||||
field(DESC, "longout")
|
||||
field(EGU, "volts")
|
||||
field(HIHI, "80")
|
||||
field(HIGH, "60")
|
||||
field(LOW, "-60")
|
||||
field(LOLO, "-80")
|
||||
field(HYST, "2")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HSV, "MINOR")
|
||||
field(LSV, "MINOR")
|
||||
field(LLSV, "MAJOR")
|
||||
field(HOPR, "2147483647")
|
||||
field(LOPR, "-2147483648")
|
||||
field(DRVH, "2147483647")
|
||||
field(DRVL, "-2147483648")
|
||||
}
|
||||
|
||||
record(ao,"DBRdoubleout") {
|
||||
field(DESC, "ao")
|
||||
field(PREC,"2")
|
||||
field(EGUF,"10.0")
|
||||
field(EGUL,"-10.0")
|
||||
field(EGU, "volts")
|
||||
field(HIHI, "8")
|
||||
field(HIGH, "6")
|
||||
field(LOW, "-6")
|
||||
field(LOLO, "-8")
|
||||
field(HYST, ".1")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HSV, "MINOR")
|
||||
field(LSV, "MINOR")
|
||||
field(LLSV, "MAJOR")
|
||||
field(HOPR, "10.0")
|
||||
field(LOPR, "-10.0")
|
||||
field(DRVH, "1e29")
|
||||
field(DRVL, "-1e29")
|
||||
}
|
||||
|
||||
record(ai,"DBRdoublein") {
|
||||
field(DESC, "ai")
|
||||
field(PREC,"2")
|
||||
field(EGUF,"10.0")
|
||||
field(EGUL,"-10.0")
|
||||
field(EGU, "volts")
|
||||
field(HIHI, "8")
|
||||
field(HIGH, "6")
|
||||
field(LOW, "-6")
|
||||
field(LOLO, "-8")
|
||||
field(HYST, ".1")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HSV, "MINOR")
|
||||
field(LSV, "MINOR")
|
||||
field(LLSV, "MAJOR")
|
||||
field(HOPR, "10.0")
|
||||
field(LOPR, "-10.0")
|
||||
}
|
||||
|
||||
|
||||
record(stringout,"DBRstringout") {
|
||||
field(DESC, "stringout")
|
||||
}
|
||||
|
||||
record(stringin,"DBRstringin") {
|
||||
field(DESC, "stringin")
|
||||
}
|
||||
|
||||
|
||||
record(bo,"DBRbinaryout") {
|
||||
field(DESC, "bo")
|
||||
field(ZNAM,"zero")
|
||||
field(ONAM,"one")
|
||||
}
|
||||
|
||||
record(mbbo,"DBRmbbout") {
|
||||
field(DESC, "mbbo")
|
||||
field(VAL,"1")
|
||||
field(ZRVL,"0")
|
||||
field(ONVL,"1")
|
||||
field(TWVL,"2")
|
||||
field(THVL,"3")
|
||||
field(FRVL,"4")
|
||||
field(FVVL,"5")
|
||||
field(SXVL,"6")
|
||||
field(SVVL,"7")
|
||||
field(EIVL,"8")
|
||||
field(NIVL,"9")
|
||||
field(TEVL,"10")
|
||||
field(ELVL,"11")
|
||||
field(TVVL,"12")
|
||||
field(TTVL,"13")
|
||||
field(FTVL,"14")
|
||||
field(FFVL,"15")
|
||||
field(ZRST,"zero")
|
||||
field(ONST,"one")
|
||||
field(TWST,"two")
|
||||
field(THST,"three")
|
||||
field(FRST,"four")
|
||||
field(FVST,"five")
|
||||
field(SXST,"six")
|
||||
field(SVST,"seven")
|
||||
field(EIST,"eight")
|
||||
field(NIST,"nine")
|
||||
field(TEST,"ten")
|
||||
field(ELST,"eleven")
|
||||
field(TVST,"twelve")
|
||||
field(TTST,"thirteen")
|
||||
field(FTST,"fourteen")
|
||||
field(FFST,"fifteen")
|
||||
}
|
||||
|
||||
record(mbbi,"DBRmbbin") {
|
||||
field(DESC, "mbbi")
|
||||
field(VAL,"1")
|
||||
field(ZRVL,"0")
|
||||
field(ONVL,"1")
|
||||
field(TWVL,"2")
|
||||
field(THVL,"3")
|
||||
field(FRVL,"4")
|
||||
field(FVVL,"5")
|
||||
field(SXVL,"6")
|
||||
field(SVVL,"7")
|
||||
field(EIVL,"8")
|
||||
field(NIVL,"9")
|
||||
field(TEVL,"10")
|
||||
field(ELVL,"11")
|
||||
field(TVVL,"12")
|
||||
field(TTVL,"13")
|
||||
field(FTVL,"14")
|
||||
field(FFVL,"15")
|
||||
field(ZRST,"zero")
|
||||
field(ONST,"one")
|
||||
field(TWST,"two")
|
||||
field(THST,"three")
|
||||
field(FRST,"four")
|
||||
field(FVST,"five")
|
||||
field(SXST,"six")
|
||||
field(SVST,"seven")
|
||||
field(EIST,"eight")
|
||||
field(NIST,"nine")
|
||||
field(TEST,"ten")
|
||||
field(ELST,"eleven")
|
||||
field(TVST,"twelve")
|
||||
field(TTST,"thirteen")
|
||||
field(FTST,"fourteen")
|
||||
field(FFST,"fifteen")
|
||||
}
|
||||
|
||||
record(waveform,"DBRbyteArray") {
|
||||
field(DESC, "byteArray")
|
||||
field(NELM,"5")
|
||||
field(FTVL,"CHAR")
|
||||
field(HOPR, "127")
|
||||
field(LOPR, "-128")
|
||||
}
|
||||
|
||||
record(waveform,"DBRshortArray") {
|
||||
field(DESC, "shortArray")
|
||||
field(NELM,"5")
|
||||
field(FTVL,"SHORT")
|
||||
field(HOPR, "32767")
|
||||
field(LOPR, "-32768")
|
||||
}
|
||||
|
||||
record(waveform,"DBRintArray") {
|
||||
field(DESC, "intArray")
|
||||
field(NELM,"5")
|
||||
field(FTVL,"LONG")
|
||||
field(HOPR, "2147483647")
|
||||
field(LOPR, "-2147483648")
|
||||
}
|
||||
|
||||
record(waveform,"DBRubyteArray") {
|
||||
field(DESC, "ubyteArray")
|
||||
field(NELM,"5")
|
||||
field(FTVL,"UCHAR")
|
||||
field(HOPR, "255")
|
||||
field(LOPR, "0")
|
||||
}
|
||||
|
||||
|
||||
record(waveform,"DBRushortArray") {
|
||||
field(DESC, "ushortArray")
|
||||
field(NELM,"5")
|
||||
field(FTVL,"USHORT")
|
||||
field(HOPR, "65535")
|
||||
field(LOPR, "0")
|
||||
}
|
||||
|
||||
record(waveform,"DBRuintArray") {
|
||||
field(DESC, "uintArray")
|
||||
field(NELM,"5")
|
||||
field(FTVL,"ULONG")
|
||||
field(HOPR, "2147483647")
|
||||
field(LOPR, "-2147483648")
|
||||
}
|
||||
|
||||
|
||||
record(waveform,"DBRstringArray") {
|
||||
field(DESC, "stringArray")
|
||||
field(NELM,"5")
|
||||
field(FTVL,"STRING")
|
||||
}
|
||||
|
||||
record(waveform,"DBRfloatArray") {
|
||||
field(DESC, "floatArray")
|
||||
field(NELM,"5")
|
||||
field(PREC,"2")
|
||||
field(FTVL,"FLOAT")
|
||||
field(HOPR, "1000")
|
||||
field(LOPR, "-1000")
|
||||
}
|
||||
|
||||
record(waveform,"DBRdoubleArray") {
|
||||
field(DESC, "doubleArray")
|
||||
field(NELM,"5")
|
||||
field(PREC,"2")
|
||||
field(FTVL,"DOUBLE")
|
||||
field(HOPR, "1000")
|
||||
field(LOPR, "-1000")
|
||||
}
|
||||
Reference in New Issue
Block a user