Merge caProvider changes (PR #131) from mrkraimer
ANJ: Added some items to release_notes.dox Fixes: Github issue #126
This commit is contained in:
622
caProvider.md
622
caProvider.md
@ -1,16 +1,20 @@
|
|||||||
# pvAccessCPP: ca provider
|
# pvAccessCPP: ca provider
|
||||||
|
|
||||||
2018.07.09
|
2018.10.05
|
||||||
|
|
||||||
Editors:
|
Editors:
|
||||||
|
|
||||||
* Marty Kraimer
|
* Marty Kraimer
|
||||||
|
|
||||||
This is a description of channel provider **ca** that is implemented as part of **pvAccessCPP**.
|
**ca** is a channel provider **ca** that is implemented as part of **pvAccessCPP**.
|
||||||
|
|
||||||
|
|
||||||
It uses the **channel access** network protocol to communicate with a server,
|
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.
|
i. e. the network protocol that has been used to communicate with **EPICS IOCs** since 1990.
|
||||||
|
|
||||||
|
A description of **ca** is provided in
|
||||||
|
[caProvider](https://mrkraimer.github.io/website/caProvider/caProvider.html)
|
||||||
|
|
||||||
Provider **pva** is another way to connect to a **DBRecord**,
|
Provider **pva** is another way to connect to a **DBRecord**,
|
||||||
But this only works if the IOC has **qsrv** installed.
|
But this only works if the IOC has **qsrv** installed.
|
||||||
**qsrv**, which is provided with
|
**qsrv**, which is provided with
|
||||||
@ -19,618 +23,4 @@ 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 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.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ Release 6.1.0 (UNRELEASED)
|
|||||||
- PVA Server now handles pipeline=true automatically for all ChannelProviders.
|
- PVA Server now handles pipeline=true automatically for all ChannelProviders.
|
||||||
It is only necessary to implement epics::pvAccess::Monitor::reportRemoteQueueStatus()
|
It is only necessary to implement epics::pvAccess::Monitor::reportRemoteQueueStatus()
|
||||||
to receive notification of initial window size.
|
to receive notification of initial window size.
|
||||||
|
- Most locking issues in the 'ca' provider should now be resolved.
|
||||||
- Additions
|
- Additions
|
||||||
- pvput to NTEnum via. string now supported
|
- pvput to NTEnum via. string now supported
|
||||||
- pvac::* add valid() method and boolean cast shorthand. Also reset() and operator<<(ostream, ...)
|
- pvac::* add valid() method and boolean cast shorthand. Also reset() and operator<<(ostream, ...)
|
||||||
@ -31,6 +32,10 @@ Release 6.1.0 (UNRELEASED)
|
|||||||
- Add pvac::ClientProvider::name()
|
- Add pvac::ClientProvider::name()
|
||||||
- Add epics::pvAccess::MonitorFIFO utility implementation of epics::pvAccess::Monitor
|
- Add epics::pvAccess::MonitorFIFO utility implementation of epics::pvAccess::Monitor
|
||||||
- Add pvas::StaticProvider, pvas::DynamicProvider, and pvas::SharedPV. See @ref pvas
|
- Add pvas::StaticProvider, pvas::DynamicProvider, and pvas::SharedPV. See @ref pvas
|
||||||
|
- Support in the 'ca' provider for new pvRequest options:
|
||||||
|
- New `DBE` record option to select CA events to monitor, containing any combination of the keywords `VALUE`, `ARCHIVE`, `ALARM` and/or `PROPERTY`.
|
||||||
|
- New `dbtype` field option to represent data types returned by CA as an unsigned or 64-bit integer.
|
||||||
|
- New `pvtype=pvString` field option to represent an array of chars from CA as a string.
|
||||||
|
|
||||||
Release 6.0.0 (Dec 2017)
|
Release 6.0.0 (Dec 2017)
|
||||||
========================
|
========================
|
||||||
|
@ -11,6 +11,7 @@ LIB_SYS_LIBS_WIN32 += ws2_32
|
|||||||
|
|
||||||
INC += pv/caProvider.h
|
INC += pv/caProvider.h
|
||||||
|
|
||||||
|
pvAccessCA_SRCS += channelConnectThread.cpp
|
||||||
pvAccessCA_SRCS += monitorEventThread.cpp
|
pvAccessCA_SRCS += monitorEventThread.cpp
|
||||||
pvAccessCA_SRCS += getDoneThread.cpp
|
pvAccessCA_SRCS += getDoneThread.cpp
|
||||||
pvAccessCA_SRCS += putDoneThread.cpp
|
pvAccessCA_SRCS += putDoneThread.cpp
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <pv/standardField.h>
|
#include <pv/standardField.h>
|
||||||
#include <pv/logger.h>
|
#include <pv/logger.h>
|
||||||
#include <pv/pvAccess.h>
|
#include <pv/pvAccess.h>
|
||||||
|
#include "channelConnectThread.h"
|
||||||
#include "monitorEventThread.h"
|
#include "monitorEventThread.h"
|
||||||
#include "getDoneThread.h"
|
#include "getDoneThread.h"
|
||||||
#include "putDoneThread.h"
|
#include "putDoneThread.h"
|
||||||
@ -51,16 +52,43 @@ static void ca_connection_handler(struct connection_handler_args args)
|
|||||||
CAChannel *channel = static_cast<CAChannel*>(ca_puser(args.chid));
|
CAChannel *channel = static_cast<CAChannel*>(ca_puser(args.chid));
|
||||||
|
|
||||||
if (args.op == CA_OP_CONN_UP) {
|
if (args.op == CA_OP_CONN_UP) {
|
||||||
channel->connected();
|
channel->connect(true);
|
||||||
} else if (args.op == CA_OP_CONN_DOWN) {
|
} else if (args.op == CA_OP_CONN_DOWN) {
|
||||||
channel->disconnected();
|
channel->connect(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAChannel::connected()
|
void CAChannel::connect(bool isConnected)
|
||||||
{
|
{
|
||||||
if(DEBUG_LEVEL>0) {
|
if(DEBUG_LEVEL>0) {
|
||||||
cout<< "CAChannel::connected " << channelName << endl;
|
cout<< "CAChannel::connect " << channelName << endl;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Lock lock(requestsMutex);
|
||||||
|
channelConnected = isConnected;
|
||||||
|
}
|
||||||
|
channelConnectThread->channelConnected(notifyChannelRequester);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAChannel::notifyClient()
|
||||||
|
{
|
||||||
|
if(DEBUG_LEVEL>0) {
|
||||||
|
cout<< "CAChannel::notifyClient " << channelName << endl;
|
||||||
|
}
|
||||||
|
CAChannelProviderPtr provider(channelProvider.lock());
|
||||||
|
if(!provider) return;
|
||||||
|
bool isConnected = false;
|
||||||
|
{
|
||||||
|
Lock lock(requestsMutex);
|
||||||
|
isConnected = channelConnected;
|
||||||
|
}
|
||||||
|
if(!isConnected) {
|
||||||
|
ChannelRequester::shared_pointer req(channelRequester.lock());
|
||||||
|
if(req) {
|
||||||
|
EXCEPTION_GUARD(req->channelStateChange(
|
||||||
|
shared_from_this(), Channel::DISCONNECTED));
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
while(!getFieldQueue.empty()) {
|
while(!getFieldQueue.empty()) {
|
||||||
getFieldQueue.front()->activate();
|
getFieldQueue.front()->activate();
|
||||||
@ -87,18 +115,6 @@ void CAChannel::connected()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAChannel::disconnected()
|
|
||||||
{
|
|
||||||
if(DEBUG_LEVEL>0) {
|
|
||||||
cout<< "CAChannel::disconnected " << channelName << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
ChannelRequester::shared_pointer req(channelRequester.lock());
|
|
||||||
if(req) {
|
|
||||||
EXCEPTION_GUARD(req->channelStateChange(
|
|
||||||
shared_from_this(), Channel::DISCONNECTED));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CAChannel::CAChannel(std::string const & channelName,
|
CAChannel::CAChannel(std::string const & channelName,
|
||||||
CAChannelProvider::shared_pointer const & channelProvider,
|
CAChannelProvider::shared_pointer const & channelProvider,
|
||||||
@ -107,7 +123,9 @@ CAChannel::CAChannel(std::string const & channelName,
|
|||||||
channelProvider(channelProvider),
|
channelProvider(channelProvider),
|
||||||
channelRequester(channelRequester),
|
channelRequester(channelRequester),
|
||||||
channelID(0),
|
channelID(0),
|
||||||
channelCreated(false)
|
channelCreated(false),
|
||||||
|
channelConnected(false),
|
||||||
|
channelConnectThread(ChannelConnectThread::get())
|
||||||
{
|
{
|
||||||
if(DEBUG_LEVEL>0) {
|
if(DEBUG_LEVEL>0) {
|
||||||
cout<< "CAChannel::CAChannel " << channelName << endl;
|
cout<< "CAChannel::CAChannel " << channelName << endl;
|
||||||
@ -116,11 +134,13 @@ CAChannel::CAChannel(std::string const & channelName,
|
|||||||
|
|
||||||
void CAChannel::activate(short priority)
|
void CAChannel::activate(short priority)
|
||||||
{
|
{
|
||||||
|
ChannelRequester::shared_pointer req(channelRequester.lock());
|
||||||
|
if(!req) return;
|
||||||
if(DEBUG_LEVEL>0) {
|
if(DEBUG_LEVEL>0) {
|
||||||
cout<< "CAChannel::activate " << channelName << endl;
|
cout<< "CAChannel::activate " << channelName << endl;
|
||||||
}
|
}
|
||||||
ChannelRequester::shared_pointer req(channelRequester.lock());
|
notifyChannelRequester = NotifyChannelRequesterPtr(new NotifyChannelRequester());
|
||||||
if(!req) return;
|
notifyChannelRequester->setChannel(shared_from_this());
|
||||||
attachContext();
|
attachContext();
|
||||||
int result = ca_create_channel(channelName.c_str(),
|
int result = ca_create_channel(channelName.c_str(),
|
||||||
ca_connection_handler,
|
ca_connection_handler,
|
||||||
@ -382,8 +402,7 @@ void CAChannelGetField::callRequester(CAChannelPtr const & caChannel)
|
|||||||
if(!requester) return;
|
if(!requester) return;
|
||||||
PVStructurePtr pvRequest(createRequest(""));
|
PVStructurePtr pvRequest(createRequest(""));
|
||||||
DbdToPvPtr dbdToPv = DbdToPv::create(caChannel,pvRequest,getIO);
|
DbdToPvPtr dbdToPv = DbdToPv::create(caChannel,pvRequest,getIO);
|
||||||
PVStructurePtr pvStructure = dbdToPv->createPVStructure();
|
Structure::const_shared_pointer structure(dbdToPv->getStructure());
|
||||||
Structure::const_shared_pointer structure(pvStructure->getStructure());
|
|
||||||
Field::const_shared_pointer field =
|
Field::const_shared_pointer field =
|
||||||
subField.empty() ?
|
subField.empty() ?
|
||||||
std::tr1::static_pointer_cast<const Field>(structure) :
|
std::tr1::static_pointer_cast<const Field>(structure) :
|
||||||
@ -451,6 +470,7 @@ void CAChannelGet::activate()
|
|||||||
std::cout << "CAChannelGet::activate " << channel->getChannelName() << endl;
|
std::cout << "CAChannelGet::activate " << channel->getChannelName() << endl;
|
||||||
}
|
}
|
||||||
dbdToPv = DbdToPv::create(channel,pvRequest,getIO);
|
dbdToPv = DbdToPv::create(channel,pvRequest,getIO);
|
||||||
|
dbdToPv->getChoices(channel);
|
||||||
pvStructure = dbdToPv->createPVStructure();
|
pvStructure = dbdToPv->createPVStructure();
|
||||||
bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields()));
|
bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields()));
|
||||||
notifyGetRequester = NotifyGetRequesterPtr(new NotifyGetRequester());
|
notifyGetRequester = NotifyGetRequesterPtr(new NotifyGetRequester());
|
||||||
@ -488,6 +508,9 @@ void CAChannelGet::getDone(struct event_handler_args &args)
|
|||||||
|
|
||||||
void CAChannelGet::notifyClient()
|
void CAChannelGet::notifyClient()
|
||||||
{
|
{
|
||||||
|
if(DEBUG_LEVEL>1) {
|
||||||
|
std::cout << "CAChannelGet::notifyClient " << channel->getChannelName() << endl;
|
||||||
|
}
|
||||||
ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock());
|
ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock());
|
||||||
if(!getRequester) return;
|
if(!getRequester) return;
|
||||||
EXCEPTION_GUARD(getRequester->getDone(getStatus, shared_from_this(), pvStructure, bitSet));
|
EXCEPTION_GUARD(getRequester->getDone(getStatus, shared_from_this(), pvStructure, bitSet));
|
||||||
@ -572,6 +595,7 @@ void CAChannelPut::activate()
|
|||||||
cout << "CAChannelPut::activate " << channel->getChannelName() << endl;
|
cout << "CAChannelPut::activate " << channel->getChannelName() << endl;
|
||||||
}
|
}
|
||||||
dbdToPv = DbdToPv::create(channel,pvRequest,putIO);
|
dbdToPv = DbdToPv::create(channel,pvRequest,putIO);
|
||||||
|
dbdToPv->getChoices(channel);
|
||||||
pvStructure = dbdToPv->createPVStructure();
|
pvStructure = dbdToPv->createPVStructure();
|
||||||
bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields()));
|
bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields()));
|
||||||
PVStringPtr pvString = pvRequest->getSubField<PVString>("record._options.block");
|
PVStringPtr pvString = pvRequest->getSubField<PVString>("record._options.block");
|
||||||
@ -809,7 +833,8 @@ CAChannelMonitor::CAChannelMonitor(
|
|||||||
pvRequest(pvRequest),
|
pvRequest(pvRequest),
|
||||||
isStarted(false),
|
isStarted(false),
|
||||||
monitorEventThread(MonitorEventThread::get()),
|
monitorEventThread(MonitorEventThread::get()),
|
||||||
pevid(NULL)
|
pevid(NULL),
|
||||||
|
eventMask(DBE_VALUE | DBE_ALARM)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
CAChannelMonitor::~CAChannelMonitor()
|
CAChannelMonitor::~CAChannelMonitor()
|
||||||
@ -831,6 +856,7 @@ void CAChannelMonitor::activate()
|
|||||||
std::cout << "CAChannelMonitor::activate " << channel->getChannelName() << endl;
|
std::cout << "CAChannelMonitor::activate " << channel->getChannelName() << endl;
|
||||||
}
|
}
|
||||||
dbdToPv = DbdToPv::create(channel,pvRequest,monitorIO);
|
dbdToPv = DbdToPv::create(channel,pvRequest,monitorIO);
|
||||||
|
dbdToPv->getChoices(channel);
|
||||||
pvStructure = dbdToPv->createPVStructure();
|
pvStructure = dbdToPv->createPVStructure();
|
||||||
activeElement = MonitorElementPtr(new MonitorElement(pvStructure));
|
activeElement = MonitorElementPtr(new MonitorElement(pvStructure));
|
||||||
int32 queueSize = 2;
|
int32 queueSize = 2;
|
||||||
@ -844,6 +870,15 @@ void CAChannelMonitor::activate()
|
|||||||
ss >> size;
|
ss >> size;
|
||||||
if (size > 1) queueSize = size;
|
if (size > 1) queueSize = size;
|
||||||
}
|
}
|
||||||
|
pvString = pvOptions->getSubField<PVString>("DBE");
|
||||||
|
if(pvString) {
|
||||||
|
std::string value(pvString->get());
|
||||||
|
eventMask = 0;
|
||||||
|
if(value.find("VALUE")!=std::string::npos) eventMask|=DBE_VALUE;
|
||||||
|
if(value.find("ARCHIVE")!=std::string::npos) eventMask|=DBE_ARCHIVE;
|
||||||
|
if(value.find("ALARM")!=std::string::npos) eventMask|=DBE_ALARM;
|
||||||
|
if(value.find("PROPERTY")!=std::string::npos) eventMask|=DBE_PROPERTY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
notifyMonitorRequester = NotifyMonitorRequesterPtr(new NotifyMonitorRequester());
|
notifyMonitorRequester = NotifyMonitorRequesterPtr(new NotifyMonitorRequester());
|
||||||
notifyMonitorRequester->setChannelMonitor(shared_from_this());
|
notifyMonitorRequester->setChannelMonitor(shared_from_this());
|
||||||
@ -916,7 +951,7 @@ Status CAChannelMonitor::start()
|
|||||||
channel->attachContext();
|
channel->attachContext();
|
||||||
int result = ca_create_subscription(dbdToPv->getRequestType(),
|
int result = ca_create_subscription(dbdToPv->getRequestType(),
|
||||||
0,
|
0,
|
||||||
channel->getChannelID(), DBE_VALUE,
|
channel->getChannelID(), eventMask,
|
||||||
ca_subscription_handler, this,
|
ca_subscription_handler, this,
|
||||||
&pevid);
|
&pevid);
|
||||||
if (result == ECA_NORMAL)
|
if (result == ECA_NORMAL)
|
||||||
|
@ -28,6 +28,15 @@ namespace epics {
|
|||||||
namespace pvAccess {
|
namespace pvAccess {
|
||||||
namespace ca {
|
namespace ca {
|
||||||
|
|
||||||
|
class CAChannel;
|
||||||
|
typedef std::tr1::shared_ptr<CAChannel> CAChannelPtr;
|
||||||
|
typedef std::tr1::weak_ptr<CAChannel> CAChannelWPtr;
|
||||||
|
class ChannelConnectThread;
|
||||||
|
typedef std::tr1::shared_ptr<ChannelConnectThread> ChannelConnectThreadPtr;
|
||||||
|
|
||||||
|
class NotifyChannelRequester;
|
||||||
|
typedef std::tr1::shared_ptr<NotifyChannelRequester> NotifyChannelRequesterPtr;
|
||||||
|
|
||||||
class NotifyMonitorRequester;
|
class NotifyMonitorRequester;
|
||||||
typedef std::tr1::shared_ptr<NotifyMonitorRequester> NotifyMonitorRequesterPtr;
|
typedef std::tr1::shared_ptr<NotifyMonitorRequester> NotifyMonitorRequesterPtr;
|
||||||
class MonitorEventThread;
|
class MonitorEventThread;
|
||||||
@ -88,9 +97,6 @@ public:
|
|||||||
short priority,
|
short priority,
|
||||||
ChannelRequester::shared_pointer const & channelRequester);
|
ChannelRequester::shared_pointer const & channelRequester);
|
||||||
virtual ~CAChannel();
|
virtual ~CAChannel();
|
||||||
|
|
||||||
void connected();
|
|
||||||
void disconnected();
|
|
||||||
chid getChannelID();
|
chid getChannelID();
|
||||||
|
|
||||||
virtual std::tr1::shared_ptr<ChannelProvider> getProvider();
|
virtual std::tr1::shared_ptr<ChannelProvider> getProvider();
|
||||||
@ -113,6 +119,8 @@ public:
|
|||||||
|
|
||||||
void attachContext();
|
void attachContext();
|
||||||
void disconnectChannel();
|
void disconnectChannel();
|
||||||
|
void connect(bool isConnected);
|
||||||
|
void notifyClient();
|
||||||
private:
|
private:
|
||||||
virtual void destroy() {}
|
virtual void destroy() {}
|
||||||
CAChannel(std::string const & channelName,
|
CAChannel(std::string const & channelName,
|
||||||
@ -126,6 +134,9 @@ private:
|
|||||||
ChannelRequester::weak_pointer channelRequester;
|
ChannelRequester::weak_pointer channelRequester;
|
||||||
chid channelID;
|
chid channelID;
|
||||||
bool channelCreated;
|
bool channelCreated;
|
||||||
|
bool channelConnected;
|
||||||
|
ChannelConnectThreadPtr channelConnectThread;
|
||||||
|
NotifyChannelRequesterPtr notifyChannelRequester;
|
||||||
|
|
||||||
epics::pvData::Mutex requestsMutex;
|
epics::pvData::Mutex requestsMutex;
|
||||||
std::queue<CAChannelGetFieldPtr> getFieldQueue;
|
std::queue<CAChannelGetFieldPtr> getFieldQueue;
|
||||||
@ -253,6 +264,7 @@ private:
|
|||||||
bool isStarted;
|
bool isStarted;
|
||||||
MonitorEventThreadPtr monitorEventThread;
|
MonitorEventThreadPtr monitorEventThread;
|
||||||
evid pevid;
|
evid pevid;
|
||||||
|
unsigned long eventMask;
|
||||||
NotifyMonitorRequesterPtr notifyMonitorRequester;
|
NotifyMonitorRequesterPtr notifyMonitorRequester;
|
||||||
|
|
||||||
DbdToPvPtr dbdToPv;
|
DbdToPvPtr dbdToPv;
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <pv/logger.h>
|
#include <pv/logger.h>
|
||||||
#include <pv/pvAccess.h>
|
#include <pv/pvAccess.h>
|
||||||
|
|
||||||
|
#include "channelConnectThread.h"
|
||||||
#include "monitorEventThread.h"
|
#include "monitorEventThread.h"
|
||||||
#include "getDoneThread.h"
|
#include "getDoneThread.h"
|
||||||
#include "putDoneThread.h"
|
#include "putDoneThread.h"
|
||||||
@ -39,6 +40,7 @@ CAChannelProvider::CAChannelProvider()
|
|||||||
|
|
||||||
CAChannelProvider::CAChannelProvider(const std::tr1::shared_ptr<Configuration>&)
|
CAChannelProvider::CAChannelProvider(const std::tr1::shared_ptr<Configuration>&)
|
||||||
: current_context(0),
|
: current_context(0),
|
||||||
|
channelConnectThread(ChannelConnectThread::get()),
|
||||||
monitorEventThread(MonitorEventThread::get()),
|
monitorEventThread(MonitorEventThread::get()),
|
||||||
getDoneThread(GetDoneThread::get()),
|
getDoneThread(GetDoneThread::get()),
|
||||||
putDoneThread(PutDoneThread::get())
|
putDoneThread(PutDoneThread::get())
|
||||||
@ -75,9 +77,10 @@ CAChannelProvider::~CAChannelProvider()
|
|||||||
channelQ.front()->disconnectChannel();
|
channelQ.front()->disconnectChannel();
|
||||||
channelQ.pop();
|
channelQ.pop();
|
||||||
}
|
}
|
||||||
monitorEventThread->stop();
|
|
||||||
getDoneThread->stop();
|
|
||||||
putDoneThread->stop();
|
putDoneThread->stop();
|
||||||
|
getDoneThread->stop();
|
||||||
|
monitorEventThread->stop();
|
||||||
|
channelConnectThread->stop();
|
||||||
if(DEBUG_LEVEL>0) {
|
if(DEBUG_LEVEL>0) {
|
||||||
std::cout << "CAChannelProvider::~CAChannelProvider() calling ca_context_destroy\n";
|
std::cout << "CAChannelProvider::~CAChannelProvider() calling ca_context_destroy\n";
|
||||||
}
|
}
|
||||||
@ -174,10 +177,8 @@ void CAChannelProvider::attachContext()
|
|||||||
{
|
{
|
||||||
ca_client_context* thread_context = ca_current_context();
|
ca_client_context* thread_context = ca_current_context();
|
||||||
if (thread_context == current_context) return;
|
if (thread_context == current_context) return;
|
||||||
if (thread_context != NULL) {
|
|
||||||
throw std::runtime_error("CAChannelProvider::attachContext Foreign CA context in use");
|
|
||||||
}
|
|
||||||
int result = ca_attach_context(current_context);
|
int result = ca_attach_context(current_context);
|
||||||
|
if(result==ECA_ISATTACHED) return;
|
||||||
if (result != ECA_NORMAL) {
|
if (result != ECA_NORMAL) {
|
||||||
std::string mess("CAChannelProvider::attachContext error calling ca_attach_context ");
|
std::string mess("CAChannelProvider::attachContext error calling ca_attach_context ");
|
||||||
mess += ca_message(result);
|
mess += ca_message(result);
|
||||||
|
@ -24,6 +24,9 @@ namespace ca {
|
|||||||
|
|
||||||
#define DEBUG_LEVEL 0
|
#define DEBUG_LEVEL 0
|
||||||
|
|
||||||
|
class ChannelConnectThread;
|
||||||
|
typedef std::tr1::shared_ptr<ChannelConnectThread> ChannelConnectThreadPtr;
|
||||||
|
|
||||||
class MonitorEventThread;
|
class MonitorEventThread;
|
||||||
typedef std::tr1::shared_ptr<MonitorEventThread> MonitorEventThreadPtr;
|
typedef std::tr1::shared_ptr<MonitorEventThread> MonitorEventThreadPtr;
|
||||||
|
|
||||||
@ -86,6 +89,7 @@ private:
|
|||||||
ca_client_context* current_context;
|
ca_client_context* current_context;
|
||||||
epics::pvData::Mutex channelListMutex;
|
epics::pvData::Mutex channelListMutex;
|
||||||
std::vector<CAChannelWPtr> caChannelList;
|
std::vector<CAChannelWPtr> caChannelList;
|
||||||
|
ChannelConnectThreadPtr channelConnectThread;
|
||||||
MonitorEventThreadPtr monitorEventThread;
|
MonitorEventThreadPtr monitorEventThread;
|
||||||
GetDoneThreadPtr getDoneThread;
|
GetDoneThreadPtr getDoneThread;
|
||||||
PutDoneThreadPtr putDoneThread;
|
PutDoneThreadPtr putDoneThread;
|
||||||
|
115
src/ca/channelConnectThread.cpp
Normal file
115
src/ca/channelConnectThread.cpp
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/**
|
||||||
|
* 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 "channelConnectThread.h"
|
||||||
|
|
||||||
|
using namespace epics::pvData;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace epics {
|
||||||
|
namespace pvAccess {
|
||||||
|
namespace ca {
|
||||||
|
|
||||||
|
ChannelConnectThreadPtr ChannelConnectThread::get()
|
||||||
|
{
|
||||||
|
static ChannelConnectThreadPtr master;
|
||||||
|
static Mutex mutex;
|
||||||
|
Lock xx(mutex);
|
||||||
|
if(!master) {
|
||||||
|
master = ChannelConnectThreadPtr(new ChannelConnectThread());
|
||||||
|
master->start();
|
||||||
|
}
|
||||||
|
return master;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChannelConnectThread::ChannelConnectThread()
|
||||||
|
: isStop(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ChannelConnectThread::~ChannelConnectThread()
|
||||||
|
{
|
||||||
|
//std::cout << "ChannelConnectThread::~ChannelConnectThread()\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ChannelConnectThread::start()
|
||||||
|
{
|
||||||
|
thread = std::tr1::shared_ptr<epicsThread>(new epicsThread(
|
||||||
|
*this,
|
||||||
|
"channelConnectThread",
|
||||||
|
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||||
|
epicsThreadPriorityLow));
|
||||||
|
thread->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ChannelConnectThread::stop()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Lock xx(mutex);
|
||||||
|
isStop = true;
|
||||||
|
}
|
||||||
|
waitForCommand.signal();
|
||||||
|
waitForStop.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChannelConnectThread::channelConnected(
|
||||||
|
NotifyChannelRequesterPtr const ¬ifyChannelRequester)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Lock lock(mutex);
|
||||||
|
if(notifyChannelRequester->isOnQueue) return;
|
||||||
|
notifyChannelRequester->isOnQueue = true;
|
||||||
|
notifyChannelQueue.push(notifyChannelRequester);
|
||||||
|
}
|
||||||
|
waitForCommand.signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChannelConnectThread::run()
|
||||||
|
{
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
waitForCommand.wait();
|
||||||
|
while(true) {
|
||||||
|
bool more = false;
|
||||||
|
NotifyChannelRequester* notifyChannelRequester(NULL);
|
||||||
|
{
|
||||||
|
Lock lock(mutex);
|
||||||
|
if(!notifyChannelQueue.empty())
|
||||||
|
{
|
||||||
|
more = true;
|
||||||
|
NotifyChannelRequesterWPtr req(notifyChannelQueue.front());
|
||||||
|
notifyChannelQueue.pop();
|
||||||
|
NotifyChannelRequesterPtr reqPtr(req.lock());
|
||||||
|
if(reqPtr) {
|
||||||
|
notifyChannelRequester = reqPtr.get();
|
||||||
|
reqPtr->isOnQueue = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!more) break;
|
||||||
|
if(notifyChannelRequester!=NULL)
|
||||||
|
{
|
||||||
|
CAChannelPtr channel(notifyChannelRequester->channel.lock());
|
||||||
|
if(channel) channel->notifyClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(isStop) {
|
||||||
|
waitForStop.signal();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}}}
|
71
src/ca/channelConnectThread.h
Normal file
71
src/ca/channelConnectThread.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 ChannelConnectThread_H
|
||||||
|
#define ChannelConnectThread_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 NotifyChannelRequester;
|
||||||
|
typedef std::tr1::shared_ptr<NotifyChannelRequester> NotifyChannelRequesterPtr;
|
||||||
|
typedef std::tr1::weak_ptr<NotifyChannelRequester> NotifyChannelRequesterWPtr;
|
||||||
|
|
||||||
|
|
||||||
|
class ChannelConnectThread;
|
||||||
|
typedef std::tr1::shared_ptr<ChannelConnectThread> ChannelConnectThreadPtr;
|
||||||
|
|
||||||
|
class CAChannel;
|
||||||
|
typedef std::tr1::shared_ptr<CAChannel> CAChannelPtr;
|
||||||
|
typedef std::tr1::weak_ptr<CAChannel> CAChannelWPtr;
|
||||||
|
|
||||||
|
class NotifyChannelRequester
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ChannelRequester::weak_pointer channelRequester;
|
||||||
|
CAChannelWPtr channel;
|
||||||
|
bool isOnQueue;
|
||||||
|
NotifyChannelRequester() : isOnQueue(false) {}
|
||||||
|
void setChannel(CAChannelPtr const &channel)
|
||||||
|
{ this->channel = channel;}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ChannelConnectThread :
|
||||||
|
public epicsThreadRunable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static ChannelConnectThreadPtr get();
|
||||||
|
~ChannelConnectThread();
|
||||||
|
virtual void run();
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
void channelConnected(NotifyChannelRequesterPtr const ¬ifyChannelRequester);
|
||||||
|
private:
|
||||||
|
ChannelConnectThread();
|
||||||
|
|
||||||
|
bool isStop;
|
||||||
|
std::tr1::shared_ptr<epicsThread> thread;
|
||||||
|
epics::pvData::Mutex mutex;
|
||||||
|
epics::pvData::Event waitForCommand;
|
||||||
|
epics::pvData::Event waitForStop;
|
||||||
|
std::queue<NotifyChannelRequesterWPtr> notifyChannelQueue;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}}}
|
||||||
|
|
||||||
|
#endif /* ChannelConnectThread_H */
|
@ -10,6 +10,7 @@
|
|||||||
#include <alarm.h>
|
#include <alarm.h>
|
||||||
#include <alarmString.h>
|
#include <alarmString.h>
|
||||||
|
|
||||||
|
#include <pv/alarm.h>
|
||||||
#include <pv/standardField.h>
|
#include <pv/standardField.h>
|
||||||
#include <pv/logger.h>
|
#include <pv/logger.h>
|
||||||
#include <pv/pvAccess.h>
|
#include <pv/pvAccess.h>
|
||||||
@ -23,6 +24,7 @@
|
|||||||
using namespace epics::pvData;
|
using namespace epics::pvData;
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::ostringstream;
|
using std::ostringstream;
|
||||||
|
using std::cout;
|
||||||
|
|
||||||
namespace epics {
|
namespace epics {
|
||||||
namespace pvAccess {
|
namespace pvAccess {
|
||||||
@ -30,23 +32,7 @@ namespace ca {
|
|||||||
|
|
||||||
#define CA_PRIORITY 50
|
#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(
|
DbdToPvPtr DbdToPv::create(
|
||||||
CAChannelPtr const & caChannel,
|
CAChannelPtr const & caChannel,
|
||||||
@ -60,16 +46,20 @@ DbdToPvPtr DbdToPv::create(
|
|||||||
|
|
||||||
DbdToPv::DbdToPv(IOType ioType)
|
DbdToPv::DbdToPv(IOType ioType)
|
||||||
: ioType(ioType),
|
: ioType(ioType),
|
||||||
fieldRequested(false),
|
dbfIsUCHAR(false),
|
||||||
|
dbfIsUSHORT(false),
|
||||||
|
dbfIsULONG(false),
|
||||||
|
dbfIsINT64(false),
|
||||||
|
dbfIsUINT64(false),
|
||||||
|
valueRequested(false),
|
||||||
alarmRequested(false),
|
alarmRequested(false),
|
||||||
timeStampRequested(false),
|
timeStampRequested(false),
|
||||||
displayRequested(false),
|
displayRequested(false),
|
||||||
controlRequested(false),
|
controlRequested(false),
|
||||||
valueAlarmRequested(false),
|
valueAlarmRequested(false),
|
||||||
isArray(false),
|
isArray(false),
|
||||||
|
charArrayIsString(false),
|
||||||
firstTime(true),
|
firstTime(true),
|
||||||
choicesValid(false),
|
|
||||||
waitForChoicesValid(false),
|
|
||||||
caValueType(-1),
|
caValueType(-1),
|
||||||
caRequestType(-1),
|
caRequestType(-1),
|
||||||
maxElements(0)
|
maxElements(0)
|
||||||
@ -95,15 +85,56 @@ static chtype getDbrType(const ScalarType scalarType)
|
|||||||
{
|
{
|
||||||
case pvString : return DBR_STRING;
|
case pvString : return DBR_STRING;
|
||||||
case pvByte : return DBR_CHAR;
|
case pvByte : return DBR_CHAR;
|
||||||
|
case pvUByte : return DBR_CHAR;
|
||||||
case pvShort : return DBR_SHORT;
|
case pvShort : return DBR_SHORT;
|
||||||
|
case pvUShort : return DBR_SHORT;
|
||||||
case pvInt : return DBR_LONG;
|
case pvInt : return DBR_LONG;
|
||||||
|
case pvUInt : return DBR_LONG;
|
||||||
case pvFloat : return DBR_FLOAT;
|
case pvFloat : return DBR_FLOAT;
|
||||||
case pvDouble : return DBR_DOUBLE;
|
case pvDouble : return DBR_DOUBLE;
|
||||||
|
case pvLong : return DBR_DOUBLE;
|
||||||
|
case pvULong : return DBR_DOUBLE;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
throw std::runtime_error("getDbr: illegal scalarType");
|
throw std::runtime_error("getDbr: illegal scalarType");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static dbr_short_t convertDBstatus(dbr_short_t dbStatus)
|
||||||
|
{
|
||||||
|
switch(dbStatus) {
|
||||||
|
case NO_ALARM:
|
||||||
|
return noStatus;
|
||||||
|
case READ_ALARM:
|
||||||
|
case WRITE_ALARM:
|
||||||
|
case HIHI_ALARM:
|
||||||
|
case HIGH_ALARM:
|
||||||
|
case LOLO_ALARM:
|
||||||
|
case LOW_ALARM:
|
||||||
|
case STATE_ALARM:
|
||||||
|
case COS_ALARM:
|
||||||
|
case HW_LIMIT_ALARM:
|
||||||
|
return deviceStatus;
|
||||||
|
case COMM_ALARM:
|
||||||
|
case TIMEOUT_ALARM:
|
||||||
|
return driverStatus;
|
||||||
|
case CALC_ALARM:
|
||||||
|
case SCAN_ALARM:
|
||||||
|
case LINK_ALARM:
|
||||||
|
case SOFT_ALARM:
|
||||||
|
case BAD_SUB_ALARM:
|
||||||
|
return recordStatus;
|
||||||
|
case DISABLE_ALARM:
|
||||||
|
case SIMM_ALARM:
|
||||||
|
case READ_ACCESS_ALARM:
|
||||||
|
case WRITE_ACCESS_ALARM:
|
||||||
|
return dbStatus;
|
||||||
|
case UDF_ALARM:
|
||||||
|
return undefinedStatus;
|
||||||
|
default:
|
||||||
|
return undefinedStatus; // UNDEFINED
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void DbdToPv::activate(
|
void DbdToPv::activate(
|
||||||
CAChannelPtr const & caChannel,
|
CAChannelPtr const & caChannel,
|
||||||
@ -131,14 +162,14 @@ void DbdToPv::activate(
|
|||||||
}
|
}
|
||||||
if(fieldPVStructure->getPVFields().size()==0)
|
if(fieldPVStructure->getPVFields().size()==0)
|
||||||
{
|
{
|
||||||
fieldRequested = true;
|
valueRequested = true;
|
||||||
alarmRequested = true;
|
alarmRequested = true;
|
||||||
timeStampRequested = true;
|
timeStampRequested = true;
|
||||||
displayRequested = true;
|
displayRequested = true;
|
||||||
controlRequested = true;
|
controlRequested = true;
|
||||||
valueAlarmRequested = true;
|
valueAlarmRequested = true;
|
||||||
} else {
|
} else {
|
||||||
if(fieldPVStructure->getSubField("value")) fieldRequested = true;
|
if(fieldPVStructure->getSubField("value")) valueRequested = true;
|
||||||
if(fieldPVStructure->getSubField("alarm")) alarmRequested = true;
|
if(fieldPVStructure->getSubField("alarm")) alarmRequested = true;
|
||||||
if(fieldPVStructure->getSubField("timeStamp")) timeStampRequested = true;
|
if(fieldPVStructure->getSubField("timeStamp")) timeStampRequested = true;
|
||||||
if(fieldPVStructure->getSubField("display")) displayRequested = true;
|
if(fieldPVStructure->getSubField("display")) displayRequested = true;
|
||||||
@ -173,39 +204,75 @@ void DbdToPv::activate(
|
|||||||
}
|
}
|
||||||
caRequestType = (properties.size()==0 ? DBR_ENUM : DBR_TIME_ENUM);
|
caRequestType = (properties.size()==0 ? DBR_ENUM : DBR_TIME_ENUM);
|
||||||
structure = standardField->enumerated(properties);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
ScalarType st = dbr2ST[channelType];
|
||||||
|
PVStringPtr pvValue = fieldPVStructure->getSubField<PVString>("value._options.dbtype");
|
||||||
|
if(pvValue)
|
||||||
|
{
|
||||||
|
std::string value(pvValue->get());
|
||||||
|
if(value.find("DBF_UCHAR")!=std::string::npos) {
|
||||||
|
if(st==pvByte) {
|
||||||
|
dbfIsUCHAR = true;
|
||||||
|
st = pvUByte;
|
||||||
|
caValueType = DBR_CHAR;
|
||||||
|
}
|
||||||
|
} else if(value.find("DBF_USHORT")!=std::string::npos) {
|
||||||
|
if(st==pvInt) {
|
||||||
|
dbfIsUSHORT = true;
|
||||||
|
st = pvUShort;
|
||||||
|
caValueType = DBR_SHORT;
|
||||||
|
}
|
||||||
|
} else if(value.find("DBF_ULONG")!=std::string::npos) {
|
||||||
|
if(st==pvDouble) {
|
||||||
|
dbfIsULONG = true;
|
||||||
|
st = pvUInt;
|
||||||
|
caValueType = DBR_LONG;
|
||||||
|
}
|
||||||
|
} else if(value.find("DBF_INT64")!=std::string::npos) {
|
||||||
|
if(st==pvDouble) {
|
||||||
|
dbfIsINT64 = true;
|
||||||
|
st = pvLong;
|
||||||
|
}
|
||||||
|
} else if(value.find("DBF_UINT64")!=std::string::npos) {
|
||||||
|
if(st==pvDouble) {
|
||||||
|
dbfIsUINT64 = true;
|
||||||
|
st = pvULong;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if(st==pvString) {
|
||||||
|
displayRequested = false;
|
||||||
|
controlRequested = false;
|
||||||
|
valueAlarmRequested = false;
|
||||||
|
}
|
||||||
maxElements = ca_element_count(channelID);
|
maxElements = ca_element_count(channelID);
|
||||||
if(maxElements!=1) isArray = true;
|
if(maxElements!=1) isArray = true;
|
||||||
if(isArray)
|
if(isArray)
|
||||||
{
|
{
|
||||||
controlRequested = false;
|
controlRequested = false;
|
||||||
valueAlarmRequested = false;
|
valueAlarmRequested = false;
|
||||||
|
if(channelType==DBR_CHAR && fieldPVStructure)
|
||||||
|
{
|
||||||
|
PVStringPtr pvValue = fieldPVStructure->getSubField<PVString>("value._options.pvtype");
|
||||||
|
if(pvValue) {
|
||||||
|
std::string value(pvValue->get());
|
||||||
|
if(value.find("pvString")!=std::string::npos) {
|
||||||
|
charArrayIsString = true;
|
||||||
|
st = pvString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ScalarType st = dbr2ST[channelType];
|
|
||||||
if(st==pvString) {
|
|
||||||
displayRequested = false;
|
|
||||||
controlRequested = false;
|
|
||||||
valueAlarmRequested = false;
|
|
||||||
}
|
|
||||||
if(controlRequested || displayRequested || valueAlarmRequested) timeStampRequested = false;
|
if(controlRequested || displayRequested || valueAlarmRequested) timeStampRequested = false;
|
||||||
FieldCreatePtr fieldCreate(FieldCreate::getFieldCreate());
|
FieldCreatePtr fieldCreate(FieldCreate::getFieldCreate());
|
||||||
PVDataCreatePtr pvDataCreate(PVDataCreate::getPVDataCreate());
|
PVDataCreatePtr pvDataCreate(PVDataCreate::getPVDataCreate());
|
||||||
FieldBuilderPtr fieldBuilder(fieldCreate->createFieldBuilder());
|
FieldBuilderPtr fieldBuilder(fieldCreate->createFieldBuilder());
|
||||||
if(fieldRequested) {
|
if(valueRequested) {
|
||||||
if(isArray) {
|
if(isArray && !charArrayIsString) {
|
||||||
fieldBuilder->addArray("value",st);
|
fieldBuilder->addArray("value",st);
|
||||||
} else {
|
} else {
|
||||||
fieldBuilder->add("value",st);
|
fieldBuilder->add("value",st);
|
||||||
@ -219,14 +286,19 @@ void DbdToPv::activate(
|
|||||||
switch(st)
|
switch(st)
|
||||||
{
|
{
|
||||||
case pvByte:
|
case pvByte:
|
||||||
|
case pvUByte:
|
||||||
fieldBuilder->add("valueAlarm",standardField->byteAlarm()); break;
|
fieldBuilder->add("valueAlarm",standardField->byteAlarm()); break;
|
||||||
case pvShort:
|
case pvShort:
|
||||||
|
case pvUShort:
|
||||||
fieldBuilder->add("valueAlarm",standardField->shortAlarm()); break;
|
fieldBuilder->add("valueAlarm",standardField->shortAlarm()); break;
|
||||||
case pvInt:
|
case pvInt:
|
||||||
|
case pvUInt:
|
||||||
fieldBuilder->add("valueAlarm",standardField->intAlarm()); break;
|
fieldBuilder->add("valueAlarm",standardField->intAlarm()); break;
|
||||||
case pvFloat:
|
case pvFloat:
|
||||||
fieldBuilder->add("valueAlarm",standardField->floatAlarm()); break;
|
fieldBuilder->add("valueAlarm",standardField->floatAlarm()); break;
|
||||||
case pvDouble:
|
case pvDouble:
|
||||||
|
case pvLong:
|
||||||
|
case pvULong:
|
||||||
fieldBuilder->add("valueAlarm",standardField->doubleAlarm()); break;
|
fieldBuilder->add("valueAlarm",standardField->doubleAlarm()); break;
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("DbDToPv::activate: bad type");
|
throw std::runtime_error("DbDToPv::activate: bad type");
|
||||||
@ -242,38 +314,27 @@ void DbdToPv::activate(
|
|||||||
} else {
|
} else {
|
||||||
caRequestType = dbf_type_to_DBR(caValueType);
|
caRequestType = dbf_type_to_DBR(caValueType);
|
||||||
}
|
}
|
||||||
if(displayRequested) {
|
|
||||||
chid channelID;
|
}
|
||||||
string name(caChannel->getChannelName() + ".DESC");
|
|
||||||
int result = ca_create_channel(name.c_str(),
|
chtype DbdToPv::getRequestType()
|
||||||
description_connection_handler,
|
{
|
||||||
this,
|
if(caRequestType<0) {
|
||||||
CA_PRIORITY, // TODO mapping
|
throw std::runtime_error("DbDToPv::getRequestType: bad type");
|
||||||
&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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return caRequestType;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DbdToPv::descriptionConnected(struct connection_handler_args args)
|
Structure::const_shared_pointer DbdToPv::getStructure()
|
||||||
{
|
{
|
||||||
if (args.op != CA_OP_CONN_UP) return;
|
return structure;
|
||||||
ca_array_get_callback(DBR_STRING,
|
|
||||||
0,
|
|
||||||
args.chid, descriptionHandler, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DbdToPv::getDescriptionDone(struct event_handler_args &args)
|
|
||||||
|
static void enumChoicesHandler(struct event_handler_args args)
|
||||||
{
|
{
|
||||||
if(args.status!=ECA_NORMAL) return;
|
DbdToPv *dbdToPv = static_cast<DbdToPv*>(args.usr);
|
||||||
const dbr_string_t *value = static_cast<const dbr_string_t *>(dbr_value_ptr(args.dbr,DBR_STRING));
|
dbdToPv->getChoicesDone(args);
|
||||||
description = string(*value);
|
|
||||||
ca_clear_channel(args.chid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DbdToPv::getChoicesDone(struct event_handler_args &args)
|
void DbdToPv::getChoicesDone(struct event_handler_args &args)
|
||||||
@ -288,21 +349,29 @@ void DbdToPv::getChoicesDone(struct event_handler_args &args)
|
|||||||
size_t num = dbr_enum_p->no_str;
|
size_t num = dbr_enum_p->no_str;
|
||||||
choices.reserve(num);
|
choices.reserve(num);
|
||||||
for(size_t i=0; i<num; ++i) choices.push_back(string(&dbr_enum_p->strs[i][0]));
|
for(size_t i=0; i<num; ++i) choices.push_back(string(&dbr_enum_p->strs[i][0]));
|
||||||
bool signal = false;
|
choicesEvent.signal();
|
||||||
{
|
|
||||||
Lock lock(choicesMutex);
|
|
||||||
choicesValid = true;
|
|
||||||
if(waitForChoicesValid) signal = true;
|
|
||||||
}
|
|
||||||
if(signal) choicesEvent.signal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chtype DbdToPv::getRequestType()
|
|
||||||
|
void DbdToPv::getChoices(CAChannelPtr const & caChannel)
|
||||||
{
|
{
|
||||||
if(caRequestType<0) {
|
if(caRequestType==DBR_ENUM||caRequestType==DBR_TIME_ENUM)
|
||||||
throw std::runtime_error("DbDToPv::getRequestType: bad type");
|
{
|
||||||
|
caChannel->attachContext();
|
||||||
|
chid channelID = caChannel->getChannelID();
|
||||||
|
int result = ca_array_get_callback(DBR_GR_ENUM,
|
||||||
|
1,
|
||||||
|
channelID, enumChoicesHandler, this);
|
||||||
|
if (result == ECA_NORMAL) {
|
||||||
|
result = ca_flush_io();
|
||||||
|
choicesEvent.wait();
|
||||||
|
} else {
|
||||||
|
string mess(caChannel->getChannelName());
|
||||||
|
mess += " DbdToPv::activate getting enum cnoices ";
|
||||||
|
mess += ca_message(result);
|
||||||
|
throw std::runtime_error(mess);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return caRequestType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PVStructurePtr DbdToPv::createPVStructure()
|
PVStructurePtr DbdToPv::createPVStructure()
|
||||||
@ -368,7 +437,7 @@ Status DbdToPv::getFromDBD(
|
|||||||
Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(args.status)));
|
Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(args.status)));
|
||||||
return errorStatus;
|
return errorStatus;
|
||||||
}
|
}
|
||||||
if(fieldRequested)
|
if(valueRequested)
|
||||||
{
|
{
|
||||||
void * value = dbr_value_ptr(args.dbr,caRequestType);
|
void * value = dbr_value_ptr(args.dbr,caRequestType);
|
||||||
if(isArray) {
|
if(isArray) {
|
||||||
@ -386,18 +455,51 @@ Status DbdToPv::getFromDBD(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DBR_CHAR:
|
case DBR_CHAR:
|
||||||
|
if(charArrayIsString)
|
||||||
|
{
|
||||||
|
const char * pchar = static_cast<const char *>(value);
|
||||||
|
std::string str(pchar);
|
||||||
|
PVStringPtr pvValue(pvStructure->getSubField<PVString>("value"));
|
||||||
|
pvValue->put(str);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(dbfIsUCHAR)
|
||||||
|
{
|
||||||
|
copy_DBRScalarArray<dbr_char_t,PVUByteArray>(value,count,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
copy_DBRScalarArray<dbr_char_t,PVByteArray>(value,count,pvValue);
|
copy_DBRScalarArray<dbr_char_t,PVByteArray>(value,count,pvValue);
|
||||||
break;
|
break;
|
||||||
case DBR_SHORT:
|
case DBR_SHORT:
|
||||||
|
if(dbfIsUSHORT)
|
||||||
|
{
|
||||||
|
copy_DBRScalarArray<dbr_short_t,PVUShortArray>(value,count,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
copy_DBRScalarArray<dbr_short_t,PVShortArray>(value,count,pvValue);
|
copy_DBRScalarArray<dbr_short_t,PVShortArray>(value,count,pvValue);
|
||||||
break;
|
break;
|
||||||
case DBR_LONG:
|
case DBR_LONG:
|
||||||
|
if(dbfIsULONG)
|
||||||
|
{
|
||||||
|
copy_DBRScalarArray<dbr_long_t,PVUIntArray>(value,count,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
copy_DBRScalarArray<dbr_long_t,PVIntArray>(value,count,pvValue);
|
copy_DBRScalarArray<dbr_long_t,PVIntArray>(value,count,pvValue);
|
||||||
break;
|
break;
|
||||||
case DBR_FLOAT:
|
case DBR_FLOAT:
|
||||||
copy_DBRScalarArray<dbr_float_t,PVFloatArray>(value,count,pvValue);
|
copy_DBRScalarArray<dbr_float_t,PVFloatArray>(value,count,pvValue);
|
||||||
break;
|
break;
|
||||||
case DBR_DOUBLE:
|
case DBR_DOUBLE:
|
||||||
|
if(dbfIsINT64)
|
||||||
|
{
|
||||||
|
copy_DBRScalarArray<dbr_double_t,PVLongArray>(value,count,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(dbfIsUINT64)
|
||||||
|
{
|
||||||
|
copy_DBRScalarArray<dbr_double_t,PVULongArray>(value,count,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
copy_DBRScalarArray<dbr_double_t,PVDoubleArray>(value,count,pvValue);
|
copy_DBRScalarArray<dbr_double_t,PVDoubleArray>(value,count,pvValue);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -428,11 +530,40 @@ Status DbdToPv::getFromDBD(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DBR_STRING: copy_DBRScalar<dbr_string_t,PVString>(value,pvValue); 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_CHAR:
|
||||||
case DBR_SHORT: copy_DBRScalar<dbr_short_t,PVShort>(value,pvValue); break;
|
if(dbfIsUCHAR)
|
||||||
case DBR_LONG: copy_DBRScalar<dbr_long_t,PVInt>(value,pvValue); break;
|
{
|
||||||
|
copy_DBRScalar<dbr_char_t,PVUByte>(value,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
copy_DBRScalar<dbr_char_t,PVByte>(value,pvValue); break;
|
||||||
|
case DBR_SHORT:
|
||||||
|
if(dbfIsUSHORT)
|
||||||
|
{
|
||||||
|
copy_DBRScalar<dbr_short_t,PVUShort>(value,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
copy_DBRScalar<dbr_short_t,PVShort>(value,pvValue); break;
|
||||||
|
case DBR_LONG:
|
||||||
|
if(dbfIsULONG)
|
||||||
|
{
|
||||||
|
copy_DBRScalar<dbr_long_t,PVUInt>(value,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
copy_DBRScalar<dbr_long_t,PVInt>(value,pvValue); break;
|
||||||
case DBR_FLOAT: copy_DBRScalar<dbr_float_t,PVFloat>(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;
|
case DBR_DOUBLE:
|
||||||
|
if(dbfIsINT64)
|
||||||
|
{
|
||||||
|
copy_DBRScalar<dbr_double_t,PVLong>(value,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(dbfIsUINT64)
|
||||||
|
{
|
||||||
|
copy_DBRScalar<dbr_double_t,PVULong>(value,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
copy_DBRScalar<dbr_double_t,PVDouble>(value,pvValue); break;
|
||||||
default:
|
default:
|
||||||
Status errorStatus(
|
Status errorStatus(
|
||||||
Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error"));
|
Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error"));
|
||||||
@ -461,7 +592,7 @@ Status DbdToPv::getFromDBD(
|
|||||||
PVIntPtr pvStatus(pvAlarm->getSubField<PVInt>("status"));
|
PVIntPtr pvStatus(pvAlarm->getSubField<PVInt>("status"));
|
||||||
if(caAlarm.status!=status) {
|
if(caAlarm.status!=status) {
|
||||||
caAlarm.status = status;
|
caAlarm.status = status;
|
||||||
pvStatus->put(status);
|
pvStatus->put(convertDBstatus(status));
|
||||||
string message("UNKNOWN STATUS");
|
string message("UNKNOWN STATUS");
|
||||||
if(status<=ALARM_NSTATUS) message = string(epicsAlarmConditionStrings[status]);
|
if(status<=ALARM_NSTATUS) message = string(epicsAlarmConditionStrings[status]);
|
||||||
pvMessage->put(message);
|
pvMessage->put(message);
|
||||||
@ -488,7 +619,7 @@ Status DbdToPv::getFromDBD(
|
|||||||
bitSet->set(pvSeconds->getFieldOffset());
|
bitSet->set(pvSeconds->getFieldOffset());
|
||||||
}
|
}
|
||||||
if(caTimeStamp.nsec!=stamp.nsec) {
|
if(caTimeStamp.nsec!=stamp.nsec) {
|
||||||
caTimeStamp.secPastEpoch = stamp.secPastEpoch;
|
caTimeStamp.nsec = stamp.nsec;
|
||||||
PVIntPtr pvNano(pvTimeStamp->getSubField<PVInt>("nanoseconds"));
|
PVIntPtr pvNano(pvTimeStamp->getSubField<PVInt>("nanoseconds"));
|
||||||
pvNano->put(stamp.nsec);
|
pvNano->put(stamp.nsec);
|
||||||
bitSet->set(pvNano->getFieldOffset());
|
bitSet->set(pvNano->getFieldOffset());
|
||||||
@ -590,14 +721,6 @@ Status DbdToPv::getFromDBD(
|
|||||||
pvString->put(format);
|
pvString->put(format);
|
||||||
bitSet->set(pvString->getFieldOffset());
|
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) {
|
if(valueAlarmRequested) {
|
||||||
double upper_alarm_limit = 0.0;
|
double upper_alarm_limit = 0.0;
|
||||||
@ -668,6 +791,8 @@ Status DbdToPv::getFromDBD(
|
|||||||
return Status::Ok;
|
return Status::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template<typename dbrT, typename pvT>
|
template<typename dbrT, typename pvT>
|
||||||
const void * put_DBRScalar(dbrT *val,PVScalar::shared_pointer const & pvScalar)
|
const void * put_DBRScalar(dbrT *val,PVScalar::shared_pointer const & pvScalar)
|
||||||
{
|
{
|
||||||
@ -726,18 +851,63 @@ Status DbdToPv::putToDBD(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DBR_CHAR:
|
case DBR_CHAR:
|
||||||
|
if(charArrayIsString)
|
||||||
|
{
|
||||||
|
PVStringPtr pvValue(pvStructure->getSubField<PVString>("value"));
|
||||||
|
const char * pchar = pvValue->get().c_str();
|
||||||
|
pValue = pchar;
|
||||||
|
count = pvValue->get().length();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(dbfIsUCHAR)
|
||||||
|
{
|
||||||
|
pValue = put_DBRScalarArray<dbr_char_t,PVUByteArray>(&count,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
pValue = put_DBRScalarArray<dbr_char_t,PVByteArray>(&count,pvValue);
|
pValue = put_DBRScalarArray<dbr_char_t,PVByteArray>(&count,pvValue);
|
||||||
break;
|
break;
|
||||||
case DBR_SHORT:
|
case DBR_SHORT:
|
||||||
|
if(dbfIsUSHORT)
|
||||||
|
{
|
||||||
|
pValue = put_DBRScalarArray<dbr_short_t,PVUShortArray>(&count,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
pValue = put_DBRScalarArray<dbr_short_t,PVShortArray>(&count,pvValue);
|
pValue = put_DBRScalarArray<dbr_short_t,PVShortArray>(&count,pvValue);
|
||||||
break;
|
break;
|
||||||
case DBR_LONG:
|
case DBR_LONG:
|
||||||
|
if(dbfIsULONG)
|
||||||
|
{
|
||||||
|
pValue = put_DBRScalarArray<dbr_long_t,PVUIntArray>(&count,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
pValue = put_DBRScalarArray<dbr_long_t,PVIntArray>(&count,pvValue);
|
pValue = put_DBRScalarArray<dbr_long_t,PVIntArray>(&count,pvValue);
|
||||||
break;
|
break;
|
||||||
case DBR_FLOAT:
|
case DBR_FLOAT:
|
||||||
pValue = put_DBRScalarArray<dbr_float_t,PVFloatArray>(&count,pvValue);
|
pValue = put_DBRScalarArray<dbr_float_t,PVFloatArray>(&count,pvValue);
|
||||||
break;
|
break;
|
||||||
case DBR_DOUBLE:
|
case DBR_DOUBLE:
|
||||||
|
if(dbfIsINT64)
|
||||||
|
{
|
||||||
|
PVLongArrayPtr pvValue(pvStructure->getSubField<PVLongArray>("value"));
|
||||||
|
PVLongArray::const_svector sv(pvValue->view());
|
||||||
|
pvDoubleArray = PVDoubleArrayPtr(getPVDataCreate()->createPVScalarArray<PVDoubleArray>());
|
||||||
|
pvDoubleArray->putFrom(sv);
|
||||||
|
const double * pdouble = pvDoubleArray->view().data();
|
||||||
|
count = pvValue->getLength();
|
||||||
|
pValue = pdouble;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(dbfIsUINT64)
|
||||||
|
{
|
||||||
|
PVULongArrayPtr pvValue(pvStructure->getSubField<PVULongArray>("value"));
|
||||||
|
PVULongArray::const_svector sv(pvValue->view());
|
||||||
|
pvDoubleArray = PVDoubleArrayPtr(getPVDataCreate()->createPVScalarArray<PVDoubleArray>());
|
||||||
|
pvDoubleArray->putFrom(sv);
|
||||||
|
const double * pdouble = pvDoubleArray->view().data();
|
||||||
|
count = pvValue->getLength();
|
||||||
|
pValue = pdouble;
|
||||||
|
break;
|
||||||
|
}
|
||||||
pValue = put_DBRScalarArray<dbr_double_t,PVDoubleArray>(&count,pvValue);
|
pValue = put_DBRScalarArray<dbr_double_t,PVDoubleArray>(&count,pvValue);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -750,36 +920,48 @@ Status DbdToPv::putToDBD(
|
|||||||
switch(caValueType) {
|
switch(caValueType) {
|
||||||
case DBR_ENUM:
|
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();
|
dbr_enum_t indexvalue = pvStructure->getSubField<PVInt>("value.index")->get();
|
||||||
pValue = &indexvalue;
|
pValue = &indexvalue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DBR_STRING: pValue = pvStructure->getSubField<PVString>("value")->get().c_str(); 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_CHAR:
|
||||||
case DBR_SHORT: pValue = put_DBRScalar<dbr_short_t,PVShort>(&svalue,pvValue); break;
|
if(dbfIsUCHAR)
|
||||||
case DBR_LONG: pValue = put_DBRScalar<dbr_long_t,PVInt>(&lvalue,pvValue); break;
|
{
|
||||||
|
pValue = put_DBRScalar<dbr_char_t,PVUByte>(&bvalue,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pValue = put_DBRScalar<dbr_char_t,PVByte>(&bvalue,pvValue); break;
|
||||||
|
case DBR_SHORT:
|
||||||
|
if(dbfIsUSHORT)
|
||||||
|
{
|
||||||
|
pValue = put_DBRScalar<dbr_short_t,PVUShort>(&svalue,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pValue = put_DBRScalar<dbr_short_t,PVShort>(&svalue,pvValue); break;
|
||||||
|
case DBR_LONG:
|
||||||
|
if(dbfIsULONG)
|
||||||
|
{
|
||||||
|
pValue = put_DBRScalar<dbr_long_t,PVUInt>(&lvalue,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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_FLOAT: pValue = put_DBRScalar<dbr_float_t,PVFloat>(&fvalue,pvValue); break;
|
||||||
case DBR_DOUBLE: pValue = put_DBRScalar<dbr_double_t,PVDouble>(&dvalue,pvValue); break;
|
case DBR_DOUBLE:
|
||||||
|
if(dbfIsINT64)
|
||||||
|
{
|
||||||
|
pValue = put_DBRScalar<dbr_double_t,PVLong>(&dvalue,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(dbfIsUINT64)
|
||||||
|
{
|
||||||
|
pValue = put_DBRScalar<dbr_double_t,PVULong>(&dvalue,pvValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pValue = put_DBRScalar<dbr_double_t,PVDouble>(&dvalue,pvValue); break;
|
||||||
default:
|
default:
|
||||||
Status errorStatus(
|
Status errorStatus(
|
||||||
Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error"));
|
Status::STATUSTYPE_ERROR, string("DbdToPv::putToDBD logic error"));
|
||||||
return errorStatus;
|
return errorStatus;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,6 +89,8 @@ public:
|
|||||||
epics::pvData::PVStructurePtr const & pvRequest,
|
epics::pvData::PVStructurePtr const & pvRequest,
|
||||||
IOType ioType
|
IOType ioType
|
||||||
);
|
);
|
||||||
|
epics::pvData::Structure::const_shared_pointer getStructure();
|
||||||
|
void getChoices(CAChannelPtr const & caChannel);
|
||||||
epics::pvData::PVStructurePtr createPVStructure();
|
epics::pvData::PVStructurePtr createPVStructure();
|
||||||
chtype getRequestType();
|
chtype getRequestType();
|
||||||
epics::pvData::Status getFromDBD(
|
epics::pvData::Status getFromDBD(
|
||||||
@ -104,8 +106,6 @@ public:
|
|||||||
void *userArg
|
void *userArg
|
||||||
);
|
);
|
||||||
void getChoicesDone(struct event_handler_args &args);
|
void getChoicesDone(struct event_handler_args &args);
|
||||||
void descriptionConnected(struct connection_handler_args args);
|
|
||||||
void getDescriptionDone(struct event_handler_args &args);
|
|
||||||
private:
|
private:
|
||||||
DbdToPv(IOType ioType);
|
DbdToPv(IOType ioType);
|
||||||
void activate(
|
void activate(
|
||||||
@ -113,29 +113,32 @@ private:
|
|||||||
epics::pvData::PVStructurePtr const & pvRequest
|
epics::pvData::PVStructurePtr const & pvRequest
|
||||||
);
|
);
|
||||||
IOType ioType;
|
IOType ioType;
|
||||||
bool fieldRequested;
|
bool dbfIsUCHAR;
|
||||||
|
bool dbfIsUSHORT;
|
||||||
|
bool dbfIsULONG;
|
||||||
|
bool dbfIsINT64;
|
||||||
|
bool dbfIsUINT64;
|
||||||
|
bool valueRequested;
|
||||||
bool alarmRequested;
|
bool alarmRequested;
|
||||||
bool timeStampRequested;
|
bool timeStampRequested;
|
||||||
bool displayRequested;
|
bool displayRequested;
|
||||||
bool controlRequested;
|
bool controlRequested;
|
||||||
bool valueAlarmRequested;
|
bool valueAlarmRequested;
|
||||||
bool isArray;
|
bool isArray;
|
||||||
|
bool charArrayIsString;
|
||||||
bool firstTime;
|
bool firstTime;
|
||||||
bool choicesValid;
|
|
||||||
bool waitForChoicesValid;
|
|
||||||
chtype caValueType;
|
chtype caValueType;
|
||||||
chtype caRequestType;
|
chtype caRequestType;
|
||||||
unsigned long maxElements;
|
unsigned long maxElements;
|
||||||
epics::pvData::Mutex choicesMutex;
|
|
||||||
epics::pvData::Event choicesEvent;
|
epics::pvData::Event choicesEvent;
|
||||||
epicsTimeStamp caTimeStamp;
|
epicsTimeStamp caTimeStamp;
|
||||||
CaAlarm caAlarm;
|
CaAlarm caAlarm;
|
||||||
CaDisplay caDisplay;
|
CaDisplay caDisplay;
|
||||||
CaControl caControl;
|
CaControl caControl;
|
||||||
CaValueAlarm caValueAlarm;
|
CaValueAlarm caValueAlarm;
|
||||||
std::string description;
|
|
||||||
epics::pvData::Structure::const_shared_pointer structure;
|
epics::pvData::Structure::const_shared_pointer structure;
|
||||||
std::vector<std::string> choices;
|
std::vector<std::string> choices;
|
||||||
|
epics::pvData::PVDoubleArrayPtr pvDoubleArray; //for dbfIsINT64 and dbfIsUINT64
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user