diff --git a/caProvider.md b/caProvider.md
index 6a479aa..29a285e 100644
--- a/caProvider.md
+++ b/caProvider.md
@@ -1,16 +1,20 @@
# pvAccessCPP: ca provider
-2018.07.09
+2018.10.05
Editors:
* 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,
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**,
But this only works if the IOC has **qsrv** installed.
**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 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)
-
-
-
-## 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
- 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.
-
-## 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
-```
-
-
-
-## 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**.
-
-## 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.
-
-
-
diff --git a/documentation/release_notes.dox b/documentation/release_notes.dox
index ab3e4f7..6683420 100644
--- a/documentation/release_notes.dox
+++ b/documentation/release_notes.dox
@@ -24,6 +24,7 @@ Release 6.1.0 (UNRELEASED)
- PVA Server now handles pipeline=true automatically for all ChannelProviders.
It is only necessary to implement epics::pvAccess::Monitor::reportRemoteQueueStatus()
to receive notification of initial window size.
+ - Most locking issues in the 'ca' provider should now be resolved.
- Additions
- pvput to NTEnum via. string now supported
- 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 epics::pvAccess::MonitorFIFO utility implementation of epics::pvAccess::Monitor
- 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)
========================
diff --git a/src/ca/Makefile b/src/ca/Makefile
index 93e6b27..6bed7c3 100644
--- a/src/ca/Makefile
+++ b/src/ca/Makefile
@@ -11,6 +11,7 @@ LIB_SYS_LIBS_WIN32 += ws2_32
INC += pv/caProvider.h
+pvAccessCA_SRCS += channelConnectThread.cpp
pvAccessCA_SRCS += monitorEventThread.cpp
pvAccessCA_SRCS += getDoneThread.cpp
pvAccessCA_SRCS += putDoneThread.cpp
diff --git a/src/ca/caChannel.cpp b/src/ca/caChannel.cpp
index f1f4e64..6a1cf81 100644
--- a/src/ca/caChannel.cpp
+++ b/src/ca/caChannel.cpp
@@ -10,6 +10,7 @@
#include
#include
#include
+#include "channelConnectThread.h"
#include "monitorEventThread.h"
#include "getDoneThread.h"
#include "putDoneThread.h"
@@ -51,16 +52,43 @@ static void ca_connection_handler(struct connection_handler_args args)
CAChannel *channel = static_cast(ca_puser(args.chid));
if (args.op == CA_OP_CONN_UP) {
- channel->connected();
+ channel->connect(true);
} 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) {
- 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()) {
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,
CAChannelProvider::shared_pointer const & channelProvider,
@@ -107,7 +123,9 @@ CAChannel::CAChannel(std::string const & channelName,
channelProvider(channelProvider),
channelRequester(channelRequester),
channelID(0),
- channelCreated(false)
+ channelCreated(false),
+ channelConnected(false),
+ channelConnectThread(ChannelConnectThread::get())
{
if(DEBUG_LEVEL>0) {
cout<< "CAChannel::CAChannel " << channelName << endl;
@@ -116,11 +134,13 @@ CAChannel::CAChannel(std::string const & channelName,
void CAChannel::activate(short priority)
{
+ ChannelRequester::shared_pointer req(channelRequester.lock());
+ if(!req) return;
if(DEBUG_LEVEL>0) {
cout<< "CAChannel::activate " << channelName << endl;
}
- ChannelRequester::shared_pointer req(channelRequester.lock());
- if(!req) return;
+ notifyChannelRequester = NotifyChannelRequesterPtr(new NotifyChannelRequester());
+ notifyChannelRequester->setChannel(shared_from_this());
attachContext();
int result = ca_create_channel(channelName.c_str(),
ca_connection_handler,
@@ -382,8 +402,7 @@ void CAChannelGetField::callRequester(CAChannelPtr const & caChannel)
if(!requester) return;
PVStructurePtr pvRequest(createRequest(""));
DbdToPvPtr dbdToPv = DbdToPv::create(caChannel,pvRequest,getIO);
- PVStructurePtr pvStructure = dbdToPv->createPVStructure();
- Structure::const_shared_pointer structure(pvStructure->getStructure());
+ Structure::const_shared_pointer structure(dbdToPv->getStructure());
Field::const_shared_pointer field =
subField.empty() ?
std::tr1::static_pointer_cast(structure) :
@@ -451,6 +470,7 @@ void CAChannelGet::activate()
std::cout << "CAChannelGet::activate " << channel->getChannelName() << endl;
}
dbdToPv = DbdToPv::create(channel,pvRequest,getIO);
+ dbdToPv->getChoices(channel);
pvStructure = dbdToPv->createPVStructure();
bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields()));
notifyGetRequester = NotifyGetRequesterPtr(new NotifyGetRequester());
@@ -488,6 +508,9 @@ void CAChannelGet::getDone(struct event_handler_args &args)
void CAChannelGet::notifyClient()
{
+ if(DEBUG_LEVEL>1) {
+ std::cout << "CAChannelGet::notifyClient " << channel->getChannelName() << endl;
+ }
ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock());
if(!getRequester) return;
EXCEPTION_GUARD(getRequester->getDone(getStatus, shared_from_this(), pvStructure, bitSet));
@@ -572,6 +595,7 @@ void CAChannelPut::activate()
cout << "CAChannelPut::activate " << channel->getChannelName() << endl;
}
dbdToPv = DbdToPv::create(channel,pvRequest,putIO);
+ dbdToPv->getChoices(channel);
pvStructure = dbdToPv->createPVStructure();
bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields()));
PVStringPtr pvString = pvRequest->getSubField("record._options.block");
@@ -809,7 +833,8 @@ CAChannelMonitor::CAChannelMonitor(
pvRequest(pvRequest),
isStarted(false),
monitorEventThread(MonitorEventThread::get()),
- pevid(NULL)
+ pevid(NULL),
+ eventMask(DBE_VALUE | DBE_ALARM)
{}
CAChannelMonitor::~CAChannelMonitor()
@@ -831,6 +856,7 @@ void CAChannelMonitor::activate()
std::cout << "CAChannelMonitor::activate " << channel->getChannelName() << endl;
}
dbdToPv = DbdToPv::create(channel,pvRequest,monitorIO);
+ dbdToPv->getChoices(channel);
pvStructure = dbdToPv->createPVStructure();
activeElement = MonitorElementPtr(new MonitorElement(pvStructure));
int32 queueSize = 2;
@@ -844,6 +870,15 @@ void CAChannelMonitor::activate()
ss >> size;
if (size > 1) queueSize = size;
}
+ pvString = pvOptions->getSubField("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->setChannelMonitor(shared_from_this());
@@ -916,7 +951,7 @@ Status CAChannelMonitor::start()
channel->attachContext();
int result = ca_create_subscription(dbdToPv->getRequestType(),
0,
- channel->getChannelID(), DBE_VALUE,
+ channel->getChannelID(), eventMask,
ca_subscription_handler, this,
&pevid);
if (result == ECA_NORMAL)
diff --git a/src/ca/caChannel.h b/src/ca/caChannel.h
index d5835cd..cfaf549 100644
--- a/src/ca/caChannel.h
+++ b/src/ca/caChannel.h
@@ -28,6 +28,15 @@ namespace epics {
namespace pvAccess {
namespace ca {
+class CAChannel;
+typedef std::tr1::shared_ptr CAChannelPtr;
+typedef std::tr1::weak_ptr CAChannelWPtr;
+class ChannelConnectThread;
+typedef std::tr1::shared_ptr ChannelConnectThreadPtr;
+
+class NotifyChannelRequester;
+typedef std::tr1::shared_ptr NotifyChannelRequesterPtr;
+
class NotifyMonitorRequester;
typedef std::tr1::shared_ptr NotifyMonitorRequesterPtr;
class MonitorEventThread;
@@ -88,9 +97,6 @@ public:
short priority,
ChannelRequester::shared_pointer const & channelRequester);
virtual ~CAChannel();
-
- void connected();
- void disconnected();
chid getChannelID();
virtual std::tr1::shared_ptr getProvider();
@@ -113,6 +119,8 @@ public:
void attachContext();
void disconnectChannel();
+ void connect(bool isConnected);
+ void notifyClient();
private:
virtual void destroy() {}
CAChannel(std::string const & channelName,
@@ -126,6 +134,9 @@ private:
ChannelRequester::weak_pointer channelRequester;
chid channelID;
bool channelCreated;
+ bool channelConnected;
+ ChannelConnectThreadPtr channelConnectThread;
+ NotifyChannelRequesterPtr notifyChannelRequester;
epics::pvData::Mutex requestsMutex;
std::queue getFieldQueue;
@@ -253,6 +264,7 @@ private:
bool isStarted;
MonitorEventThreadPtr monitorEventThread;
evid pevid;
+ unsigned long eventMask;
NotifyMonitorRequesterPtr notifyMonitorRequester;
DbdToPvPtr dbdToPv;
diff --git a/src/ca/caProvider.cpp b/src/ca/caProvider.cpp
index 5c5b22c..09bcb8b 100644
--- a/src/ca/caProvider.cpp
+++ b/src/ca/caProvider.cpp
@@ -11,6 +11,7 @@
#include
#include
+#include "channelConnectThread.h"
#include "monitorEventThread.h"
#include "getDoneThread.h"
#include "putDoneThread.h"
@@ -39,6 +40,7 @@ CAChannelProvider::CAChannelProvider()
CAChannelProvider::CAChannelProvider(const std::tr1::shared_ptr&)
: current_context(0),
+ channelConnectThread(ChannelConnectThread::get()),
monitorEventThread(MonitorEventThread::get()),
getDoneThread(GetDoneThread::get()),
putDoneThread(PutDoneThread::get())
@@ -75,9 +77,10 @@ CAChannelProvider::~CAChannelProvider()
channelQ.front()->disconnectChannel();
channelQ.pop();
}
- monitorEventThread->stop();
- getDoneThread->stop();
putDoneThread->stop();
+ getDoneThread->stop();
+ monitorEventThread->stop();
+ channelConnectThread->stop();
if(DEBUG_LEVEL>0) {
std::cout << "CAChannelProvider::~CAChannelProvider() calling ca_context_destroy\n";
}
@@ -174,10 +177,8 @@ 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::attachContext Foreign CA context in use");
- }
int result = ca_attach_context(current_context);
+ if(result==ECA_ISATTACHED) return;
if (result != ECA_NORMAL) {
std::string mess("CAChannelProvider::attachContext error calling ca_attach_context ");
mess += ca_message(result);
diff --git a/src/ca/caProviderPvt.h b/src/ca/caProviderPvt.h
index 0d48271..4a84c9d 100644
--- a/src/ca/caProviderPvt.h
+++ b/src/ca/caProviderPvt.h
@@ -24,6 +24,9 @@ namespace ca {
#define DEBUG_LEVEL 0
+class ChannelConnectThread;
+typedef std::tr1::shared_ptr ChannelConnectThreadPtr;
+
class MonitorEventThread;
typedef std::tr1::shared_ptr MonitorEventThreadPtr;
@@ -86,6 +89,7 @@ private:
ca_client_context* current_context;
epics::pvData::Mutex channelListMutex;
std::vector caChannelList;
+ ChannelConnectThreadPtr channelConnectThread;
MonitorEventThreadPtr monitorEventThread;
GetDoneThreadPtr getDoneThread;
PutDoneThreadPtr putDoneThread;
diff --git a/src/ca/channelConnectThread.cpp b/src/ca/channelConnectThread.cpp
new file mode 100644
index 0000000..c378019
--- /dev/null
+++ b/src/ca/channelConnectThread.cpp
@@ -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
+#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(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;
+ }
+ }
+}
+
+}}}
diff --git a/src/ca/channelConnectThread.h b/src/ca/channelConnectThread.h
new file mode 100644
index 0000000..7dcb568
--- /dev/null
+++ b/src/ca/channelConnectThread.h
@@ -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
+#include
+#include
+#include
+#include
+#include
+
+namespace epics {
+namespace pvAccess {
+namespace ca {
+
+class NotifyChannelRequester;
+typedef std::tr1::shared_ptr NotifyChannelRequesterPtr;
+typedef std::tr1::weak_ptr NotifyChannelRequesterWPtr;
+
+
+class ChannelConnectThread;
+typedef std::tr1::shared_ptr ChannelConnectThreadPtr;
+
+class CAChannel;
+typedef std::tr1::shared_ptr CAChannelPtr;
+typedef std::tr1::weak_ptr 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 thread;
+ epics::pvData::Mutex mutex;
+ epics::pvData::Event waitForCommand;
+ epics::pvData::Event waitForStop;
+ std::queue notifyChannelQueue;
+};
+
+
+}}}
+
+#endif /* ChannelConnectThread_H */
diff --git a/src/ca/dbdToPv.cpp b/src/ca/dbdToPv.cpp
index 09f30f7..254ba7f 100644
--- a/src/ca/dbdToPv.cpp
+++ b/src/ca/dbdToPv.cpp
@@ -10,6 +10,7 @@
#include
#include
+#include
#include
#include
#include
@@ -23,6 +24,7 @@
using namespace epics::pvData;
using std::string;
using std::ostringstream;
+using std::cout;
namespace epics {
namespace pvAccess {
@@ -30,23 +32,7 @@ namespace ca {
#define CA_PRIORITY 50
-static void enumChoicesHandler(struct event_handler_args args)
-{
- DbdToPv *dbdToPv = static_cast(args.usr);
- dbdToPv->getChoicesDone(args);
-}
-static void description_connection_handler(struct connection_handler_args args)
-{
- DbdToPv *dbdToPv = static_cast(ca_puser(args.chid));
- dbdToPv->descriptionConnected(args);
-}
-
-static void descriptionHandler(struct event_handler_args args)
-{
- DbdToPv *dbdToPv = static_cast(args.usr);
- dbdToPv->getDescriptionDone(args);
-}
DbdToPvPtr DbdToPv::create(
CAChannelPtr const & caChannel,
@@ -60,16 +46,20 @@ DbdToPvPtr DbdToPv::create(
DbdToPv::DbdToPv(IOType ioType)
: ioType(ioType),
- fieldRequested(false),
+ dbfIsUCHAR(false),
+ dbfIsUSHORT(false),
+ dbfIsULONG(false),
+ dbfIsINT64(false),
+ dbfIsUINT64(false),
+ valueRequested(false),
alarmRequested(false),
timeStampRequested(false),
displayRequested(false),
controlRequested(false),
valueAlarmRequested(false),
isArray(false),
+ charArrayIsString(false),
firstTime(true),
- choicesValid(false),
- waitForChoicesValid(false),
caValueType(-1),
caRequestType(-1),
maxElements(0)
@@ -95,15 +85,56 @@ static chtype getDbrType(const ScalarType scalarType)
{
case pvString : return DBR_STRING;
case pvByte : return DBR_CHAR;
+ case pvUByte : return DBR_CHAR;
case pvShort : return DBR_SHORT;
+ case pvUShort : return DBR_SHORT;
case pvInt : return DBR_LONG;
+ case pvUInt : return DBR_LONG;
case pvFloat : return DBR_FLOAT;
case pvDouble : return DBR_DOUBLE;
+ case pvLong : return DBR_DOUBLE;
+ case pvULong : return DBR_DOUBLE;
default: break;
}
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(
CAChannelPtr const & caChannel,
@@ -131,14 +162,14 @@ void DbdToPv::activate(
}
if(fieldPVStructure->getPVFields().size()==0)
{
- fieldRequested = true;
+ valueRequested = true;
alarmRequested = true;
timeStampRequested = true;
displayRequested = true;
controlRequested = true;
valueAlarmRequested = true;
} else {
- if(fieldPVStructure->getSubField("value")) fieldRequested = true;
+ if(fieldPVStructure->getSubField("value")) valueRequested = true;
if(fieldPVStructure->getSubField("alarm")) alarmRequested = true;
if(fieldPVStructure->getSubField("timeStamp")) timeStampRequested = true;
if(fieldPVStructure->getSubField("display")) displayRequested = true;
@@ -173,39 +204,75 @@ void DbdToPv::activate(
}
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;
}
+ ScalarType st = dbr2ST[channelType];
+ PVStringPtr pvValue = fieldPVStructure->getSubField("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);
if(maxElements!=1) isArray = true;
if(isArray)
{
controlRequested = false;
valueAlarmRequested = false;
+ if(channelType==DBR_CHAR && fieldPVStructure)
+ {
+ PVStringPtr pvValue = fieldPVStructure->getSubField("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;
FieldCreatePtr fieldCreate(FieldCreate::getFieldCreate());
PVDataCreatePtr pvDataCreate(PVDataCreate::getPVDataCreate());
FieldBuilderPtr fieldBuilder(fieldCreate->createFieldBuilder());
- if(fieldRequested) {
- if(isArray) {
+ if(valueRequested) {
+ if(isArray && !charArrayIsString) {
fieldBuilder->addArray("value",st);
} else {
fieldBuilder->add("value",st);
@@ -219,14 +286,19 @@ void DbdToPv::activate(
switch(st)
{
case pvByte:
+ case pvUByte:
fieldBuilder->add("valueAlarm",standardField->byteAlarm()); break;
case pvShort:
+ case pvUShort:
fieldBuilder->add("valueAlarm",standardField->shortAlarm()); break;
case pvInt:
+ case pvUInt:
fieldBuilder->add("valueAlarm",standardField->intAlarm()); break;
case pvFloat:
fieldBuilder->add("valueAlarm",standardField->floatAlarm()); break;
case pvDouble:
+ case pvLong:
+ case pvULong:
fieldBuilder->add("valueAlarm",standardField->doubleAlarm()); break;
default:
throw std::runtime_error("DbDToPv::activate: bad type");
@@ -242,38 +314,27 @@ void DbdToPv::activate(
} 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);
- }
+
+}
+
+chtype DbdToPv::getRequestType()
+{
+ if(caRequestType<0) {
+ throw std::runtime_error("DbDToPv::getRequestType: bad type");
}
+ return caRequestType;
}
-void DbdToPv::descriptionConnected(struct connection_handler_args args)
+Structure::const_shared_pointer DbdToPv::getStructure()
{
- if (args.op != CA_OP_CONN_UP) return;
- ca_array_get_callback(DBR_STRING,
- 0,
- args.chid, descriptionHandler, this);
+ return structure;
}
-void DbdToPv::getDescriptionDone(struct event_handler_args &args)
+
+static void enumChoicesHandler(struct event_handler_args args)
{
- if(args.status!=ECA_NORMAL) return;
- const dbr_string_t *value = static_cast(dbr_value_ptr(args.dbr,DBR_STRING));
- description = string(*value);
- ca_clear_channel(args.chid);
+ DbdToPv *dbdToPv = static_cast(args.usr);
+ dbdToPv->getChoicesDone(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;
choices.reserve(num);
for(size_t i=0; istrs[i][0]));
- bool signal = false;
- {
- Lock lock(choicesMutex);
- choicesValid = true;
- if(waitForChoicesValid) signal = true;
- }
- if(signal) choicesEvent.signal();
+ choicesEvent.signal();
}
-chtype DbdToPv::getRequestType()
+
+void DbdToPv::getChoices(CAChannelPtr const & caChannel)
{
- if(caRequestType<0) {
- throw std::runtime_error("DbDToPv::getRequestType: bad type");
+ if(caRequestType==DBR_ENUM||caRequestType==DBR_TIME_ENUM)
+ {
+ 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()
@@ -368,7 +437,7 @@ Status DbdToPv::getFromDBD(
Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(args.status)));
return errorStatus;
}
- if(fieldRequested)
+ if(valueRequested)
{
void * value = dbr_value_ptr(args.dbr,caRequestType);
if(isArray) {
@@ -386,18 +455,51 @@ Status DbdToPv::getFromDBD(
break;
}
case DBR_CHAR:
+ if(charArrayIsString)
+ {
+ const char * pchar = static_cast(value);
+ std::string str(pchar);
+ PVStringPtr pvValue(pvStructure->getSubField("value"));
+ pvValue->put(str);
+ break;
+ }
+ if(dbfIsUCHAR)
+ {
+ copy_DBRScalarArray(value,count,pvValue);
+ break;
+ }
copy_DBRScalarArray(value,count,pvValue);
break;
case DBR_SHORT:
+ if(dbfIsUSHORT)
+ {
+ copy_DBRScalarArray(value,count,pvValue);
+ break;
+ }
copy_DBRScalarArray(value,count,pvValue);
break;
case DBR_LONG:
+ if(dbfIsULONG)
+ {
+ copy_DBRScalarArray(value,count,pvValue);
+ break;
+ }
copy_DBRScalarArray(value,count,pvValue);
break;
case DBR_FLOAT:
copy_DBRScalarArray(value,count,pvValue);
break;
case DBR_DOUBLE:
+ if(dbfIsINT64)
+ {
+ copy_DBRScalarArray(value,count,pvValue);
+ break;
+ }
+ if(dbfIsUINT64)
+ {
+ copy_DBRScalarArray(value,count,pvValue);
+ break;
+ }
copy_DBRScalarArray(value,count,pvValue);
break;
default:
@@ -428,11 +530,40 @@ Status DbdToPv::getFromDBD(
break;
}
case DBR_STRING: copy_DBRScalar(value,pvValue); break;
- case DBR_CHAR: copy_DBRScalar(value,pvValue); break;
- case DBR_SHORT: copy_DBRScalar(value,pvValue); break;
- case DBR_LONG: copy_DBRScalar(value,pvValue); break;
+ case DBR_CHAR:
+ if(dbfIsUCHAR)
+ {
+ copy_DBRScalar(value,pvValue);
+ break;
+ }
+ copy_DBRScalar(value,pvValue); break;
+ case DBR_SHORT:
+ if(dbfIsUSHORT)
+ {
+ copy_DBRScalar(value,pvValue);
+ break;
+ }
+ copy_DBRScalar(value,pvValue); break;
+ case DBR_LONG:
+ if(dbfIsULONG)
+ {
+ copy_DBRScalar(value,pvValue);
+ break;
+ }
+ copy_DBRScalar(value,pvValue); break;
case DBR_FLOAT: copy_DBRScalar(value,pvValue); break;
- case DBR_DOUBLE: copy_DBRScalar(value,pvValue); break;
+ case DBR_DOUBLE:
+ if(dbfIsINT64)
+ {
+ copy_DBRScalar(value,pvValue);
+ break;
+ }
+ if(dbfIsUINT64)
+ {
+ copy_DBRScalar(value,pvValue);
+ break;
+ }
+ copy_DBRScalar(value,pvValue); break;
default:
Status errorStatus(
Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error"));
@@ -461,7 +592,7 @@ Status DbdToPv::getFromDBD(
PVIntPtr pvStatus(pvAlarm->getSubField("status"));
if(caAlarm.status!=status) {
caAlarm.status = status;
- pvStatus->put(status);
+ pvStatus->put(convertDBstatus(status));
string message("UNKNOWN STATUS");
if(status<=ALARM_NSTATUS) message = string(epicsAlarmConditionStrings[status]);
pvMessage->put(message);
@@ -488,7 +619,7 @@ Status DbdToPv::getFromDBD(
bitSet->set(pvSeconds->getFieldOffset());
}
if(caTimeStamp.nsec!=stamp.nsec) {
- caTimeStamp.secPastEpoch = stamp.secPastEpoch;
+ caTimeStamp.nsec = stamp.nsec;
PVIntPtr pvNano(pvTimeStamp->getSubField("nanoseconds"));
pvNano->put(stamp.nsec);
bitSet->set(pvNano->getFieldOffset());
@@ -590,14 +721,6 @@ Status DbdToPv::getFromDBD(
pvString->put(format);
bitSet->set(pvString->getFieldOffset());
}
- if(!description.empty())
- {
- PVStringPtr pvString = pvDisplay->getSubField("description");
- if(description.compare(pvString->get()) !=0) {
- pvString->put(description);
- bitSet->set(pvString->getFieldOffset());
- }
- }
}
if(valueAlarmRequested) {
double upper_alarm_limit = 0.0;
@@ -668,6 +791,8 @@ Status DbdToPv::getFromDBD(
return Status::Ok;
}
+
+
template
const void * put_DBRScalar(dbrT *val,PVScalar::shared_pointer const & pvScalar)
{
@@ -726,18 +851,63 @@ Status DbdToPv::putToDBD(
break;
}
case DBR_CHAR:
+ if(charArrayIsString)
+ {
+ PVStringPtr pvValue(pvStructure->getSubField("value"));
+ const char * pchar = pvValue->get().c_str();
+ pValue = pchar;
+ count = pvValue->get().length();
+ break;
+ }
+ if(dbfIsUCHAR)
+ {
+ pValue = put_DBRScalarArray(&count,pvValue);
+ break;
+ }
pValue = put_DBRScalarArray(&count,pvValue);
break;
case DBR_SHORT:
+ if(dbfIsUSHORT)
+ {
+ pValue = put_DBRScalarArray(&count,pvValue);
+ break;
+ }
pValue = put_DBRScalarArray(&count,pvValue);
break;
case DBR_LONG:
+ if(dbfIsULONG)
+ {
+ pValue = put_DBRScalarArray(&count,pvValue);
+ break;
+ }
pValue = put_DBRScalarArray(&count,pvValue);
break;
case DBR_FLOAT:
pValue = put_DBRScalarArray(&count,pvValue);
break;
case DBR_DOUBLE:
+ if(dbfIsINT64)
+ {
+ PVLongArrayPtr pvValue(pvStructure->getSubField("value"));
+ PVLongArray::const_svector sv(pvValue->view());
+ pvDoubleArray = PVDoubleArrayPtr(getPVDataCreate()->createPVScalarArray());
+ pvDoubleArray->putFrom(sv);
+ const double * pdouble = pvDoubleArray->view().data();
+ count = pvValue->getLength();
+ pValue = pdouble;
+ break;
+ }
+ if(dbfIsUINT64)
+ {
+ PVULongArrayPtr pvValue(pvStructure->getSubField("value"));
+ PVULongArray::const_svector sv(pvValue->view());
+ pvDoubleArray = PVDoubleArrayPtr(getPVDataCreate()->createPVScalarArray());
+ pvDoubleArray->putFrom(sv);
+ const double * pdouble = pvDoubleArray->view().data();
+ count = pvValue->getLength();
+ pValue = pdouble;
+ break;
+ }
pValue = put_DBRScalarArray(&count,pvValue);
break;
default:
@@ -750,36 +920,48 @@ Status DbdToPv::putToDBD(
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("value.index")->get();
pValue = &indexvalue;
break;
}
case DBR_STRING: pValue = pvStructure->getSubField("value")->get().c_str(); break;
- case DBR_CHAR: pValue = put_DBRScalar(&bvalue,pvValue); break;
- case DBR_SHORT: pValue = put_DBRScalar(&svalue,pvValue); break;
- case DBR_LONG: pValue = put_DBRScalar(&lvalue,pvValue); break;
+ case DBR_CHAR:
+ if(dbfIsUCHAR)
+ {
+ pValue = put_DBRScalar(&bvalue,pvValue);
+ break;
+ }
+ pValue = put_DBRScalar(&bvalue,pvValue); break;
+ case DBR_SHORT:
+ if(dbfIsUSHORT)
+ {
+ pValue = put_DBRScalar(&svalue,pvValue);
+ break;
+ }
+ pValue = put_DBRScalar(&svalue,pvValue); break;
+ case DBR_LONG:
+ if(dbfIsULONG)
+ {
+ pValue = put_DBRScalar(&lvalue,pvValue);
+ break;
+ }
+ pValue = put_DBRScalar(&lvalue,pvValue); break;
case DBR_FLOAT: pValue = put_DBRScalar(&fvalue,pvValue); break;
- case DBR_DOUBLE: pValue = put_DBRScalar(&dvalue,pvValue); break;
+ case DBR_DOUBLE:
+ if(dbfIsINT64)
+ {
+ pValue = put_DBRScalar(&dvalue,pvValue);
+ break;
+ }
+ if(dbfIsUINT64)
+ {
+ pValue = put_DBRScalar(&dvalue,pvValue);
+ break;
+ }
+ pValue = put_DBRScalar(&dvalue,pvValue); break;
default:
Status errorStatus(
- Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error"));
+ Status::STATUSTYPE_ERROR, string("DbdToPv::putToDBD logic error"));
return errorStatus;
}
}
diff --git a/src/ca/dbdToPv.h b/src/ca/dbdToPv.h
index 5ee77b5..1c9a0fa 100644
--- a/src/ca/dbdToPv.h
+++ b/src/ca/dbdToPv.h
@@ -89,6 +89,8 @@ public:
epics::pvData::PVStructurePtr const & pvRequest,
IOType ioType
);
+ epics::pvData::Structure::const_shared_pointer getStructure();
+ void getChoices(CAChannelPtr const & caChannel);
epics::pvData::PVStructurePtr createPVStructure();
chtype getRequestType();
epics::pvData::Status getFromDBD(
@@ -104,8 +106,6 @@ public:
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(
@@ -113,29 +113,32 @@ private:
epics::pvData::PVStructurePtr const & pvRequest
);
IOType ioType;
- bool fieldRequested;
+ bool dbfIsUCHAR;
+ bool dbfIsUSHORT;
+ bool dbfIsULONG;
+ bool dbfIsINT64;
+ bool dbfIsUINT64;
+ bool valueRequested;
bool alarmRequested;
bool timeStampRequested;
bool displayRequested;
bool controlRequested;
bool valueAlarmRequested;
bool isArray;
+ bool charArrayIsString;
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 choices;
+ epics::pvData::PVDoubleArrayPtr pvDoubleArray; //for dbfIsINT64 and dbfIsUINT64
};
}